|
#!/usr/bin/env python |
|
# Copyright 2016 The Fuchsia Authors. All rights reserved. |
|
# Use of this source code is governed by a BSD-style license that can be |
|
# found in the LICENSE file. |
|
"""Runs source formatters on modified files. |
|
|
|
In order to find the files to be formatted, this uses `git diff-index` against |
|
the newest parent commit in the upstream branch (or against HEAD if there is no |
|
upstream branch). In result, files that are locally modified, staged or touched |
|
by any commits introduced on the local branch are formatted. |
|
""" |
|
|
|
import argparse |
|
import os.path |
|
import paths |
|
import platform |
|
import subprocess |
|
import sys |
|
|
|
ossuffix = "linux" |
|
if platform.platform().startswith("Darwin"): |
|
ossuffix = "darwin" |
|
CLANG_TOOL = os.path.join(paths.BUILDTOOLS_ROOT, "toolchain", |
|
"clang+llvm-x86_64-%s" % ossuffix, "bin", "clang-format") |
|
GN_TOOL = os.path.join(paths.BUILDTOOLS_ROOT, "gn") |
|
|
|
C_CMD = [CLANG_TOOL, "-style=file", "-fallback-style=Chromium", "-i"] |
|
GN_CMD = [GN_TOOL, "format"] |
|
|
|
EXT_TO_COMMAND = { |
|
".cc": C_CMD, |
|
".cpp": C_CMD, |
|
".gn": GN_CMD, |
|
".gni": GN_CMD, |
|
".h": C_CMD, |
|
".hh": C_CMD, |
|
".hpp": C_CMD, |
|
} |
|
|
|
|
|
def _get_diff_base(): |
|
"""Returns the newest local commit that is also in the upstream branch, or |
|
"HEAD" if there is no upstream branch. |
|
""" |
|
try: |
|
upstream = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", |
|
"--symbolic-full-name", "@{u}"]).strip() |
|
# Get local commits not in upstream. |
|
local_commits = filter(len, subprocess.check_output([ |
|
"git", "rev-list", "HEAD", "^" + upstream, "--"]).split("\n")) |
|
if not local_commits: |
|
return "HEAD" |
|
|
|
# Return parent of the oldest commit. |
|
return subprocess.check_output(["git", "rev-parse", |
|
local_commits[-1] + "^"]).strip() |
|
|
|
except subprocess.CalledProcessError: |
|
return "HEAD" |
|
|
|
|
|
def main(): |
|
parser = argparse.ArgumentParser(description="Format modified files.") |
|
parser.add_argument( |
|
"--dry-run", |
|
dest="dry_run", |
|
action="store_true", |
|
default=False, |
|
help="just pretend to run stuff") |
|
parser.add_argument( |
|
"--verbose", |
|
dest="verbose", |
|
action="store_true", |
|
default=False, |
|
help="tell me what you're doing") |
|
args = parser.parse_args() |
|
|
|
# Find the files to be formatted. |
|
diff_base = _get_diff_base() |
|
if args.verbose: |
|
print "Diff base: " + diff_base |
|
files = filter(len, subprocess.check_output( |
|
["git", "diff-index", "--name-only", diff_base]).split("\n")) |
|
|
|
if args.verbose: |
|
print |
|
print "Files to be formatted:" |
|
if not files: |
|
print " (no files)" |
|
return |
|
|
|
for file in files: |
|
print " - " + file |
|
|
|
# Run the formatters. |
|
if args.dry_run: |
|
print |
|
print "Would run the following formatters (dry run):" |
|
elif args.verbose: |
|
print "Running the following formatters:" |
|
|
|
count = 0 |
|
|
|
for file in files: |
|
# Skip deleted files. |
|
if not os.path.isfile(file): |
|
continue |
|
|
|
_, extension = os.path.splitext(file) |
|
if extension not in EXT_TO_COMMAND: |
|
continue |
|
|
|
cmd = EXT_TO_COMMAND[extension] + [file] |
|
count += 1 |
|
if args.dry_run or args.verbose: |
|
print cmd |
|
|
|
if args.dry_run: |
|
continue |
|
|
|
subprocess.check_call(cmd) |
|
|
|
if (args.dry_run or args.verbose) and not count: |
|
print " (none)" |
|
return 0 |
|
|
|
|
|
if __name__ == "__main__": |
|
sys.exit(main()) |