Commit 3ac1c4e3 authored by nick@chromium.org's avatar nick@chromium.org

Depot tools: use the clang-format binaries that are now included

as part of the Chromium checkout.

This follows the approach used by gn.

Changes include:
 - in-the-PATH clang-format trampoline scripts
 - clang_format.py, which finds clang-format binaries inside of Chrome
 - Hook 'git cl format' to the new binaries and scripts
 - Rearrange some code, for reuse between clang_format.py and gn.py

BUG=240309
TEST=presubmits (one failure on mac, but it fails on a clean checkout too)

Review URL: https://codereview.chromium.org/134313007

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@245074 0039d316-1c4b-4281-b951-d872f2087c98
parent 9eabb22c
#!/usr/bin/env bash
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
base_dir=$(dirname "$0")
PYTHONDONTWRITEBYTECODE=1 exec python "$base_dir/clang_format.py" "$@"
@echo off
:: Copyright 2014 The Chromium Authors. All rights reserved.
:: Use of this source code is governed by a BSD-style license that can be
:: found in the LICENSE file.
:: This is required with cygwin only.
PATH=%~dp0;%PATH%
:: Defer control.
%~dp0python "%~dp0\clang_format.py" %*
#!/usr/bin/env python
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Redirects to the version of clang-format checked into the Chrome tree.
clang-format binaries are pulled down from Google Cloud Storage whenever you
sync Chrome, to platform-specific locations. This script knows how to locate
those tools, assuming the script is invoked from inside a Chromium checkout."""
import gclient_utils
import os
import subprocess
import sys
class NotFoundError(Exception):
"""A file could not be found."""
def __init__(self, e):
Exception.__init__(self,
'Problem while looking for clang-format in Chromium source tree:\n'
' %s' % e)
def _FindChromiumTree():
"""Return the root of the current chromium checkout, or die trying."""
source_root = gclient_utils.FindFileUpwards('.gclient')
if not source_root:
raise NotFoundError(
'.gclient file not found in any parent of the current path.')
return source_root
def FindClangFormatToolInChromiumTree():
"""Return a path to the clang-format executable, or die trying."""
# The binaries in platform-specific subdirectories in src/tools/gn/bin.
tool_path = os.path.join(_FindChromiumTree(), 'src', 'third_party',
'clang_format', 'bin',
gclient_utils.GetMacWinOrLinux(),
'clang-format' + gclient_utils.GetExeSuffix())
if not os.path.exists(tool_path):
# TODO(nick): After March 2014, eliminate the following advisory.
error_text = '''\n GIT CL FORMAT - WINTER WEATHER ADVISORY
clang-format binaries now come with every Chrome checkout!
Unfortunately, your depot_tools scripts tried to find clang-format binaries
in your Chrome checkout, but failed. This is expected if you haven't synced
since the binaries were added.
'git cl format' will probably not work until you sync your Chrome tree.
Sorry about that.
Contact nick@chromium.org if you have any additional questions.\n\n'''
error_text += 'File does not exist: %s' % tool_path
raise NotFoundError(error_text)
return tool_path
def FindClangFormatScriptInChromiumTree(script_name):
"""Return a path to a clang-format helper script, or die trying."""
# The binaries in platform-specific subdirectories in src/tools/gn/bin.
script_path = os.path.join(_FindChromiumTree(), 'src', 'third_party',
'clang_format', 'scripts', script_name)
if not os.path.exists(script_path):
raise NotFoundError('File does not exist: %s' % script_path)
return script_path
def main(args):
try:
tool = FindClangFormatToolInChromiumTree()
except NotFoundError, e:
print >> sys.stderr, e
sys.exit(1)
# Add some visibility to --help showing where the tool lives, since this
# redirection can be a little opaque.
help_syntax = ('-h', '--help', '-help', '-help-list', '--help-list')
if any(match in args for match in help_syntax):
print '\nDepot tools redirects you to the clang-format at:\n %s\n' % tool
return subprocess.call([tool] + sys.argv[1:])
if __name__ == '__main__':
sys.exit(main(sys.argv))
...@@ -33,6 +33,7 @@ class Error(Exception): ...@@ -33,6 +33,7 @@ class Error(Exception):
msg = '\n'.join('%d> %s' % (index, l) for l in msg.splitlines()) msg = '\n'.join('%d> %s' % (index, l) for l in msg.splitlines())
super(Error, self).__init__(msg, *args, **kwargs) super(Error, self).__init__(msg, *args, **kwargs)
def SplitUrlRevision(url): def SplitUrlRevision(url):
"""Splits url and returns a two-tuple: url, rev""" """Splits url and returns a two-tuple: url, rev"""
if url.startswith('ssh:'): if url.startswith('ssh:'):
...@@ -103,10 +104,10 @@ def FileWrite(filename, content, mode='w'): ...@@ -103,10 +104,10 @@ def FileWrite(filename, content, mode='w'):
def safe_rename(old, new): def safe_rename(old, new):
"""Renames a file reliably. """Renames a file reliably.
Sometimes os.rename does not work because a dying git process keeps a handle Sometimes os.rename does not work because a dying git process keeps a handle
on it for a few seconds. An exception is then thrown, which make the program on it for a few seconds. An exception is then thrown, which make the program
give up what it was doing and remove what was deleted. give up what it was doing and remove what was deleted.
The only solution is to catch the exception and try again until it works. The only solution is to catch the exception and try again until it works.
""" """
# roughly 10s # roughly 10s
retries = 100 retries = 100
...@@ -560,6 +561,24 @@ def FindFileUpwards(filename, path=None): ...@@ -560,6 +561,24 @@ def FindFileUpwards(filename, path=None):
path = new_path path = new_path
def GetMacWinOrLinux():
"""Returns 'mac', 'win', or 'linux', matching the current platform."""
if sys.platform.startswith(('cygwin', 'win')):
return 'win'
elif sys.platform.startswith('linux'):
return 'linux'
elif sys.platform == 'darwin':
return 'mac'
raise Error('Unknown platform: ' + sys.platform)
def GetExeSuffix():
"""Returns '' or '.exe' depending on how executables work on this platform."""
if sys.platform.startswith(('cygwin', 'win')):
return '.exe'
return ''
def GetGClientRootAndEntries(path=None): def GetGClientRootAndEntries(path=None):
"""Returns the gclient root and the dict of entries.""" """Returns the gclient root and the dict of entries."""
config_file = '.gclient_entries' config_file = '.gclient_entries'
......
...@@ -32,6 +32,7 @@ except ImportError: ...@@ -32,6 +32,7 @@ except ImportError:
from third_party import colorama from third_party import colorama
from third_party import upload from third_party import upload
import breakpad # pylint: disable=W0611 import breakpad # pylint: disable=W0611
import clang_format
import fix_encoding import fix_encoding
import gclient_utils import gclient_utils
import presubmit_support import presubmit_support
...@@ -269,7 +270,7 @@ class Settings(object): ...@@ -269,7 +270,7 @@ class Settings(object):
if not self.updated: if not self.updated:
# The only value that actually changes the behavior is # The only value that actually changes the behavior is
# autoupdate = "false". Everything else means "true". # autoupdate = "false". Everything else means "true".
autoupdate = RunGit(['config', 'rietveld.autoupdate'], autoupdate = RunGit(['config', 'rietveld.autoupdate'],
error_ok=True error_ok=True
).strip().lower() ).strip().lower()
...@@ -2342,7 +2343,14 @@ def CMDformat(parser, args): ...@@ -2342,7 +2343,14 @@ def CMDformat(parser, args):
diff_cmd += ['*' + ext for ext in CLANG_EXTS] diff_cmd += ['*' + ext for ext in CLANG_EXTS]
diff_output = RunGit(diff_cmd) diff_output = RunGit(diff_cmd)
top_dir = RunGit(["rev-parse", "--show-toplevel"]).rstrip('\n') top_dir = os.path.normpath(
RunGit(["rev-parse", "--show-toplevel"]).rstrip('\n'))
# Locate the clang-format binary in the checkout
try:
clang_format_tool = clang_format.FindClangFormatToolInChromiumTree()
except clang_format.NotFoundError, e:
DieWithError(e)
if opts.full: if opts.full:
# diff_output is a list of files to send to clang-format. # diff_output is a list of files to send to clang-format.
...@@ -2350,24 +2358,21 @@ def CMDformat(parser, args): ...@@ -2350,24 +2358,21 @@ def CMDformat(parser, args):
if not files: if not files:
print "Nothing to format." print "Nothing to format."
return 0 return 0
RunCommand(['clang-format', '-i', '-style', 'Chromium'] + files, RunCommand([clang_format_tool, '-i', '-style', 'Chromium'] + files,
cwd=top_dir) cwd=top_dir)
else: else:
env = os.environ.copy()
env['PATH'] = os.path.dirname(clang_format_tool)
# diff_output is a patch to send to clang-format-diff.py # diff_output is a patch to send to clang-format-diff.py
cfd_path = os.path.join('/usr', 'lib', 'clang-format', try:
'clang-format-diff.py') script = clang_format.FindClangFormatScriptInChromiumTree(
if not os.path.exists(cfd_path): 'clang-format-diff.py')
DieWithError('Could not find clang-format-diff at %s.' % cfd_path) except clang_format.NotFoundError, e:
cmd = [sys.executable, cfd_path, '-p0', '-style', 'Chromium'] DieWithError(e)
# Newer versions of clang-format-diff.py require an explicit -i flag cmd = [sys.executable, script, '-p0', '-style', 'Chromium', '-i']
# to apply the edits to files, otherwise it just displays a diff.
# Probe the usage string to verify if this is needed. RunCommand(cmd, stdin=diff_output, cwd=top_dir, env=env)
help_text = RunCommand([sys.executable, cfd_path, '-h'])
if '[-i]' in help_text:
cmd.append('-i')
RunCommand(cmd, stdin=diff_output, cwd=top_dir)
return 0 return 0
......
...@@ -12,55 +12,27 @@ binary. It will also automatically try to find the gn binary when run inside ...@@ -12,55 +12,27 @@ binary. It will also automatically try to find the gn binary when run inside
the chrome source tree, so users can just type "gn" on the command line the chrome source tree, so users can just type "gn" on the command line
(normally depot_tools is on the path).""" (normally depot_tools is on the path)."""
import gclient_utils
import os import os
import subprocess import subprocess
import sys import sys
class PlatformUnknownError(IOError):
pass
def HasDotfile(path):
"""Returns True if the given path has a .gn file in it."""
return os.path.exists(path + '/.gn')
def FindSourceRootOnPath():
"""Searches upward from the current directory for the root of the source
tree and returns the found path. Returns None if no source root could
be found."""
cur = os.getcwd()
while True:
if HasDotfile(cur):
return cur
up_one = os.path.dirname(cur)
if up_one == cur:
return None # Reached the top of the directory tree
cur = up_one
def RunGN(sourceroot): def RunGN(sourceroot):
# The binaries in platform-specific subdirectories in src/tools/gn/bin. # The binaries in platform-specific subdirectories in src/tools/gn/bin.
gnpath = sourceroot + '/tools/gn/bin/' gnpath = os.path.join(sourceroot,
if sys.platform in ('cygwin', 'win32'): 'tools', 'gn', 'bin', gclient_utils.GetMacWinOrLinux(),
gnpath += 'win/gn.exe' 'gn' + gclient_utils.GetExeSuffix())
elif sys.platform.startswith('linux'):
gnpath += 'linux/gn'
elif sys.platform == 'darwin':
gnpath += 'mac/gn'
else:
raise PlatformUnknownError('Unknown platform for GN: ' + sys.platform)
return subprocess.call([gnpath] + sys.argv[1:]) return subprocess.call([gnpath] + sys.argv[1:])
def main(args): def main(args):
sourceroot = FindSourceRootOnPath() sourceroot = gclient_utils.FindFileUpwards('.gn')
if not sourceroot: if not sourceroot:
print >> sys.stderr, '.gn file not found in any parent of the current path.' print >> sys.stderr, '.gn file not found in any parent of the current path.'
sys.exit(1) sys.exit(1)
return RunGN(sourceroot) return RunGN(sourceroot)
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main(sys.argv)) sys.exit(main(sys.argv))
...@@ -31,15 +31,17 @@ class GclientUtilsUnittest(GclientUtilBase): ...@@ -31,15 +31,17 @@ class GclientUtilsUnittest(GclientUtilBase):
'Annotated', 'AutoFlush', 'CheckCallAndFilter', 'CommandToStr', 'Annotated', 'AutoFlush', 'CheckCallAndFilter', 'CommandToStr',
'CheckCallAndFilterAndHeader', 'Error', 'ExecutionQueue', 'FileRead', 'CheckCallAndFilterAndHeader', 'Error', 'ExecutionQueue', 'FileRead',
'FileWrite', 'FindFileUpwards', 'FindGclientRoot', 'FileWrite', 'FindFileUpwards', 'FindGclientRoot',
'GetGClientRootAndEntries', 'GetEditor', 'IsDateRevision', 'GetGClientRootAndEntries', 'GetEditor', 'GetExeSuffix',
'MakeDateRevision', 'MakeFileAutoFlush', 'MakeFileAnnotated', 'GetMacWinOrLinux', 'IsDateRevision', 'MakeDateRevision',
'PathDifference', 'ParseCodereviewSettingsContent', 'NumLocalCpus', 'MakeFileAutoFlush', 'MakeFileAnnotated', 'PathDifference',
'PrintableObject', 'RETRY_INITIAL_SLEEP', 'RETRY_MAX', 'RunEditor', 'ParseCodereviewSettingsContent', 'NumLocalCpus', 'PrintableObject',
'GCLIENT_CHILDREN', 'GCLIENT_CHILDREN_LOCK', 'GClientChildren', 'RETRY_INITIAL_SLEEP', 'RETRY_MAX', 'RunEditor', 'GCLIENT_CHILDREN',
'SplitUrlRevision', 'SyntaxErrorToError', 'UpgradeToHttps', 'Wrapper', 'GCLIENT_CHILDREN_LOCK', 'GClientChildren', 'SplitUrlRevision',
'WorkItem', 'codecs', 'lockedmethod', 'logging', 'os', 'pipes', 'Queue', 'SyntaxErrorToError', 'UpgradeToHttps', 'Wrapper', 'WorkItem',
're', 'rmtree', 'safe_makedirs', 'safe_rename', 'stat', 'subprocess', 'codecs', 'lockedmethod', 'logging', 'os', 'pipes', 'Queue', 're',
'rmtree', 'safe_makedirs', 'safe_rename', 'stat', 'subprocess',
'subprocess2', 'sys', 'tempfile', 'threading', 'time', 'urlparse', 'subprocess2', 'sys', 'tempfile', 'threading', 'time', 'urlparse',
] ]
# If this test fails, you should add the relevant test. # If this test fails, you should add the relevant test.
self.compareMembers(gclient_utils, members) self.compareMembers(gclient_utils, members)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment