回肠荡气什么意思| 家长里短是什么意思| 惶恐是什么意思| 小孩便秘是什么原因引起的| 雅漾喷雾有什么功效| 预警是什么意思| hp值是什么意思| 木鱼是什么意思| 紫癜是什么病| 八段锦什么时候练最好| 撤退性出血什么意思| 茶不能和什么一起吃| 什么是蝴蝶效应| 左边头疼是什么原因怎么办| 累的什么| 月经期间吃什么补血| nuxe是什么牌子| 砷是什么东西| 红眼鱼是什么鱼| 什么叫封闭针| 前列腺吃什么药| 等离子是什么| 大型血小板比率偏低是什么意思| 天生一对是什么意思| sla是什么意思| 流虚汗是什么原因| 体制外是什么意思| 嘴巴长疱疹是什么原因| 谷草谷丙偏高代表什么| 直肠窝积液是什么意思| 胃不好早餐吃什么好| 食管反流吃什么药最好| 八方来财是什么生肖| 幽门杆菌有什么症状| 哺乳期感冒可以吃什么药| 鸡蛋壳薄是什么原因| 猫传腹是什么病| 不值一提是什么意思| 喝酒过敏是什么原因| 山楂泡酒有什么功效| 口苦吃什么好| 龋牙是什么意思| 什么东西补钙| 吃榴莲有什么坏处| april是什么意思| 纣王叫什么名字| 天蝎座的幸运色是什么| 脚背疼挂什么科| 腾云驾雾是什么生肖| 紫五行属什么| 阴液是什么| 什么军什么马| 痔疮是什么病| 粗茶淡饭下一句是什么| 梦见杀鸡见血什么征兆| 反复口腔溃疡是什么病的前兆| 来月经可以吃什么| 什么叫洗钱| 这些是什么| 玉和玉髓有什么区别| 桑葚补什么| 鸢是什么意思| 经常叹气是什么原因| 减肥吃什么药好| 东坡肉属于什么菜系| 2019是什么生肖| 尿是什么味道| 吃小米粥有什么好处| 柚子不能和什么一起吃| 谷草转氨酶偏低是什么原因| 等离子是什么| 吃什么生精养精最快| 房奴什么意思| 随波逐流什么意思| 什么情况下会怀孕| 孕妇流鼻血是什么原因| 为什么没有广东大学| ca199偏高是什么原因| 一个厂一个人念什么| 十一朵玫瑰花代表什么意思| 酸枣仁有什么功效| 农历八月十三是什么星座| 菩提根是什么材质| 接骨木是什么| 昆仑山在什么地方| 家里为什么有跳蚤| 枸杞泡水喝有什么作用和功效| 脸部出汗多是什么原因引起的| 素股是什么意思| 免疫组化是什么| 为什么会出现幻觉| 什么是单克隆抗体| 阴唇肥大有什么影响| 悬案是什么意思| 1965年什么时候退休| 无法无天是什么生肖| 吃什么增强抵抗力| 暗卫是什么意思| 男士圆脸适合什么发型| 感冒了吃什么水果好| 心房纤颤是什么意思| 什么鸟| 阴道放气是什么原因| 给女生送礼物送什么好| 胆囊切除后对身体有什么影响| 吃什么避孕药可以推迟月经| gr是什么| 右肾钙化灶是什么意思| 10月25日什么星座| 腿脚酸软无力是什么原因| 暖心向阳是什么意思| 飞机用什么油| 湿疹是什么原因引起的| 提高免疫力吃什么药| 吃葡萄对身体有什么好处| 蛞蝓是什么| 水杯什么品牌好| 缺维生素D吃什么补得最快| 97年属什么今年多大| 午时是什么时候| 钠低是什么原因造成的| 纤维瘤是什么| 客套是什么意思| 畸胎瘤是什么病严重吗| 嗓子疼感冒吃什么药| 胎芽是什么意思| 电器发生火灾用什么灭火器| 硬刚是什么意思| 腺样体肥大有什么症状| 升白针是什么药| 装牙套有什么坏处| 威士忌兑什么饮料好喝| 插入是什么感觉| 看身高挂什么科| 自渎是什么意思| 元老是什么意思| 煤油对人体有什么危害| 12月3日什么星座| 一什么鱼| 全科医学科是什么科| 吃什么白细胞升的最快| 口周读什么| 什么尾巴长不了| 什么的阳光| 5月3号是什么星座| barry什么意思| 吃什么长肌肉| 阴道发白是什么原因| 双向情感障碍是什么意思| 草莓什么时候成熟| 什么地飞| 风暴是什么意思| 0代表什么意思| 老炮是什么意思| 傍晚是什么时候| 缺铁性贫血吃什么药好| 什么生肖最旺鸡| 花儿为什么这样红歌词| 中风吃什么药最有效| 为什么家里会有蜈蚣| 记忆是什么意思| 功是什么意思| 脚臭是什么原因引起的| 盆腔炎是什么原因引起的| 小孩干呕是什么原因| 老感冒是什么原因| 球蛋白有什么作用和功效| 过继是什么意思| 梦见碗是什么意思| 1989年什么生肖| 后援会是什么意思| 中的反义词是什么| 胃火旺吃什么药| 男人梦见猫是什么意思| 郡字五行属什么| 剖腹产什么时候可以洗澡| 四个月念什么| 蠼螋对人有什么危害| 泼皮是什么意思| 脖子上长扁平疣是什么原因| 猪肉和什么菜搭配最好| 爱马仕是什么牌子| 舌头有裂纹什么原因| 发烧41度是什么概念| 油耳是什么意思| 跳蚤怕什么| 吃海带有什么好处和坏处| 低压高吃点什么药| 女士内裤用什么洗最好| 螚什么意思| hiv是什么| 心虚吃什么补最快| 无犯罪证明需要什么材料| 发什么大成语| 肾囊肿有什么危害| 为什么说白痰要人命| 胃反流是什么原因| 支气管炎吃什么药有效| viagra是什么药| 食积是什么意思| c1和c2有什么区别| 男人前列腺炎有什么症状表现| 体质指数是什么意思| 苏轼是什么朝代的| 牙龈出血用什么牙膏| 来月经吃什么水果| ec是什么意思| sk-ll是什么牌子| 为什么会长瘤| 什么无终| 孩子咬嘴唇是什么原因| 化疗中的病人应该吃什么| 均码是什么意思| 哆啦a梦的口袋叫什么| 乳酸杆菌大量是什么意思| 什么是毛囊炎及症状图片| 海尔兄弟叫什么| 结巴是什么原因引起的| 什么是命中注定| 心肌供血不足吃什么药| 一生无虞是什么意思| trust什么意思| 奶油色是什么颜色| Rm是什么| 今年71岁属什么生肖| 尿蛋白十一什么意思| 曹操姓什么| 精液的主要成分是什么| 刮腻子是什么意思| 什么蚌相争| 梅毒是什么意思| 英氏属于什么档次的| 雷人是什么意思| 小肝癌是什么意思| 儿童身高矮小挂什么科| 授教什么意思| 10月1日是什么日子| 5月20日是什么日子| 离卦代表什么| 什么什么动听| 10月20是什么星座| 楼台是什么意思| 什么羊肉最好吃| hav是什么病毒| 发字五行属什么| 苦命是什么意思| 甲醛中毒挂什么科| 茼蒿不能和什么一起吃| 什么是被子植物| 舌头发涩是什么原因造成的| 粉瘤挂什么科| 锌中毒是什么症状| 方脸适合什么耳环| 养胃喝什么茶| 自食其力是什么意思| 湿气重吃什么中成药| 豆角炒什么好吃| 卖淫什么意思| 柑橘溃疡病用什么药| 什么时候阅兵| 二十年婚姻是什么婚| 胆固醇高吃什么| 为什么尽量抽混合型烟| 雌二醇过高是什么原因| 百度
blob: 958e6f57311d1f50bf17dbc80eedea79aeefdead [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Helper for adding or removing an include to/from source file(s).
clang-format already provides header sorting functionality; however, the
functionality is limited to sorting headers within a block of headers surrounded
by blank lines (these are a heuristic to avoid clang breaking ordering for
headers sensitive to inclusion order, e.g. <windows.h>).
As a result, inserting a new header is a bit more complex than simply inserting
the new header at the top and running clang-format.
This script implements additional logic to:
- classify different blocks of headers by type (C system, C++ system, user)
- find the appropriate insertion point for the new header
- creating a new header block if necessary
As a bonus, it does *also* sort the includes, though any sorting disagreements
with clang-format should be resolved in favor of clang-format.
It also supports removing a header with option `--remove`.
Usage:
tools/add_header.py --header '<utility>' foo/bar.cc foo/baz.cc foo/baz.h
tools/add_header.py --header '<vector>' --remove foo/bar.cc foo/baz.cc foo/baz.h
"""
import argparse
import difflib
import os.path
import re
import sys
# The specific values of these constants are also used as a sort key for
# ordering different header types in the correct relative order.
_HEADER_TYPE_C_SYSTEM = 0
_HEADER_TYPE_CXX_SYSTEM = 1
_HEADER_TYPE_USER = 2
_HEADER_TYPE_INVALID = -1
def ClassifyHeader(decorated_name):
if IsCSystemHeader(decorated_name):
return _HEADER_TYPE_C_SYSTEM
elif IsCXXSystemHeader(decorated_name):
return _HEADER_TYPE_CXX_SYSTEM
elif IsUserHeader(decorated_name):
return _HEADER_TYPE_USER
else:
return _HEADER_TYPE_INVALID
def UndecoratedName(decorated_name):
"""Returns the undecorated version of decorated_name by removing "" or <>."""
assert IsSystemHeader(decorated_name) or IsUserHeader(decorated_name)
return decorated_name[1:-1]
def IsSystemHeader(decorated_name):
"""Returns true if decorated_name looks like a system header."""
return decorated_name[0] == '<' and decorated_name[-1] == '>'
def IsCSystemHeader(decorated_name):
"""Returns true if decoraed_name looks like a C system header."""
return IsSystemHeader(decorated_name) and UndecoratedName(
decorated_name).endswith('.h')
def IsCXXSystemHeader(decorated_name):
"""Returns true if decoraed_name looks like a C++ system header."""
return IsSystemHeader(
decorated_name) and not UndecoratedName(decorated_name).endswith('.h')
def IsUserHeader(decorated_name):
"""Returns true if decoraed_name looks like a user header."""
return decorated_name[0] == '"' and decorated_name[-1] == '"'
_EMPTY_LINE_RE = re.compile(r'\s*$')
_COMMENT_RE = re.compile(r'\s*//(.*)$')
_INCLUDE_RE = re.compile(
r'\s*#(import|include)\s+([<"].+?[">])\s*?(?://(.*))?$')
def FindIncludes(lines):
"""Finds the block of #includes, assuming Google+Chrome C++ style source.
Note that this doesn't simply return a slice of the input lines, because
having the actual indices simplifies things when generatingn the updated
source text.
Args:
lines: The source text split into lines.
Returns:
A tuple of begin, end indices that can be used to slice the input lines to
contain the includes to process. Returns -1, -1 if no such block of
input lines could be found.
"""
begin = end = -1
for idx, line in enumerate(lines):
# Skip over any initial comments (e.g. the copyright boilerplate) or empty
# lines.
# TODO(dcheng): This means that any preamble comment associated with the
# first header will be dropped. So far, this hasn't broken anything, but
# maybe this needs to be more clever.
# TODO(dcheng): #define and #undef should probably also be allowed.
if _EMPTY_LINE_RE.match(line) or _COMMENT_RE.match(line):
continue
m = _INCLUDE_RE.match(line)
if not m:
if begin < 0:
# No match, but no #includes have been seen yet. Keep scanning for the
# first #include.
continue
# Give up, it's something weird that probably requires manual
# intervention.
break
if begin < 0:
begin = idx
end = idx + 1
return begin, end
class Include(object):
"""Represents an #include/#import and any interesting metadata for it.
Attributes:
decorated_name: The name of the header file, decorated with <> for system
headers or "" for user headers.
directive: 'include' or 'import'
TODO(dcheng): In the future, this may need to support C++ modules.
preamble: Any comment lines that precede this include line, e.g.:
// This is a preamble comment
// for a header file.
#include <windows.h>
would have a preamble of
['// This is a preamble comment', '// for a header file.'].
inline_comment: Any comment that comes after the #include on the same line,
e.g.
#include <windows.h> // For CreateWindowExW()
would be parsed with an inline comment of ' For CreateWindowExW'.
header_type: The header type corresponding to decorated_name as determined
by ClassifyHeader().
is_primary_header: True if this is the primary related header of a C++
implementation file. Any primary header will be sorted to the top in its
own separate block.
"""
def __init__(self, decorated_name, directive, preamble, inline_comment):
self.decorated_name = decorated_name
assert directive == 'include' or directive == 'import'
self.directive = directive
self.preamble = preamble
self.inline_comment = inline_comment
self.header_type = ClassifyHeader(decorated_name)
assert self.header_type != _HEADER_TYPE_INVALID
self.is_primary_header = False
def __repr__(self):
return str((self.decorated_name, self.directive, self.preamble,
self.inline_comment, self.header_type, self.is_primary_header))
def ShouldInsertNewline(self, previous_include):
# Per the Google C++ style guide, different blocks of headers should be
# separated by an empty line.
return (self.is_primary_header != previous_include.is_primary_header
or self.header_type != previous_include.header_type)
def ToSource(self):
"""Generates a C++ source representation of this include."""
source = []
source.extend(self.preamble)
include_line = '#%s %s' % (self.directive, self.decorated_name)
if self.inline_comment:
include_line = include_line + ' //' + self.inline_comment
source.append(include_line)
return [line.rstrip() for line in source]
def ParseIncludes(lines):
"""Parses lines into a list of Include objects. Returns None on failure.
Args:
lines: A list of strings representing C++ source text.
Returns:
A list of Include objects representing the parsed input lines, or None if
the input lines could not be parsed.
"""
includes = []
preamble = []
for line in lines:
if _EMPTY_LINE_RE.match(line):
if preamble:
# preamble contents are flushed when an #include directive is matched.
# If preamble is non-empty, that means there is a preamble separated
# from its #include directive by at least one newline. Just give up,
# since the sorter has no idea how to preserve structure in this case.
return None
continue
m = _INCLUDE_RE.match(line)
if not m:
preamble.append(line)
continue
includes.append(Include(m.group(2), m.group(1), preamble, m.group(3)))
preamble = []
# In theory, the caller should never pass a list of lines with a dangling
# preamble. But there's a test case that exercises this, and just in case it
# actually happens, fail a bit more gracefully.
if preamble:
return None
return includes
def _DecomposePath(filename):
"""Decomposes a filename into a list of directories and the basename.
Args:
filename: A filename!
Returns:
A tuple of a list of directories and a string basename.
"""
dirs = []
dirname, basename = os.path.split(filename)
while dirname:
dirname, last = os.path.split(dirname)
dirs.append(last)
dirs.reverse()
# Remove the extension from the basename.
basename = os.path.splitext(basename)[0]
return dirs, basename
_PLATFORM_SUFFIX = (
r'(?:_(?:android|aura|chromeos|fuchsia|ios|linux|mac|ozone|posix|win|x11))?'
)
_TEST_SUFFIX = r'(?:_(?:browser|interactive_ui|perf|ui|unit)?test)?'
def MarkPrimaryInclude(includes, filename):
"""Finds the primary header in includes and marks it as such.
Per the style guide, if moo.cc's main purpose is to implement or test the
functionality in moo.h, moo.h should be ordered first in the includes.
Args:
includes: A list of Include objects.
filename: The filename to use as the basis for finding the primary header.
"""
# Header files never have a primary include.
if filename.endswith('.h'):
return
# First pass. Looking for exact match primary header.
exact_match_primary_header = f'{os.path.splitext(filename)[0]}.h'
for include in includes:
if IsUserHeader(include.decorated_name) and UndecoratedName(
include.decorated_name) == exact_match_primary_header:
include.is_primary_header = True
return
basis = _DecomposePath(filename)
# Second pass. The list of includes is searched in reverse order of length.
# Even though matching is fuzzy, moo_posix.h should take precedence over moo.h
# when considering moo_posix.cc.
includes.sort(key=lambda i: -len(i.decorated_name))
for include in includes:
if include.header_type != _HEADER_TYPE_USER:
continue
to_test = _DecomposePath(UndecoratedName(include.decorated_name))
# If the basename to test is longer than the basis, just skip it and
# continue. moo.c should never match against moo_posix.h.
if len(to_test[1]) > len(basis[1]):
continue
# The basename in the two paths being compared need to fuzzily match.
# This allows for situations where moo_posix.cc implements the interfaces
# defined in moo.h.
escaped_basename = re.escape(to_test[1])
if not (re.match(escaped_basename + _PLATFORM_SUFFIX + _TEST_SUFFIX + '$',
basis[1]) or
re.match(escaped_basename + _TEST_SUFFIX + _PLATFORM_SUFFIX + '$',
basis[1])):
continue
# The topmost directory name must match, and the rest of the directory path
# should be 'substantially similar'.
s = difflib.SequenceMatcher(None, to_test[0], basis[0])
first_matched = False
total_matched = 0
for match in s.get_matching_blocks():
if total_matched == 0 and match.a == 0 and match.b == 0:
first_matched = True
total_matched += match.size
if not first_matched:
continue
# 'Substantially similar' is defined to be:
# - no more than two differences
# - at least one match besides the topmost directory
total_differences = abs(total_matched -
len(to_test[0])) + abs(total_matched -
len(basis[0]))
# Note: total_differences != 0 is mainly intended to allow more succinct
# tests (otherwise tests with just a basename would always trip the
# total_matched < 2 check).
if total_differences != 0 and (total_differences > 2 or total_matched < 2):
continue
include.is_primary_header = True
return
def SerializeIncludes(includes):
"""Turns includes back into the corresponding C++ source text.
Args:
includes: a list of Include objects.
Returns:
A list of strings representing C++ source text.
"""
source = []
# LINT.IfChange(winheader)
# Headers that are sorted above others to prevent inclusion order issues.
# NOTE: The order of these headers is the sort key and will be the order in
# the output file. It should be set to match whatever clang-format will do.
special_headers = [
# Listed first because it must be before initguid.h in the block below.
'<objbase.h>',
# Alphabetized block that don't matter relative to each other, but need to
# be included before any instance of the listed other header. These other
# listed headers are non-exhaustive examples.
'<atlbase.h>', # atlapp.h
'<initguid.h>', # emi.h
'<mmdeviceapi.h>', # functiondiscoverykeys_devpkey.h
'<ocidl.h>', # commdlg.h
'<ole2.h>', # intshcut.h
'<shobjidl.h>', # propkey.h
'<tchar.h>', # tpcshrd.h
'<unknwn.h>', # intshcut.h
'<windows.h>', # hidclass.h, memoryapi.h, ncrypt.h, shellapi.h,
# versionhelpers.h, winbase.h, etc.
'<winsock2.h>', # ws2tcpip.h
'<winternl.h>', # ntsecapi.h; also needs `#define _NTDEF_`
'<ws2tcpip.h>', # iphlpapi.h
]
# LINT.ThenChange(/.clang-format:winheader)
# Ensure that headers are sorted as follows:
#
# 1. The primary header, if any, appears first.
# 2. All headers of the same type (e.g. C system, C++ system headers, et
# cetera) are grouped contiguously.
# 3. Any special sorting rules needed within each group for satisfying
# platform header idiosyncrasies. In practice, this only applies to C
# system headers.
# 4. The remaining headers without special sorting rules are sorted
# lexicographically.
#
# The for loop below that outputs the actual source text depends on #2 above
# to insert newlines between different groups of headers.
def SortKey(include):
def SpecialSortKey(include):
lower_name = include.decorated_name.lower()
for i in range(len(special_headers)):
if special_headers[i] == lower_name:
return i
return len(special_headers)
return (not include.is_primary_header, include.header_type,
SpecialSortKey(include), include.decorated_name)
includes.sort(key=SortKey)
# Assume there's always at least one include.
previous_include = None
for include in includes:
if previous_include and include.ShouldInsertNewline(previous_include):
source.append('')
source.extend(include.ToSource())
previous_include = include
return source
def AddHeaderToSource(filename, source, decorated_name, remove=False):
"""Adds or removes the specified header into/from the source text, if needed.
Args:
filename: The name of the source file.
source: A string containing the contents of the source file.
decorated_name: The decorated name of the header to add or remove.
remove: If true, remove instead of adding.
Returns:
None if no changes are needed or the modified source text otherwise.
"""
lines = source.splitlines()
begin, end = FindIncludes(lines)
# No #includes in this file. Just give up.
# TODO(dcheng): Be more clever and insert it after the file-level comment or
# include guard as appropriate.
if begin < 0:
print(f'Skipping {filename}: unable to find includes!')
return None
includes = ParseIncludes(lines[begin:end])
if not includes:
print(f'Skipping {filename}: unable to parse includes!')
return None
if remove:
for i in includes:
if decorated_name == i.decorated_name:
includes.remove(i)
break
else:
print(f'Skipping {filename}: unable to find {decorated_name}')
return None
else:
if decorated_name in [i.decorated_name for i in includes]:
# Nothing to do.
print(f'Skipping {filename}: no changes required!')
return None
else:
includes.append(Include(decorated_name, 'include', [], None))
MarkPrimaryInclude(includes, filename)
lines[begin:end] = SerializeIncludes(includes)
lines.append('') # To avoid eating the newline at the end of the file.
return '\n'.join(lines)
def main():
parser = argparse.ArgumentParser(
description='Mass add (or remove) a new header into a bunch of files.')
parser.add_argument(
'--header',
help='The decorated filename of the header to insert (e.g. "a" or <a>)',
required=True)
parser.add_argument('--remove',
help='Remove the header file instead of adding it',
action='store_true')
parser.add_argument('files', nargs='+')
args = parser.parse_args()
if ClassifyHeader(args.header) == _HEADER_TYPE_INVALID:
print('--header argument must be a decorated filename, e.g.')
print(' --header "<utility>"')
print('or')
print(' --header \'"moo.h"\'')
return 1
operation = 'Removing' if args.remove else 'Inserting'
print(f'{operation} #include {args.header}...')
for filename in args.files:
with open(filename, 'r') as f:
new_source = AddHeaderToSource(os.path.normpath(filename), f.read(),
args.header, args.remove)
if not new_source:
continue
with open(filename, 'w', newline='\n') as f:
f.write(new_source)
if __name__ == '__main__':
sys.exit(main())
阴茎进入阴道是什么感觉 西洋参有什么用 有黄痰吃什么药 戌是什么意思 为什么一吃饭就肚子疼
tap是什么意思 吃什么大便能特别通畅 头部检查挂什么科 着了过是什么词 什么时间吃苹果最好
人彘为什么还能活着 牟利什么意思 为什么偏偏喜欢你 avg是什么意思 小便浑浊是什么原因
难为情是什么意思 什么叫高危行为 馒头逼是什么意思 joma是什么牌子 当我谈跑步时我谈些什么
雫是什么意思hcv8jop9ns0r.cn 吃什么对肺好hcv8jop3ns1r.cn 什么发色显皮肤白hcv7jop5ns0r.cn 梦见火灾预示什么hcv8jop5ns7r.cn 生抽可以用什么代替hcv7jop9ns5r.cn
太阳线是什么意思hcv7jop5ns3r.cn 非洲是什么人种hcv9jop2ns2r.cn 6月25号是什么星座hcv9jop1ns1r.cn 片状低回声区什么意思hcv7jop7ns3r.cn 狸是什么动物hcv8jop6ns4r.cn
gr是什么单位hcv8jop8ns3r.cn 晚上睡觉多梦是什么原因hcv9jop1ns5r.cn 牛反刍是什么意思hcv7jop7ns0r.cn 癫痫是什么症状hcv9jop8ns2r.cn 芈月和嬴政什么关系hcv8jop5ns5r.cn
a4腰什么意思hcv8jop6ns5r.cn 为什么月经会推迟hcv8jop7ns1r.cn 体检应该挂什么科hcv9jop3ns8r.cn 晕车吃什么好hcv9jop1ns1r.cn 插班生是什么意思naasee.com
百度