Updated packaging scripts for Mac OS X
[quassel.git] / scripts / build / macosx_DeployApp.py
1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3
4 ################################################################################
5 #                                                                              #
6 # 2008 June 27th by Marcus 'EgS' Eggenberger <egs@quassel-irc.org>             #
7 #                                                                              #
8 # The author disclaims copyright to this source code.                          #
9 # This Python Script is in the PUBLIC DOMAIN.                                  #
10 #                                                                              #
11 ################################################################################
12
13 # ==============================
14 #  Imports
15 # ==============================
16 import sys
17 import os
18
19 from subprocess import Popen, PIPE
20
21 class InstallQt(object):
22     def __init__(self, appdir, bundle = True):
23         self.appDir = appdir
24         self.bundle = bundle
25         self.executableDir = self.appDir
26         if bundle:
27             self.executableDir += "/MacOS"
28
29         if bundle:
30             self.frameworkDir = self.appDir + "/Frameworks"
31         else:
32             self.frameworkDir = self.executableDir + "/Frameworks"
33
34         self.needFrameworks = []
35
36         self.findFrameworkPath()
37
38         executables = [self.executableDir + "/" + executable for executable in os.listdir(self.executableDir)]
39
40         for executable in executables:
41             for framework,lib in self.determineDependancies(executable):
42                 if framework not in self.needFrameworks:
43                     self.needFrameworks.append(framework)
44                     self.installFramework(framework)
45             self.changeDylPath(executable)
46
47     def findFrameworkPath(self):
48         otoolProcess = Popen('qmake -query QT_INSTALL_LIBS', shell=True, stdout=PIPE, stderr=PIPE)
49         self.sourceFrameworkPath = otoolProcess.stdout.read().strip()
50         otoolProcess.stdout.close()
51         otoolProcess.wait()
52
53
54     def installFramework(self, framework):
55         try:
56             os.mkdir(self.frameworkDir)
57         except:
58             pass
59
60         if not framework.startswith('/'):
61             framework = "%s/%s" % (self.sourceFrameworkPath, framework)
62
63         # Copy Framework
64         os.system('cp -R "%s" "%s"' % (framework, self.frameworkDir))
65
66         frameworkname = framework.split('/')[-1]
67         localframework = self.frameworkDir + "/" + frameworkname
68
69         # De-Myllify
70         os.system('find "%s" -name *debug* -exec rm -f {} \;' % localframework)
71         os.system('find "%s" -name Headers -exec rm -rf {} \; 2>/dev/null' % localframework)
72
73         # Install new Lib ID and Change Path to Frameworks for the Dynamic linker
74         for lib in os.listdir(localframework + "/Versions/Current"):
75             lib = "%s/Versions/Current/%s" % (localframework, lib)
76             otoolProcess = Popen('otool -D "%s"' % lib, shell=True, stdout=PIPE, stderr=PIPE)
77             try:
78                 libname = [line for line in otoolProcess.stdout][1].strip()
79             except:
80                 libname = ''
81             otoolProcess.stdout.close()
82             if otoolProcess.wait() == 1: # we found some Resource dir or similar -> skip
83                 continue
84             frameworkpath, libpath = libname.split(frameworkname)
85             if self.bundle:
86                 newlibname = "@executable_path/../%s%s" % (frameworkname, libpath)
87             else:
88                 newlibname = "@executable_path/%s%s" % (frameworkname, libpath)
89             #print 'install_name_tool -id "%s" "%s"' % (newlibname, lib)
90             os.system('install_name_tool -id "%s" "%s"' % (newlibname, lib))
91             self.changeDylPath(lib)
92             
93     def determineDependancies(self, app):
94         otoolPipe = Popen('otool -L "%s"' % app, shell=True, stdout=PIPE).stdout
95         otoolOutput = [line for line in otoolPipe]
96         otoolPipe.close()
97         libs = [line.split()[0] for line in otoolOutput[1:] if "Qt" in line and not "@executable_path" in line]
98         frameworks = [lib[:lib.find(".framework")+len(".framework")] for lib in libs]
99         return zip(frameworks, libs)
100
101     def changeDylPath(self, obj):
102         for framework, lib in self.determineDependancies(obj):
103             frameworkname = framework.split('/')[-1]
104             frameworkpath, libpath = lib.split(frameworkname)
105             if self.bundle:
106                 newlibname = "@executable_path/../Frameworks/%s%s" % (frameworkname, libpath)
107             else:
108                 newlibname = "@executable_path/Frameworks/%s%s" % (frameworkname, libpath)
109
110             #print 'install_name_tool -change "%s" "%s" "%s"' % (lib, newlibname, obj)
111             os.system('install_name_tool -change "%s" "%s" "%s"' % (lib, newlibname, obj))
112
113 if __name__ == "__main__":
114     if len(sys.argv) < 2:
115         print "Wrong Argument Count (Syntax: %s [--nobundle] $TARGET_APP)" % sys.argv[0]
116         sys.exit(1)
117     else:
118         bundle = True
119         offset = 1
120
121         if sys.argv[1].startswith("--"):
122             offset = 2
123             if sys.argv[1] == "--nobundle":
124                 bundle = False
125
126         targetDir = sys.argv[offset]
127         if bundle:
128             targetDir += "/Contents"
129
130         InstallQt(targetDir, bundle)
131     
132