blob: 39b2af28cfe5f08e7f6b3ee1872bc6b9ffeda77c [file] [log] [blame]
#!/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")
DART_TOOL = os.path.join(paths.DART_ROOT, "bin", "dartfmt")
GN_TOOL = os.path.join(paths.BUILDTOOLS_ROOT, "gn")
C_CMD = [CLANG_TOOL, "-style=file", "-fallback-style=Chromium", "-sort-includes", "-i"]
DART_CMD = [DART_TOOL, "-w"]
GN_CMD = [GN_TOOL, "format"]
EXT_TO_COMMAND = {
".cc": C_CMD,
".cpp": C_CMD,
".dart": DART_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())