This repository has been archived by the owner on Dec 24, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
resman
277 lines (224 loc) · 11 KB
/
resman
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
#!/usr/bin/env python
# encoding: utf-8
"""
resman
Help manage unused resources in Xcode projects
Version 1.0
Created by Sumeru Chatterjee
"""
from optparse import OptionParser
import pdb
import sys
import os
from subprocess import call
from subprocess import Popen
from subprocess import PIPE
import shutil
from Pbxproj import Pbxproj
from Pbxproj import relpath
def main():
usage = '''%prog -p <Project Directory> -d <Resource Directory> -x <xcode-project> <options>
Resource Cleaner.
Helps Clean unused resources (png/jpg/gif/tiff) in Xcode projects.
The source files that are searched are (*.h,*.m,*.xib,*.nib)'''
parser = OptionParser(usage = usage)
parser.add_option("-x", "--xcode-project", dest="xcodeproject",
help="Search for resource usage from all source code files in this project",
action="append")
parser.add_option("-p", "--projectdir", dest="project_dir",
help="Search for resource usage from source code files in in this directory",
action="append")
parser.add_option("-d", "--resourcedir", dest="resource_dir",
help="Get the list of resources from this directory",
action="append")
parser.add_option("-n", "--no-recursive", dest="norecurse",
help="Get the resources from the first subdirectory in RESOURCE_DIR only (if the RESOURCE_DIR option is exercised)",
action="store_true")
parser.add_option("-v", "--verbose", dest="verbose",
help="Display verbose output",
action="store_true")
parser.add_option("-m","--move", dest ="move",
help="Move all invalid resources to invalid resources folder",
action="store_true")
(options, args) = parser.parse_args()
if not((options.__dict__["xcodeproject"]) or (options.__dict__["project_dir"])):
print "\nError:Source List not found"
print "Must specify either xcodeproject or project_dir for getting list of source files"
parser.print_help()
exit(0)
xcodeproject = None
if options.__dict__["xcodeproject"]:
xcodeprojectfile = os.path.abspath(options.__dict__["xcodeproject"][0])
xcodeproject = Pbxproj.get_pbxproj_by_name(xcodeprojectfile)
resource_dir = None
if options.__dict__["resource_dir"]:
resource_dir = os.path.abspath(options.__dict__["resource_dir"][0])
project_dir = None
if options.__dict__["project_dir"]:
project_dir = os.path.abspath(options.__dict__["project_dir"][0])
verbose = options.__dict__['verbose']
norecurse = options.__dict__['norecurse']
move = options.__dict__['move']
if resource_dir:
resourcelist = get_resource_files(resource_dir,norecurse)
if not resourcelist:
print "\n\nERROR:No resource files found in directory %(dir)s\n\n"%{'dir':resource_dir}
exit(0)
print "\nCalculating Sizes ..\n\n"
totalsize = 0
sizedictionary = {}
for file in resourcelist:
sizedictionary[file] = os.path.getsize(file)
totalsize += sizedictionary[file]
# if the user has given an Xcodeproject file then find resources used by the project
if xcodeproject:
projectresourcefiles = xcodeproject.get_built_resources()
filtered_resourcelist = []
for resourcefile in resourcelist:
filename = os.path.basename(resourcefile)
found = False
for resource in projectresourcefiles:
if filename == os.path.basename(resource):
found = True
break
if found:
filtered_resourcelist.append(resourcefile)
filtered_totalsize=0
for file in filtered_resourcelist:
sizedictionary[file] = os.path.getsize(file)
filtered_totalsize += sizedictionary[file]
total_resourcelist = resourcelist
resourcelist = filtered_resourcelist
if not resourcelist:
print "\n\nERROR:No resource files found in directory %(dir)s that are used in project %(proj)s\n\n"%{'dir':resource_dir,'proj':os.path.basename(xcodeprojectfile)}
exit(0)
else:
print "\nError:RESOURCE DIRECTORY NOT SPECIFIED"
parser.print_help()
exit(0)
print "%(num)s resource files found in resource directory %(dir)s\n\n"%{'num':len(resourcelist),'dir':resource_dir}
if verbose:
print "Resource Files"
print "-----------------\n"
print "\n".join(os.path.basename(file)+" Size:"+convert_bytes(sizedictionary[file]) for file in resourcelist)
print "\n\n"
sourcelist = get_project_files_from_dir(project_dir)
if sourcelist:
print "%(num)s source or ib files found in project directory %(dir)s\n\n"%{'num':len(sourcelist),'dir':project_dir}
if verbose:
print "Source Files"
print "-----------------\n"
print "\n".join(os.path.basename(file) for file in sourcelist)
print "\n\n"
else:
print "\n\nERROR:No source(*.h/*.m/) or ib files(*.xib/*.nib) found in directory %(dir)s..Aborting\n\n"%{'dir':project_dir}
exit(0)
print "Now Validating each resource file..\n\n"
sourcelist_file = "./filelist.temp"
outfile = open(sourcelist_file,"w")
for sourcefile in sourcelist:
escapedsourcefile = sourcefile.replace(" ","\ ")
outfile.write(escapedsourcefile+"\n")
outfile.close()
invalid_resource_list = []
invalidsize = 0
for resource_file in resourcelist:
valid = validate(resource_file,sourcelist_file,verbose)
if not valid:
print "Resource %(file)s Size:%(size)s - Invalid"%{'file':os.path.basename(resource_file),'size':convert_bytes(sizedictionary[resource_file])}
invalid_resource_list.append(resource_file)
invalidsize += os.path.getsize(resource_file)
else:
if verbose:
"Resource %(file)s Size:%(size)s - Invalid"%{'file':os.path.basename(resource_file),'size':convert_bytes(sizedictionary[resource_file])}
os.remove(sourcelist_file)
print "\n\nSummary\n--------------"
if not xcodeproject:
print "Total Number of Resources :%d"%len(resourcelist)
print "Total Size of Resources :%s"%convert_bytes(totalsize)
print "Total Number of Invalid Resources :%d"%len(invalid_resource_list)
print "Total Size of Invalid Resources :%s"%convert_bytes(invalidsize)
else:
print "Total Number of Resources :%d"%len(total_resourcelist)
print "Total Size of Resources :%s"%convert_bytes(totalsize)
print "Total Number of Resources used in project :%d"%len(resourcelist)
print "Total Size of Resources used in project :%s"%convert_bytes(filtered_totalsize)
print "Total Number of Unused Resources used in project:%d"%len(invalid_resource_list)
print "Total Size of Unused Resources used in project:%s"%convert_bytes(invalidsize)
if move and invalid_resource_list:
invalid_dir = resource_dir+"/"+"unused"
if not os.path.exists(invalid_dir):
os.makedirs(invalid_dir)
for resource_file in invalid_resource_list:
src = resource_file
dst = os.path.join(invalid_dir,relpath(resource_dir,src))
shutil.move(src,dst)
print "\n\n"
def validate(resource_file,sourcelist_file,verbose):
if os.path.exists(resource_file):
resourcefilename = os.path.basename(resource_file)
filename = resourcefilename.split('.')[0]
ext = "".join(resourcefilename.split('.')[1:])
filename = filename.replace("@2x","")
regex = "(<string.*?>|\"|\"bundle://)%(name)s(.%(ext)s)?(</string>|\")"%{'name':filename,'ext':ext}
if verbose:
print "\n\nValdating File:%(file)s,Search Regex:%(regex)s"%{'file':resourcefilename,'regex':regex}
p1 = Popen(["cat","filelist.temp"],stdout=PIPE)
p2 = Popen(["xargs","egrep",regex],stdin=p1.stdout,stdout=PIPE)
p1.stdout.close()
grep_output = p2.communicate()[0]
p1.wait()
if not grep_output:
return False
else:
if verbose:
print "Matches :\n"+grep_output
return True
else:
print "\n\nERROR:Resource File %s does not exist..Aborting\n\n"%resource_file
exit(0)
def get_resource_files(resource_dir,norecurse=False):
filelist = []
if os.path.exists(resource_dir):
if not norecurse:
p1 = Popen(["find","-E",resource_dir,"-type","f","-regex","(.*png|.*jpg|.*gif|.*tiff)"], stdout=PIPE)
else:
p1 = Popen(["find","-E",resource_dir,"-type","f","-regex","(.*png|.*jpg|.*gif|.*tiff)","-maxdepth","1"], stdout=PIPE)
for file in p1.stdout:
if not "DS_Store" in file:
filelist.append(file[:-1])
p1.stdout.close()
return filelist
else:
print "\n\nERROR:Directory %s does not exist..Aborting\n\n"%resource_dir
exit(0)
def get_project_files_from_dir(project_dir):
filelist = []
if os.path.exists(project_dir):
p1 = Popen(["find","-E",project_dir,"-type","f","-regex","(.*h|.*m|.*xib|.*nib)"], stdout=PIPE)
for file in p1.stdout:
filelist.append(file[:-1])
p1.stdout.close()
return filelist
else:
print "\n\nERROR:Directory %s does not exist..Aborting\n\n"%project_dir
exit(0)
def convert_bytes(bytes):
bytes = float(bytes)
if bytes >= 1099511627776:
terabytes = bytes / 1099511627776
size = '%.2fT' % terabytes
elif bytes >= 1073741824:
gigabytes = bytes / 1073741824
size = '%.2fG' % gigabytes
elif bytes >= 1048576:
megabytes = bytes / 1048576
size = '%.2fM' % megabytes
elif bytes >= 1024:
kilobytes = bytes / 1024
size = '%.2fK' % kilobytes
else:
size = '%.2fb' % bytes
return size
if __name__ == "__main__":
sys.exit(main())