cpplint解析与改进
发布者:
Renjian
前阵子基于google的cpplint实现了检查我们自己项目coding style的lint工具,本文对cpplint相关知识进行汇总(注:本文中的代码为部分代码,不能直接运行)。 本文分为两个部分,cpplint代码结构解析和改造cpplint。
代码结构解析
cpplint
主要从两个方面对代码进行检测
- 基于行,如空格,特殊字符,类型转换等。
- 基于代码块,如
namespace
,函数,类等。
对以上两个方面的检测间接决定cpplint的基本思想,即逐行扫描文件,并记录文件的代码块信息,同时进行检测,将检测结果通过error函数输出。下图通过函数调用关系介绍了代码的基本结构。
其主干线为
ParseArguments()--->processFile()-->ProcessFileData()--> ProcessLine() -->PrintErrorCounts()
下图为cpplint的类图,Cpplint
输入为文件名,输出lint信息。
CppLintState()
为核心类,cpplint通过它管理其他类进行checkFileInfo
记录文件的类型,长度等基本信息,cpplint根据这些基本信息来决定是否检测该文件CleanSedLines()
记录文件的行信息,用来进行基于行的检测及基于代码块检测的预处理- 其他类主要用来储存代码块的信息,用来进行代码块的检测
改造cpplint
本部分主要介绍针对cpplint的改造,使之满足项目的特定需要。
- 以前只支持输入文件,现在支持输入文件夹,检测文件夹下面所有文件,在
main
函数里对filename
进行再处理
for filename in filenames:
if os.path.isdir(filename):
for root, _dirs, files in os.walk(filename):
for subfile in files:
subfilename=os.path.join(root,subfile)
ProcessFile(subfilename, _cpplint_state.verbose_level)
else:
ProcessFile(filename, _cpplint_state.verbose_level)
-
跟perforce相结合添加
changelist
和datafile
参数, 检测特定changelist里面新添加的文件(不包括move file)。基本思想为获取changelist里面的文件,将add的文件组成一个set,delete的文件组成一个set.检测add文件set里面的文件,其文件名不能出现在delete set中。 -
添加–datafile参数,检测配置文件里记录的文件
elif opt == '--changelist':
changelist_num = val
filenames_changelist = GetFilenamesFromChangeList(changelist_num, p4envlist)
filenames += filenames_changelist
elif opt == '--datafile':
datafile = val
filenames_datafile = GetFilenamesFromDataFile(datafile, p4envlist)
filenames += filenames_datafile
elif ...:
def GetFilenamesFromDataFile(datafile, p4envlist):
"""Get filenames from existing data file
"""
depot_data_file = open(datafile, 'r')
# ConvertDepotPathToWorkPath() transform the depot path in perforce to local path
linted_data_filenames = set(ConvertDepotPathToWorkPath(depot_filename.rstrip('\n'), p4envlist)
for depot_filename in depot_data_file if depot_filename != '\n')
return list(linted_data_filenames)
def UpdateDataFileFromChangeList(linted_file, changelist_num, p4envlist):
"""Update From specifical changelist
Add new added file to datafile
Input: [linted_file, changelist_num, p4envlist]
Return: [update_files, is_linted_file_changed]
"""
linted_filenames = set(linted_filename.rstrip('\n') for linted_filename in linted_file)
if p4envlist:
describe_output = subprocess.Popen(('p4', '-p', p4envlist[0], '-u', p4envlist[1],
'-c', p4envlist[2], 'describe', '-s', changelist_num),
stdout=subprocess.PIPE).stdout)
checked_file_lines = describe_output.readlines()
describe_output.close()
if 'Affected files ...\n' in checked_file_lines:
begin_index = checked_file_lines.index('Affected files ...\n')
checked_file_lines = checked_file_lines[begin_index+2:-1]
else:
opened_output = subprocess.Popen(('p4', 'opened', '-c', changelist_num),
stdout=subprocess.PIPE).stdout)
checked_file_lines = opened_output.readlines()
opened_output.close()
update_files = set()
add_depot_files = set()
delete_depot_files = set()
for line in checked_file_lines:
line = line.strip()
if not line:
continue
if p4envlist:
m_line = re.search(r'\.\.\. (?P<depot_path>[^#]+)#\d+ (?P<file_opt>add|delete|edit)', line)
else:
m_line = re.search(r'(?P<depot_path>[^#]+)#\d+ - (?P<file_opt>add|delete|edit) .*', line)
if not m_line:
continue
depot_file = m_line.group('depot_path')
....
if os.path.splitext(depot_file)[-1] not in ('.c', '.cc', '.cpp', '.cxx', '.h', '.hpp'):
continue
if m_line.group('file_opt') == 'add':
add_depot_files.add(depot_file)
elif m_line.group('file_opt') == 'delete':
delete_depot_files.add(depot_file)
elif depot_file in linted_filenames and not p4envlist:
update_files.add(ConvertDepotPathToWorkPath(depot_file))
delete_filenames = {}
for delete_depot_file in delete_depot_files:
delete_work_file = ConvertDepotPathToWorkPath(delete_depot_file, p4envlist)
delete_filename = os.path.basename(delete_work_file)
delete_filenames[delete_filename] = delete_depot_file
is_linted_file_changed = False
for add_depot_file in add_depot_files:
add_work_file = ConvertDepotPathToWorkPath(add_depot_file, p4envlist)
add_filename = os.path.basename(add_work_file)
if add_filename in delete_filenames:
if delete_filenames[add_filename] not in linted_filenames:
continue
update_files.add(add_work_file)
if add_depot_file not in linted_filenames:
linted_file.write(add_depot_file+'\n')
is_linted_file_changed = True
return [update_files, is_linted_file_changed]
- 添加
--custom
参数,支持自定义check rule
cpplint.py
提供--filter
参数来实现lint类型的过滤.
cpplint规定一系列lint的类型,whitespace
, +whitespace/braces
, runtime/printf
, +runtime/printf_format
默认情况下cpplint检查所有类型(详细内容可参考代码,已在其他文中介绍),用户可以通过filter=-x,+y,...
定制自己的lint需求
"-FOO" means "do not print categories that start with FOO".
"+FOO" means "do print categories that start with FOO".
Examples: --filter=-whitespace,+whitespace/braces,+build/include_what_you_use
cpplint提供了--customstyle
参数,通过--customstyle
参数,用户可以输入一个以正则表达式形式表现的check rule, 通过这个rule去check目标文件的每一行.
由于在customstyle参数输入正则表达式的时候不支持空格,用户可以以两个叹号(’!!’)进行替换,程序内部会将两个叹号转换成空格然后进行正则检测。
例如:
python cpplint.py --customstyle=void.*!! $file
实现代码如下:
_CUSTOM_STYLE_TEMPLATES = []
... elif opt == '--customstyle':
input_style = val.replace('!!', ' ')
global _CUSTOM_STYLE_TEMPLATES
_CUSTOM_STYLE_TEMPLATES.append(input_style)
global _CUSTOM_STYLE_TEMPLATES...
def CheckCustomStyle(filename, clean_lines, linenum, error):
"""Check for custom style which users defined.
Args:
filename: The name of the current file.
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
error: The function to call with any errors found.
"""
line = clean_lines.elided[linenum]
for custom_style in _CUSTOM_STYLE_TEMPLATES:
if Search(r'%s'%custom_style, line)::q
error(filename, linenum, 'readability/braces', 5,
'this line can not follow the custom style: %s'%custom_style)
与其他工具的结合
cpplint作为代码检查工具,可以和其他工具相结合,如perforce, Visual Studio, eclipse等, 其他方法类似,这里不再赘述。
- Linux: 进入cpplint.py所在目录,执行如下命令
optional: p4 login
python cpplint.py --changelist='195822'
上面code表示对ChangeList 195822中的文件进行代码检查,目前仅对新加的文件进行检查。
- Windows: 在perforce点击Tools-Manage Custom Tools…上面进行设置,如图:
选择Tool,按下图进行设置,Application 选择同步下来的cpplint.bat文件,点击OK,就可以在P4中使用cpplint.
在perforce中使用cpplint跟post-review一样,右键changelist, 点击cpplint,即可以看到cpplint的结果,如下:
睿初科技软件开发技术博客,转载请注明出处
blog comments powered by Disqus
发布日期
标签
最近发表
- volatile与多线程
- TDD practice in UI: Develop and test GUI independently by mockito
- jemalloc源码解析-核心架构
- jemalloc源码解析-内存管理
- boost::bind源码分析
- 小试QtTest
- 一个gtk下的目录权限问题
- Django学习 - Model
- Code snippets from C & C++ Code Capsule
- Using Eclipse Spy in GUI products based on RCP
文章分类
- cpp 3
- wxwidgets 4
- swt/jface 1
- chrome 3
- memory_management 5
- eclipse 1
- 工具 4
- 项目管理 1
- cpplint 1
- 算法 1
- 编程语言 1
- python 5
- compile 1
- c++ 7
- 工具 c++ 1
- 源码分析 c++ 3
- c++ boost 2
- data structure 1
- wxwidgets c++ 1
- template 1
- boost 1
- wxsocket 1
- wxwidget 2
- java 2
- 源码分析 1
- 网路工具 1
- eclipse插件 1
- django 1
- gtk 1
- 测试 1
- 测试 tdd 1
- multithreading 1