-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathuniversal_slim
executable file
·141 lines (127 loc) · 4.29 KB
/
universal_slim
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
#!/usr/bin/env python
# A dummy script to make universal libraries slimmer, thinner, better :)
# Baris Metin <baris _at_ metin.org>
import sys
import os
import time
import tempfile
import subprocess
import platform
import optparse
import stat
MACHINE_ARCH = platform.machine()
def run(cmd):
p = subprocess.Popen(cmd, shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True)
p.wait()
return p.returncode, p.stdout.readlines()
def get_all_applications_dirs():
return [x.strip() for x in os.popen('mdfind "kMDItemContentTypeTree == \\\"com.apple.application\\\""').readlines()]
def get_size_info(binary):
architectures = {}
cur_arch = ""
ret, lines = run("lipo -detailed_info %s" % binary)
assert(ret == 0)
for line in lines:
line = line.strip()
if line.startswith("architecture"):
cur_arch = line.split()[1]
elif line.startswith("size"):
architectures[cur_arch] = int(line.split()[1])
return architectures
def check_universal_file(f, options):
if not os.path.isfile(f):
return None
mode = os.stat(f)[0]
if options.force:
pass
elif mode & stat.S_IXOTH or mode & stat.S_IXGRP or mode & stat.S_IXUSR:
pass
elif f.endswith(".dynlib"):
pass
else:
return None
if run("file \"%s\"" % f)[1][0].find("Mach-O") >= 0:
try:
archs = get_size_info(f)
if len(archs) > 1:
return {f: archs}
except AssertionError:
pass
return None
def walk_directory(d, options):
all = {}
for root,dirs,files in os.walk(d):
for f in files:
info = check_universal_file(os.path.join(root,f), options)
if info:
all.update(info)
return all
def report(all):
space_saved = 0
for k in all.iterkeys():
for arch,size in all[k].items():
if arch != MACHINE_ARCH:
space_saved += size
if space_saved:
print "Found %.2f M junk in %d binaries!" % ((space_saved / 1024.0 / 1024.0), len(all))
else:
print "Looks clean..."
return space_saved
def thin(all):
print "Operation 'tiny bunny' started on %s binaries. Be patient..." % len(all)
for k in all.iterkeys():
mode = os.stat(k)[stat.ST_MODE]
f, path = tempfile.mkstemp('slimer_binary')
os.close(f)
ret, out = run("lipo -thin %s %s -output %s" % (MACHINE_ARCH, k, path))
if not ret:
os.rename(path, k)
os.chmod(k, mode)
def do_all_apps(options, args):
all = {}
all_saved = 0
for i in get_all_applications_dirs():
print i
new = walk_directory(i, options)
saved_space = report(new)
if saved_space:
all.update(new)
all_saved += saved_space
print
print "TOTAL: Found %.2f M junk in %d binaries!" % ((all_saved / 1024.0 / 1024.0), len(all))
if not options.report_only and len(all):
thin(all)
def do_list(options, args):
all = {}
for a in args:
if os.path.isdir(a):
all.update(walk_directory(a, options))
else:
info = check_universal_file(a, options)
if info:
all.update(info)
report(all)
if not options.report_only and len(all):
thin(all)
if __name__ == "__main__":
usage = "Usage: %prog [options] [args]\nMakes Universal binaries thinner..."
parser = optparse.OptionParser(usage=usage)
parser.add_option("-A", "--all_apps", action="store_true",
help="Walk through all Applications indexed by Spotlight")
parser.add_option("-R", "--report_only", action="store_true",
help="Only print the report, don't modify the binaries.")
parser.add_option("-F", "--force", action="store_true",
help="Force to run check on all files (not only on binaries & dynlibs)")
(options, args) = parser.parse_args()
if options.all_apps:
do_all_apps(options, args)
elif len(args):
do_list(options, args)
elif options.report_only:
print "Walking through all Applications..."
do_all_apps(options, args)
else:
parser.print_help()