# Copyright (c) 2017 The Chromium Embedded Framework Authors. # Portions copyright (c) 2011 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. from __future__ import absolute_import from __future__ import print_function import os, re, sys from clang_util import clang_format from file_util import eval_file, get_files, read_file, write_file from git_util import get_changed_files from yapf_util import yapf_format # File extensions that can be formatted. DEFAULT_LINT_WHITELIST_REGEX = r"(.*\.cpp|.*\.cc|.*\.h|.*\.java|.*\.mm|.*\.py)$" DEFAULT_LINT_BLACKLIST_REGEX = r"$^" # Directories containing these path components will be ignored. IGNORE_DIRECTORIES = [] # Script directory. script_dir = os.path.dirname(__file__) root_dir = os.path.join(script_dir, os.pardir) def msg(filename, status): if sys.platform == 'win32': # Use Unix path separator. filename = filename.replace("\\", "/") if len(filename) > 60: # Truncate the file path in a nice way. filename = filename[-57:] pos = filename.find("/") if pos > 0: filename = filename[pos:] filename = "..." + filename print("%-60s %s" % (filename, status)) updatect = 0 def read_config(): style_cfg = os.path.join(root_dir, ".style.cfg") if os.path.exists(style_cfg): config = eval_file(style_cfg) if 'ignore_directories' in config: global IGNORE_DIRECTORIES IGNORE_DIRECTORIES = config['ignore_directories'] def update_file(filename): oldcontents = read_file(filename) if len(oldcontents) == 0: msg(filename, "empty") return if os.path.splitext(filename)[1] == ".py": # Format Python files using YAPF. newcontents = yapf_format(filename, oldcontents) else: # Format C/C++/ObjC/Java files using clang-format. newcontents = clang_format(filename, oldcontents) if newcontents is None: raise Exception("Failed to process %s" % filename) if newcontents != oldcontents: msg(filename, "fixed") global updatect updatect += 1 write_file(filename, newcontents) else: msg(filename, "ok") return def fix_style(filenames, white_list=None, black_list=None): """ Execute clang-format with the specified arguments. """ if not white_list: white_list = DEFAULT_LINT_WHITELIST_REGEX white_regex = re.compile(white_list) if not black_list: black_list = DEFAULT_LINT_BLACKLIST_REGEX black_regex = re.compile(black_list) for filename in filenames: # Ignore files from specific directories. ignore = False for dir_part in filename.split(os.sep): if dir_part in IGNORE_DIRECTORIES: msg(filename, "ignored") ignore = True break if ignore: continue if filename.find('*') > 0: # Expand wildcards. filenames.extend(get_files(filename)) continue if os.path.isdir(filename): # Add directory contents. filenames.extend(get_files(os.path.join(filename, "*"))) continue if not os.path.exists(filename): files = get_changed_files(".", filename) if len(files) > 0: filenames.extend(files) else: msg(filename, "missing") continue if white_regex.match(filename): if black_regex.match(filename): msg(filename, "ignored") else: update_file(filename) else: msg(filename, "skipped") if __name__ == "__main__": if len(sys.argv) == 1: print("Usage: %s [file-path|git-hash|unstaged|staged] ...\n" % sys.argv[0]) print(" Format C, C++ and ObjC files using Chromium's clang-format style.") print("\nOptions:") print(" file-path\tProcess the specified file or directory.") print(" \t\tDirectories will be processed recursively.") print(" \t\tThe \"*\" wildcard character is supported.") print(" git-hash\tProcess all files changed in the specified Git commit.") print(" unstaged\tProcess all unstaged files in the Git repo.") print(" staged\t\tProcess all staged files in the Git repo.") sys.exit(1) # Read the configuration file. read_config() # Process anything passed on the command-line. fix_style(sys.argv[1:]) print('Done - Wrote %d files.' % updatect)