/
macosx-sanity-check.py
executable file
·130 lines (118 loc) · 4.45 KB
/
macosx-sanity-check.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
#!/usr/bin/env python
#
# This is be used to verify that all the dependant libraries of a Mac OS X executable
# are present and that they are backwards compatible with at least 10.5.
# Run with an executable as parameter
# Will return 0 if the executable an all libraries are OK
# Returns != 0 and prints some textural description on error
#
# Author: Marius Kintel <marius@kintel.net>
#
# This script lives here:
# https://github.com/kintel/MacOSX-tools
#
import sys
import os
import subprocess
import re
DEBUG = False
def usage():
print >> sys.stderr, "Usage: " + sys.argv[0] + " <executable>"
sys.exit(1)
# Try to find the given library by searching in the typical locations
# Returns the full path to the library or None if the library is not found.
def lookup_library(file):
found = None
if not re.match("/", file):
if re.search("@executable_path", file):
abs = re.sub("^@executable_path", executable_path, file)
if os.path.exists(abs): found = abs
if DEBUG: print "Lib in @executable_path found: " + found
elif re.search("\.app/", file):
found = file
if DEBUG: print "App found: " + found
elif re.search("\.framework/", file):
found = os.path.join("/Library/Frameworks", file)
if DEBUG: print "Framework found: " + found
else:
for path in os.getenv("DYLD_LIBRARY_PATH").split(':'):
abs = os.path.join(path, file)
if os.path.exists(abs): found = abs
if DEBUG: print "Library found: " + found
else:
found = file
return found
# Returns a list of dependent libraries, excluding system libs
def find_dependencies(file):
libs = []
args = ["otool", "-L", file]
if DEBUG: print "Executing " + " ".join(args)
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output,err = p.communicate()
if p.returncode != 0:
print "Failed with return code " + str(p.returncode) + ":"
print err
return None
deps = output.split('\n')
for dep in deps:
# print dep
dep = re.sub(".*:$", "", dep) # Take away header line
dep = re.sub("^\t", "", dep) # Remove initial tabs
dep = re.sub(" \(.*\)$", "", dep) # Remove trailing parentheses
if len(dep) > 0 and not re.search("/System/Library", dep) and not re.search("/usr/lib", dep):
libs.append(dep)
return libs
def validate_lib(lib):
p = subprocess.Popen(["otool", "-l", lib], stdout=subprocess.PIPE)
output = p.communicate()[0]
if p.returncode != 0: return False
if re.search("LC_DYLD_INFO_ONLY", output):
print "Error: Requires Snow Leopard: " + lib
return False
p = subprocess.Popen(["lipo", lib, "-verify_arch", "x86_64"], stdout=subprocess.PIPE)
output = p.communicate()[0]
if p.returncode != 0:
print "Error: x86_64 architecture not supported: " + lib
return False
p = subprocess.Popen(["lipo", lib, "-verify_arch", "i386"], stdout=subprocess.PIPE)
output = p.communicate()[0]
if p.returncode != 0:
print "Error: i386 architecture not supported: " + lib
return False
return True
if __name__ == '__main__':
error = False
if len(sys.argv) != 2: usage()
executable = sys.argv[1]
if DEBUG: print "Processing " + executable
executable_path = os.path.dirname(executable)
# processed is a dict {libname : [parents]} - each parent is dependant on libname
processed = {}
pending = [executable]
processed[executable] = []
while len(pending) > 0:
dep = pending.pop()
if DEBUG: print "Evaluating " + dep
deps = find_dependencies(dep)
assert(deps)
for d in deps:
absfile = lookup_library(d)
if absfile == None:
print "Not found: " + d
print " ..required by " + str(processed[dep])
error = True
continue
if absfile in processed:
processed[absfile].append(dep)
else:
processed[absfile] = [dep]
if DEBUG: print "Pending: " + absfile
pending.append(absfile)
for dep in processed:
if DEBUG: print "Validating: " + dep
# print " " + str(processed[dep])
if not validate_lib(dep):
print "..required by " + str(processed[dep])
error = True
if error: sys.exit(1)
else: sys.exit(0)