-
Notifications
You must be signed in to change notification settings - Fork 0
/
search_unused_classes.py
182 lines (172 loc) · 5.4 KB
/
search_unused_classes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#! /usr/bin/python
# -*- coding:utf-8 -*-
import os
import time
import glob
import re
from sys import argv
import hh_print
from optparse import OptionParser
import linecache
def class_name_from_implemetation_string(string):
if not string:
return None
start_flag = "@implementation"
end_flags = ["{", "("]
start = string.find(start_flag)
if start == -1:
return None
start = start + len(start_flag)
for flag in end_flags:
end = string.find(flag, start)
if end != -1:
break
if end == -1:
end = len(string)
result = string[start:end].strip()
return result
def class_name_from_interface_string(string):
if not string:
return None
start_flag = "@interface"
end_flags = [":", "("]
start = string.find(start_flag)
if start == -1:
return None
start = start + len(start_flag)
for flag in end_flags:
end = string.find(flag, start)
if end != -1:
break
if end == -1:
return None
result = string[start:end].strip()
return result
def path_from_interface_string(string):
if not string:
return None
end = string.find(":")
if end == -1:
return None
result = string[0:end].strip()
result = result.split('/')[-1]
return result
def find_all_classes(path):
command = 'ag --objc " *@interface +\w* *:" %s' % (path)
search_results = os.popen(command).readlines()
classes_set = set()
classes_info = []
duplicate_files = []
for line in search_results:
clean_line = line.strip()
interface = class_name_from_interface_string(clean_line)
# print clean_line
# print interface
if interface and interface not in classes_set:
classes_set.add(interface)
classes_info.append([interface, path_from_interface_string(clean_line)])
else:
duplicate_files.append(interface)
if len(duplicate_files) > 0:
hh_print.print_array(duplicate_files, "可能存在重复定义或者在文件夹内但没有拖进工程的文件", "b_red")
return classes_info
def class_name_of_the_line(file_path, line_number):
lines = linecache.getlines(file_path)
count = len(lines)
class_name = None
is_in_note_block = False
is_in_double_note = False
for x in range(line_number,0,-1):
# print ("line:" + str(x))
single_line = lines[x].strip()
# hh_print.print_color_string("single_line :" + single_line)
if len(single_line) == 0:
continue
if single_line.endswith('*/'):
if not single_line.startswith('/*'):
is_in_note_block = True
else:
continue
if single_line.startswith('/*'):
if is_in_note_block:
is_in_note_block = False
continue
else:
# indicate the line is in note block
is_in_double_note = True
break
if single_line.startswith('/') or single_line.startswith('#pragma'):
continue
if cmp(single_line, "@end") == 0:
break
if single_line.startswith('@interface'):
class_name = class_name_from_interface_string(single_line)
elif single_line.startswith('@implementation'):
class_name = class_name_from_implemetation_string(single_line)
# print ("class_name from singleline: %s" % class_name)
if class_name != None:
break
return [is_in_double_note, class_name]
def is_used_class(class_info, path):
class_name = class_info[0]
class_path = class_info[1]
command = 'ag --objc -w %s %s' % (class_name, path)
search_results = os.popen(command).readlines()
for one_result in search_results:
clean_one = one_result.strip()
params = clean_one.split(':')
if len(params) < 3:
continue
file_path = params[0]
line_number = params[1]
code = ':'.join(params[2:]).strip()
# hh_print.print_color_string("file_path: " + file_path)
# hh_print.print_color_string("line_number: " + line_number)
# hh_print.print_color_string("code: " + code)
interface_string = '@interface ' + class_name
prefixs = ['/', '#import', interface_string, '@implementation', '#pragma']
is_hit_prefix = False
for prefix in prefixs:
if code.startswith(prefix):
is_hit_prefix = True
break
if is_hit_prefix:
continue
name_result = class_name_of_the_line(file_path, int(line_number) - 1)
if name_result[0]:
# hh_print.print_color_string("class in note: %s" % (class_name), "b_red")
continue
class_name_for_current_line = name_result[1]
# hh_print.print_color_string("class_name_for_current_line : %s" % class_name_for_current_line)
if not class_name_for_current_line or cmp(class_name_for_current_line, class_name) != 0:
# hh_print.print_color_string("class not match: %s, %s" % (class_name, class_name_for_current_line), "b_red")
return True
# hh_print.print_color_string("False", "b_red")
return False
if __name__ == '__main__':
if len(argv) < 2:
hh_print.print_color_string("Parameters error: Usage: python %s source_path search_path" % (__file__), "b_red")
exit(0)
source_path = argv[1]
if len(argv) > 2:
search_path = argv[2]
else:
search_path = source_path
classes = find_all_classes(source_path)
if len(classes) > 0:
hh_print.print_array(classes, "所有类")
else:
hh_print.print_color_string("没搜索到类的定义,请检查", "b_red")
unused_classes = []
index = 1
print ""
for class_ext in classes:
result = is_used_class(class_ext, search_path)
if not result:
unused_classes.append(class_ext)
hh_print.print_progress(index, len(classes))
index = index + 1
if len(unused_classes) > 0:
hh_print.print_array(unused_classes, "所有很可能没有被使用的类")
else:
hh_print.print_color_string("没有未被使用的类,666~~~", "yellow")