Commit a566549e authored by Marshall Greenblatt's avatar Marshall Greenblatt

Update tooling to use clang-format (issue #2171)

parent 816f700d
......@@ -27,73 +27,6 @@ def wrap_text(text, indent = '', maxchars = 80):
result += indent+line+'\n'
return result
def wrap_code(code, indent = ' ', maxchars = 80, splitchars = '(=,'):
""" Wrap the code lines to the specified number of characters. If
necessary a line will be broken and wrapped after one of the split
characters.
"""
output = ''
# normalize line endings
code = code.replace("\r\n", "\n")
# break the code chunk into lines
lines = string.split(code, '\n')
for line in lines:
if len(line) <= maxchars:
# line is short enough that it doesn't need to be wrapped
output += line + '\n'
continue
# retrieve the whitespace at the beginning of the line for later use
# as padding
ws = ''
for char in line:
if char.isspace():
ws += char
else:
break
# iterate over all characters in the string keeping track of where the
# last valid break character was found and wrapping the line
# accordingly
lastsplit = 0
nextsplit = -1
splitct = 0
pos = 0
for char in line:
if splitchars.find(char) >= 0:
# a new split position has been found
nextsplit = pos
size = pos - lastsplit + 1
if splitct > 0:
size += len(ws) + len(indent)
if size >= maxchars:
# the line is too long
section = line[lastsplit:nextsplit+1]
if len(section) > 0:
# output the line portion between the last split and the
# next split
if splitct > 0:
# start a new line and trim the line section
output += '\n'+ws+indent
section = string.strip(section)
output += section
lastsplit = nextsplit + 1
splitct += 1
pos += 1
if len(line) - lastsplit > 0:
# output the remainder of the line
section = line[lastsplit:]
if splitct > 0:
# start a new line and trim the line section
output += '\n'+ws+indent
section = string.strip(section)
output += section
output += '\n'
return output
def is_base_class(clsname):
""" Returns true if |clsname| is a known base (root) class in the object
hierarchy.
......@@ -392,8 +325,8 @@ _cre_retval = '([A-Za-z0-9_<>:,\*\&]{1,})'
_cre_typedef = '([A-Za-z0-9_<>:,\*\&\s]{1,})'
# regex for matching function return value and name combination
_cre_func = '([A-Za-z][A-Za-z0-9_<>:,\*\&\s]{1,})'
# regex for matching virtual function modifiers
_cre_vfmod = '([A-Za-z0-9_]{0,})'
# regex for matching virtual function modifiers + arbitrary whitespace
_cre_vfmod = '([\sA-Za-z0-9_]{0,})'
# regex for matching arbitrary whitespace
_cre_space = '[\s]{1,}'
# regex for matching optional virtual keyword
......@@ -502,6 +435,8 @@ def get_copyright():
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=$$HASH$$$
//
"""
# add the copyright year
......@@ -598,12 +533,36 @@ class obj_header:
# build the class objects
for attrib, name, parent_name, body in list:
# Style may place the ':' on the next line.
comment = get_comment(data, name+' :')
if len(comment) == 0:
comment = get_comment(data, name+"\n")
validate_comment(filename, name, comment)
self.classes.append(
obj_class(self, filename, attrib, name, parent_name, body,
comment, includes, forward_declares))
# extract empty classes
p = re.compile('\n'+_cre_attrib+
'\nclass'+_cre_space+_cre_cfname+_cre_space+
':'+_cre_space+'public'+_cre_virtual+
_cre_space+_cre_cfname+_cre_space+
'{};', re.MULTILINE | re.DOTALL)
list = p.findall(data)
if len(list) > 0:
added = True
# build the class objects
for attrib, name, parent_name in list:
# Style may place the ':' on the next line.
comment = get_comment(data, name+' :')
if len(comment) == 0:
comment = get_comment(data, name+"\n")
validate_comment(filename, name, comment)
self.classes.append(
obj_class(self, filename, attrib, name, parent_name, "",
comment, includes, forward_declares))
if added:
# a global function or class was read from the header file
self.filenames.append(filename)
......@@ -800,7 +759,7 @@ class obj_class:
# extract virtual functions
p = re.compile('\n'+_cre_space+_cre_attrib+'\n'+_cre_space+'virtual'+
_cre_space+_cre_func+'\((.*?)\)'+_cre_space+_cre_vfmod,
_cre_space+_cre_func+'\((.*?)\)'+_cre_vfmod,
re.MULTILINE | re.DOTALL)
list = p.findall(body)
......@@ -811,7 +770,7 @@ class obj_class:
validate_comment(filename, retval, comment)
self.virtualfuncs.append(
obj_function_virtual(self, attrib, retval, argval, comment,
vfmod))
vfmod.strip()))
def __repr__(self):
result = '/* '+dict_to_str(self.attribs)+' */ class '+self.name+"\n{"
......@@ -2038,7 +1997,7 @@ if __name__ == "__main__":
sys.stdout.write('\n')
# output the parsed C++ data
sys.stdout.write(wrap_code(str(header), '\t'))
sys.stdout.write(str(header))
# output the C API formatted data
defined_names = header.get_defined_structs()
......@@ -2069,4 +2028,4 @@ if __name__ == "__main__":
for func in funcs:
result += func.get_capi_proto(defined_names)+';\n'
result += '\n'
sys.stdout.write(wrap_code(result, '\t'))
sys.stdout.write(result)
@echo off
python.bat check_style.py %*
# Copyright (c) 2012 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.
import os, re, string, sys
from file_util import *
import git_util as git
# script directory
script_dir = os.path.dirname(__file__)
# CEF root directory
cef_dir = os.path.abspath(os.path.join(script_dir, os.pardir))
# Valid extensions for files we want to lint.
DEFAULT_LINT_WHITELIST_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)"
DEFAULT_LINT_BLACKLIST_REGEX = r"$^"
try:
# depot_tools may already be in the import path.
import cpplint
import cpplint_chromium
except ImportError, e:
# Search the PATH environment variable to find the depot_tools folder.
depot_tools = None;
paths = os.environ.get('PATH').split(os.pathsep)
for path in paths:
if os.path.exists(os.path.join(path, 'cpplint_chromium.py')):
depot_tools = path
break
if depot_tools is None:
print >> sys.stderr, 'Error: could not find depot_tools in PATH.'
sys.exit(2)
# Add depot_tools to import path.
sys.path.append(depot_tools)
import cpplint
import cpplint_chromium
# The default implementation of FileInfo.RepositoryName looks for the top-most
# directory that contains a .git folder. This is a problem for CEF because the
# CEF root folder (which may have an arbitrary name) lives inside the Chromium
# src folder. Reimplement in a dumb but sane way.
def patch_RepositoryName(self):
fullname = self.FullName()
project_dir = os.path.dirname(fullname)
if os.path.exists(fullname):
root_dir = project_dir
while os.path.basename(project_dir) != "src":
project_dir = os.path.dirname(project_dir)
prefix = os.path.commonprefix([root_dir, project_dir])
components = fullname[len(prefix) + 1:].split('/')
return string.join(["cef"] + components[1:], '/')
return fullname
def check_style(args, white_list = None, black_list = None):
""" Execute cpplint with the specified arguments. """
# Apply patches.
cpplint.FileInfo.RepositoryName = patch_RepositoryName
# Process cpplint arguments.
filenames = cpplint.ParseArguments(args)
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)
extra_check_functions = [cpplint_chromium.CheckPointerDeclarationWhitespace]
for filename in filenames:
if white_regex.match(filename):
if black_regex.match(filename):
print "Ignoring file %s" % filename
else:
cpplint.ProcessFile(filename, cpplint._cpplint_state.verbose_level,
extra_check_functions)
else:
print "Skipping file %s" % filename
print "Total errors found: %d\n" % cpplint._cpplint_state.error_count
return 1
if __name__ == "__main__":
# Start with the default parameters.
args = [
# * Disable the 'build/class' test because it errors uselessly with C
# structure pointers and template declarations.
# * Disable the 'runtime/references' test because CEF allows non-const
# arguments passed by reference.
# * Disable the 'runtime/sizeof' test because it has a high number of
# false positives and adds marginal value.
'--filter=-build/class,-runtime/references,-runtime/sizeof',
]
# Add anything passed on the command-line.
args += sys.argv[1:]
# Pre-process the arguments before passing to the linter.
new_args = []
changed = []
for arg in args:
if arg == '--changed':
# Add any changed files.
changed = git.get_changed_files(cef_dir)
elif arg[:2] == '--' or not os.path.isdir(arg):
# Pass argument unchanged.
new_args.append(arg)
else:
# Add all files in the directory.
new_args += get_files(os.path.join(arg, '*'))
if len(changed) > 0:
new_args += changed
check_style(new_args)
#!/bin/sh
python check_style.py $@
# Copyright (c) 2017 The Chromium Embedded Framework 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 exec_util import exec_cmd
import sys
if sys.platform == 'win32':
# Force use of the clang-format version bundled with depot_tools.
clang_format_exe = 'clang-format.bat'
else:
clang_format_exe = 'clang-format'
def clang_format(file_contents):
result = exec_cmd(clang_format_exe, ".", file_contents)
if result['out'] != '':
output = result['out']
if sys.platform == 'win32':
# Convert to Unix line endings.
output = output.replace("\r", "")
return output
return None
@echo off
python.bat tools\fix_style.py %*
# 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.
import os, re, sys
from clang_util import clang_format
from file_util import *
from git_util import get_changed_files
# Valid extensions for files we want to clang-format.
DEFAULT_LINT_WHITELIST_REGEX = r"(.*\.cpp|.*\.cc|.*\.h|.*\.mm)$"
DEFAULT_LINT_BLACKLIST_REGEX = r"$^"
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 update_file(filename):
oldcontents = read_file(filename)
if len(oldcontents) == 0:
msg(filename, "empty")
return;
newcontents = clang_format(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:
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] ..." % sys.argv[0]
print "\n 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)
# Process anything passed on the command-line.
fix_style(sys.argv[1:])
print 'Done - Wrote %d files.' % updatect
#!/bin/sh
python tools/fix_style.py $@
......@@ -40,9 +40,21 @@ def get_commit_number(path = '.', branch = 'HEAD'):
return result['out'].strip()
return '0'
def get_changed_files(path = '.'):
def get_changed_files(path, hash):
""" Retrieves the list of changed files. """
# not implemented
if hash == 'unstaged':
cmd = "%s diff --name-only" % git_exe
elif hash == 'staged':
cmd = "%s diff --name-only --cached" % git_exe
else:
cmd = "%s diff-tree --no-commit-id --name-only -r %s" % (git_exe, hash)
result = exec_cmd(cmd, path)
if result['out'] != '':
files = result['out']
if sys.platform == 'win32':
# Convert to Unix line endings.
files = files.replace('\r\n', '\n')
return files.strip().split("\n")
return []
def write_indented_output(output):
......
......@@ -14,8 +14,7 @@ def make_capi_global_funcs(funcs, defined_names, translate_map, indent):
result += '\n'+format_comment(comment, indent, translate_map);
if func.get_retval().get_type().is_result_string():
result += indent+'// The resulting string must be freed by calling cef_string_userfree_free().\n'
result += wrap_code(indent+'CEF_EXPORT '+
func.get_capi_proto(defined_names)+';')
result += indent+'CEF_EXPORT '+func.get_capi_proto(defined_names)+';\n'
if first:
first = False
return result
......@@ -30,9 +29,8 @@ def make_capi_member_funcs(funcs, defined_names, translate_map, indent):
if func.get_retval().get_type().is_result_string():
result += indent+'// The resulting string must be freed by calling cef_string_userfree_free().\n'
parts = func.get_capi_parts()
result += wrap_code(indent+parts['retval']+' (CEF_CALLBACK *'+
parts['name']+')('+
string.join(parts['args'], ', ')+');')
result += indent+parts['retval']+' (CEF_CALLBACK *'+parts['name']+ \
')('+string.join(parts['args'], ', ')+');\n'
if first:
first = False
return result
......@@ -81,6 +79,8 @@ def make_capi_header(header, filename):
// by hand. See the translator.README.txt file in the tools directory for
// more information.
//
// $hash=$$HASH$$$
//
#ifndef $GUARD$
#define $GUARD$
......@@ -118,7 +118,10 @@ def make_capi_header(header, filename):
translated_includes.add(include)
declares = cls.get_forward_declares()
for declare in declares:
all_declares.add(header.get_class(declare).get_capi_name())
declare_cls = header.get_class(declare)
if declare_cls is None:
raise Exception('Unknown class: %s' % declare)
all_declares.add(declare_cls.get_capi_name())
# output translated includes
if len(translated_includes) > 0:
......@@ -195,24 +198,10 @@ extern "C" {
return result
def write_capi_header(header, header_dir, filename, backup):
capi_path = get_capi_file_name(os.path.join(header_dir, filename))
if path_exists(capi_path):
oldcontents = read_file(capi_path)
else:
oldcontents = ''
def write_capi_header(header, header_dir, filename):
file = get_capi_file_name(os.path.join(header_dir, filename))
newcontents = make_capi_header(header, filename)
if newcontents != oldcontents:
if backup and oldcontents != '':
backup_file(capi_path)
capi_dir = os.path.split(capi_path)[0]
if not os.path.isdir(capi_dir):
make_dir(capi_dir)
write_file(capi_path, newcontents)
return True
return False
return (file, newcontents)
# test the module
......
......@@ -76,31 +76,17 @@ def make_cpptoc_header(header, clsname):
result += '#endif // CEF_LIBCEF_DLL_CPPTOC_'+defname+'_CPPTOC_H_'
return wrap_code(result)
return result
def write_cpptoc_header(header, clsname, dir, backup):
def write_cpptoc_header(header, clsname, dir):
# give the output file the same directory offset as the input file
cls = header.get_class(clsname)
dir = os.path.dirname(os.path.join(dir, cls.get_file_name()))
file = os.path.join(dir, get_capi_name(clsname[3:], False)+'_cpptoc.h')
if path_exists(file):
oldcontents = read_file(file)
else:
oldcontents = ''
newcontents = make_cpptoc_header(header, clsname)
if newcontents != oldcontents:
if backup and oldcontents != '':
backup_file(file)
file_dir = os.path.split(file)[0]
if not os.path.isdir(file_dir):
make_dir(file_dir)
write_file(file, newcontents)
return True
return False
return (file, newcontents)
# test the module
......
......@@ -23,9 +23,7 @@ def make_cpptoc_function_impl_existing(cls, name, func, impl, defined_names):
if len(changes) > 0:
notify(name+' prototype changed')
return wrap_code(make_cpptoc_impl_proto(name, func, parts))+'{'+ \
changes+impl['body']+'\n}\n'
return result
return make_cpptoc_impl_proto(name, func, parts)+'{'+changes+impl['body']+'\n}\n\n'
def make_cpptoc_function_impl_new(cls, name, func, defined_names):
# retrieve the C API prototype parts
......@@ -62,7 +60,7 @@ def make_cpptoc_function_impl_new(cls, name, func, defined_names):
result += '\n #pragma message("Warning: "__FILE__": '+name+' is not implemented")'
result += '\n // END DELETE BEFORE MODIFYING'
result += '\n}\n\n'
return wrap_code(result)
return result
result += '\n // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING\n'
......@@ -454,8 +452,8 @@ def make_cpptoc_function_impl_new(cls, name, func, defined_names):
if len(result) != result_len:
result += '\n'
result += '}\n'
return wrap_code(result)
result += '}\n\n'
return result
def make_cpptoc_function_impl(cls, funcs, existing, prefixname, defined_names):
impl = ''
......@@ -633,7 +631,7 @@ def make_cpptoc_class_impl(header, clsname, impl):
'#endif\n\n'+ \
'template<> CefWrapperType '+parent_sig+'::kWrapperType = '+get_wrapper_type_enum(clsname)+';'
result += '\n\n'+wrap_code(const)
result += '\n\n'+const
return result
......@@ -671,7 +669,7 @@ def make_cpptoc_global_impl(header, impl):
return result
def write_cpptoc_impl(header, clsname, dir, backup):
def write_cpptoc_impl(header, clsname, dir):
if clsname is None:
# global file
file = dir
......@@ -691,16 +689,7 @@ def write_cpptoc_impl(header, clsname, dir, backup):
newcontents = make_cpptoc_global_impl(header, oldcontents)
else:
newcontents = make_cpptoc_class_impl(header, clsname, oldcontents)
if newcontents != oldcontents:
if backup and oldcontents != '':
backup_file(file)
file_dir = os.path.split(file)[0]
if not os.path.isdir(file_dir):
make_dir(file_dir)
write_file(file, newcontents)
return True
return False
return (file, newcontents)
# test the module
......
......@@ -119,31 +119,17 @@ def make_ctocpp_header(header, clsname):
result += '#endif // CEF_LIBCEF_DLL_CTOCPP_'+defname+'_CTOCPP_H_'
return wrap_code(result)
return result
def write_ctocpp_header(header, clsname, dir, backup):
def write_ctocpp_header(header, clsname, dir):
# give the output file the same directory offset as the input file
cls = header.get_class(clsname)
dir = os.path.dirname(os.path.join(dir, cls.get_file_name()))
file = os.path.join(dir, get_capi_name(clsname[3:], False)+'_ctocpp.h')
if path_exists(file):
oldcontents = read_file(file)
else:
oldcontents = ''
newcontents = make_ctocpp_header(header, clsname)
if newcontents != oldcontents:
if backup and oldcontents != '':
backup_file(file)
file_dir = os.path.split(file)[0]
if not os.path.isdir(file_dir):
make_dir(file_dir)
write_file(file, newcontents)