2 # -*- coding: iso-8859-1 -*-
4 ################################################################################
6 # 2008 June 27th by Marcus 'EgS' Eggenberger <egs@quassel-irc.org> #
8 # The author disclaims copyright to this source code. #
9 # This Python Script is in the PUBLIC DOMAIN. #
11 ################################################################################
13 # ==============================
15 # ==============================
20 from subprocess import Popen, PIPE
22 # ==============================
24 # ==============================
25 QT_CONFIG = """[Paths]
29 QT_CONFIG_NOBUNDLE = """[Paths]
35 class InstallQt(object):
36 def __init__(self, appdir, bundle=True, requestedPlugins=[], skipInstallQtConf=False):
39 self.frameworkDir = self.appDir + "/Frameworks"
40 self.pluginDir = self.appDir + "/plugins"
41 self.executableDir = self.appDir
43 self.executableDir += "/MacOS"
45 self.installedFrameworks = set()
47 self.findFrameworkPath()
49 executables = [self.executableDir + "/" + executable for executable in os.listdir(self.executableDir)]
50 for executable in executables:
51 self.resolveDependancies(executable)
53 self.findPluginsPath()
54 self.installPlugins(requestedPlugins)
55 if not skipInstallQtConf:
58 def qtProperty(self, qtProperty):
60 Query persistent property of Qt via qmake
62 VALID_PROPERTIES = ['QT_INSTALL_PREFIX',
70 'QT_INSTALL_TRANSLATIONS',
71 'QT_INSTALL_CONFIGURATION',
72 'QT_INSTALL_EXAMPLES',
78 if qtProperty not in VALID_PROPERTIES:
81 qmakeProcess = Popen('qmake -query %s' % qtProperty, shell=True, stdout=PIPE, stderr=PIPE)
82 result = qmakeProcess.stdout.read().strip()
83 qmakeProcess.stdout.close()
87 def findFrameworkPath(self):
88 self.sourceFrameworkPath = self.qtProperty('QT_INSTALL_LIBS')
90 def findPluginsPath(self):
91 self.sourcePluginsPath = self.qtProperty('QT_INSTALL_PLUGINS')
93 def findPlugin(self, pluginname):
94 qmakeProcess = Popen('find %s -name %s' % (self.sourcePluginsPath, pluginname), shell=True, stdout=PIPE, stderr=PIPE)
95 result = qmakeProcess.stdout.read().strip()
96 qmakeProcess.stdout.close()
102 def installPlugins(self, requestedPlugins):
104 os.mkdir(self.pluginDir)
108 for plugin in requestedPlugins:
109 if not plugin.isalnum():
110 print "Skipping library '%s'..." % plugin
113 pluginName = "lib%s.dylib" % plugin
116 pluginSource = self.findPlugin(pluginName)
118 print "WARNING: Requested library does not exist: '%s'" % plugin
121 pluginSubDir = os.path.dirname(pluginSource)
122 pluginSubDir = pluginSubDir.replace(self.sourcePluginsPath, '').strip('/')
124 os.mkdir("%s/%s" % (self.pluginDir, pluginSubDir))
128 os.system('cp "%s" "%s/%s"' % (pluginSource, self.pluginDir, pluginSubDir))
130 self.resolveDependancies("%s/%s/%s" % (self.pluginDir, pluginSubDir, pluginName))
132 def installQtConf(self):
133 qtConfName = self.appDir + "/qt.conf"
134 qtConfContent = QT_CONFIG_NOBUNDLE
136 qtConfContent = QT_CONFIG
137 qtConfName = self.appDir + "/Resources/qt.conf"
139 qtConf = open(qtConfName, 'w')
140 qtConf.write(qtConfContent)
143 def resolveDependancies(self, obj):
144 # obj must be either an application binary or a framework library
145 # print "resolving deps for:", obj
146 for framework, lib in self.determineDependancies(obj):
147 self.installFramework(framework)
148 self.changeDylPath(obj, framework, lib)
150 def installFramework(self, framework):
151 # skip if framework is already installed.
152 if framework in self.installedFrameworks:
155 self.installedFrameworks.add(framework)
157 # if the Framework-Folder is a Symlink we are in a Helper-Process ".app" (e.g. in QtWebEngine),
158 # in this case skip copying/installing on existing folders
159 skipExisting = False;
160 if os.path.islink(self.frameworkDir):
163 # ensure that the framework directory exists
165 os.mkdir(self.frameworkDir)
169 if not framework.startswith('/'):
170 framework = "%s/%s" % (self.sourceFrameworkPath, framework)
172 frameworkname = framework.split('/')[-1]
173 localframework = self.frameworkDir + "/" + frameworkname
175 # Framework already installed in previous run ... see above
176 if skipExisting and os.path.isdir(localframework):
180 os.system('cp -R "%s" "%s"' % (framework, self.frameworkDir))
183 os.system('find "%s" -name *debug* -exec rm -f {} \;' % localframework)
184 os.system('find "%s" -name Headers -exec rm -rf {} \; 2>/dev/null' % localframework)
186 # Install new Lib ID and Change Path to Frameworks for the Dynamic linker
187 for lib in os.listdir(localframework + "/Versions/Current"):
188 lib = "%s/Versions/Current/%s" % (localframework, lib)
189 otoolProcess = Popen('otool -D "%s"' % lib, shell=True, stdout=PIPE, stderr=PIPE)
191 libname = [line for line in otoolProcess.stdout][1].strip()
194 otoolProcess.stdout.close()
195 if otoolProcess.wait() == 1: # we found some Resource dir or similar -> skip
197 frameworkpath, libpath = libname.split(frameworkname)
199 newlibname = "@executable_path/../%s%s" % (frameworkname, libpath)
201 newlibname = "@executable_path/%s%s" % (frameworkname, libpath)
202 # print 'install_name_tool -id "%s" "%s"' % (newlibname, lib)
203 os.system('chmod +w "%s"' % (lib))
204 os.system('install_name_tool -id "%s" "%s"' % (newlibname, lib))
206 self.resolveDependancies(lib)
208 def determineDependancies(self, app):
209 otoolPipe = Popen('otool -L "%s"' % app, shell=True, stdout=PIPE).stdout
210 otoolOutput = [line for line in otoolPipe]
212 libs = [line.split()[0] for line in otoolOutput[1:] if ("Qt" in line or "phonon" in line) and "@executable_path" not in line]
213 frameworks = [lib[:lib.find(".framework") + len(".framework")] for lib in libs]
214 frameworks = [framework[framework.rfind('/') + 1:] for framework in frameworks]
215 return zip(frameworks, libs)
217 def changeDylPath(self, obj, framework, lib):
218 newlibname = framework + lib.split(framework)[1]
220 newlibname = "@executable_path/../Frameworks/%s" % newlibname
222 newlibname = "@executable_path/Frameworks/%s" % newlibname
224 # print 'install_name_tool -change "%s" "%s" "%s"' % (lib, newlibname, obj)
225 os.system('chmod +w "%s"' % (lib))
226 os.system('chmod +w "%s"' % (obj))
227 os.system('install_name_tool -change "%s" "%s" "%s"' % (lib, newlibname, obj))
229 if __name__ == "__main__":
230 if len(sys.argv) < 2:
231 print "Wrong Argument Count (Syntax: %s [--nobundle] [--plugins=plugin1,plugin2,...] $TARGET_APP)" % sys.argv[0]
238 while offset < len(sys.argv) and sys.argv[offset].startswith("--"):
239 if sys.argv[offset] == "--nobundle":
242 if sys.argv[offset].startswith("--plugins="):
243 plugins = sys.argv[offset].split('=')[1].split(',')
247 targetDir = sys.argv[offset]
249 targetDir += "/Contents"
251 InstallQt(targetDir, bundle, plugins)
254 webenginetarget = targetDir + '/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents'
256 if os.path.isdir(webenginetarget):
257 os.system('ln -s ../../../../../../ "%s"/Frameworks' % webenginetarget)
258 InstallQt(webenginetarget, bundle, [], True)