| #!/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()) |