| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- #!/usr/bin/python
- # This file is part of ownCloud.
- # It was inspired in large part by the macdeploy script in Clementine
- # and Tomahawk
- #
- # ownCloud is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- #
- # ownCLoud is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with ownCloud. If not, see <http://www.gnu.org/licenses/>.
- import os
- import re
- import subprocess
- import commands
- import sys
- from glob import glob
- def QueryQMake(attrib):
- return subprocess.check_output([qmake_path, '-query', attrib]).rstrip('\n')
- FRAMEWORK_SEARCH_PATH=[
- '/Library/Frameworks',
- os.path.join(os.environ['HOME'], 'Library/Frameworks')
- ]
- LIBRARY_SEARCH_PATH=['/usr/local/lib', '.']
- QT_PLUGINS = [
- 'sqldrivers/libqsqlite.dylib',
- 'platforms/libqcocoa.dylib',
- 'imageformats/libqgif.dylib',
- 'imageformats/libqico.dylib',
- 'imageformats/libqjpeg.dylib',
- 'imageformats/libqsvg.dylib',
- ]
- QT_PLUGINS_SEARCH_PATH=[
- # os.path.join(os.environ['QTDIR'], 'plugins'),
- '/usr/local/Cellar/qt/5.2.1/plugins',
- ]
- class Error(Exception):
- pass
- class CouldNotFindQtPluginErrorFindFrameworkError(Error):
- pass
- class InstallNameToolError(Error):
- pass
- class CouldNotFindQtPluginError(Error):
- pass
- class CouldNotFindScriptPluginError(Error):
- pass
- class CouldNotFindFrameworkError(Error):
- pass
- if len(sys.argv) < 3:
- print 'Usage: %s <bundle.app> <path-to-qmake>' % sys.argv[0]
- exit()
- def is_exe(fpath):
- return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
- bundle_dir = sys.argv[1]
- qmake_path = sys.argv[2]
- bundle_name = os.path.basename(bundle_dir).split('.')[0]
- commands = []
- binary_dir = os.path.join(bundle_dir, 'Contents', 'MacOS')
- frameworks_dir = os.path.join(bundle_dir, 'Contents', 'Frameworks')
- commands.append(['mkdir', '-p', frameworks_dir])
- resources_dir = os.path.join(bundle_dir, 'Contents', 'Resources')
- commands.append(['mkdir', '-p', resources_dir])
- plugins_dir = os.path.join(bundle_dir, 'Contents', 'PlugIns')
- binaries = [i for i in glob(os.path.join(bundle_dir, 'Contents', 'MacOS', "*")) if is_exe(i)];
- fixed_libraries = []
- fixed_frameworks = []
- def WriteQtConf():
- print "Writing qt.conf..."
- with open(os.path.join(resources_dir, 'qt.conf'), 'w') as f:
- f.write("[Paths]\nPlugins = PlugIns\n");
- f.close()
- def GetBrokenLibraries(binary):
- #print "Checking libs for binary: %s" % binary
- output = subprocess.Popen(['otool', '-L', binary], stdout=subprocess.PIPE).communicate()[0]
- broken_libs = {
- 'frameworks': [],
- 'libs': []}
- for line in [x.split(' ')[0].lstrip() for x in output.split('\n')[1:]]:
- #print "Checking line: %s" % line
- if not line: # skip empty lines
- continue
- if os.path.basename(binary) == os.path.basename(line):
- #print "mnope %s-%s" % (os.path.basename(binary), os.path.basename(line))
- continue
- if re.match(r'^\s*/System/', line):
- continue # System framework
- elif re.match(r'^\s*/usr/lib/', line):
- #print "unix style system lib"
- continue # unix style system library
- elif re.match(r'Breakpad', line):
- continue # Manually added by cmake.
- elif re.match(r'^\s*@executable_path', line) or re.match(r'^\s*@loader_path', line):
- # Potentially already fixed library
- if '.framework' in line:
- relative_path = os.path.join(*line.split('/')[3:])
- if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
- broken_libs['frameworks'].append(relative_path)
- else:
- relative_path = os.path.join(*line.split('/')[1:])
- #print "RELPATH %s %s" % (relative_path, os.path.join(binary_dir, relative_path))
- if not os.path.exists(os.path.join(binary_dir, relative_path)):
- broken_libs['libs'].append(relative_path)
- elif re.search(r'\w+\.framework', line):
- broken_libs['frameworks'].append(line)
- else:
- broken_libs['libs'].append(line)
- return broken_libs
- def FindFramework(path):
- search_pathes = FRAMEWORK_SEARCH_PATH
- search_pathes.insert(0, QueryQMake('QT_INSTALL_LIBS'))
- for search_path in search_pathes:
- # The following two lines are needed for a custom built Qt from version 5.5 on, possibly not for the one from the Qt SDK.
- # Looks like the upstream macdeployqt also had an issue there https://bugreports.qt.io/browse/QTBUG-47868
- if path.find( "\@rpath/"):
- path = path.replace("@rpath/", "")
- abs_path = os.path.join(search_path, path)
- if os.path.exists(abs_path):
- return abs_path
- raise CouldNotFindFrameworkError(path)
- def FindLibrary(path):
- if os.path.exists(path):
- return path
- search_pathes = LIBRARY_SEARCH_PATH
- search_pathes.insert(0, QueryQMake('QT_INSTALL_LIBS'))
- for search_path in search_pathes:
- abs_path = os.path.join(search_path, path)
- if os.path.exists(abs_path):
- return abs_path
- else: # try harder---look for lib name in library folders
- newpath = os.path.join(search_path,os.path.basename(path))
- if os.path.exists(newpath):
- return newpath
- return ""
- #raise CouldNotFindFrameworkError(path)
- def FixAllLibraries(broken_libs):
- for framework in broken_libs['frameworks']:
- FixFramework(framework)
- for lib in broken_libs['libs']:
- FixLibrary(lib)
- def FixFramework(path):
- if path in fixed_libraries:
- return
- else:
- fixed_libraries.append(path)
- abs_path = FindFramework(path)
- broken_libs = GetBrokenLibraries(abs_path)
- FixAllLibraries(broken_libs)
- new_path = CopyFramework(abs_path)
- id = os.sep.join(new_path.split(os.sep)[3:])
- FixFrameworkId(new_path, id)
- for framework in broken_libs['frameworks']:
- FixFrameworkInstallPath(framework, new_path)
- for library in broken_libs['libs']:
- FixLibraryInstallPath(library, new_path)
- def FixLibrary(path):
- if path in fixed_libraries or FindSystemLibrary(os.path.basename(path)) is not None:
- return
- else:
- fixed_libraries.append(path)
- abs_path = FindLibrary(path)
- if abs_path == "":
- print "Could not resolve %s, not fixing!" % path
- return
- broken_libs = GetBrokenLibraries(abs_path)
- FixAllLibraries(broken_libs)
- new_path = CopyLibrary(abs_path)
- FixLibraryId(new_path)
- for framework in broken_libs['frameworks']:
- FixFrameworkInstallPath(framework, new_path)
- for library in broken_libs['libs']:
- FixLibraryInstallPath(library, new_path)
- def FixPlugin(abs_path, subdir):
- broken_libs = GetBrokenLibraries(abs_path)
- FixAllLibraries(broken_libs)
- new_path = CopyPlugin(abs_path, subdir)
- for framework in broken_libs['frameworks']:
- FixFrameworkInstallPath(framework, new_path)
- for library in broken_libs['libs']:
- FixLibraryInstallPath(library, new_path)
- def FixBinary(path):
- broken_libs = GetBrokenLibraries(path)
- FixAllLibraries(broken_libs)
- for framework in broken_libs['frameworks']:
- FixFrameworkInstallPath(framework, path)
- for library in broken_libs['libs']:
- FixLibraryInstallPath(library, path)
- def CopyLibrary(path):
- new_path = os.path.join(binary_dir, os.path.basename(path))
- args = ['ditto', '--arch=x86_64', path, new_path]
- commands.append(args)
- args = ['chmod', 'u+w', new_path]
- commands.append(args)
- return new_path
- def CopyPlugin(path, subdir):
- new_path = os.path.join(plugins_dir, subdir, os.path.basename(path))
- args = ['mkdir', '-p', os.path.dirname(new_path)]
- commands.append(args)
- args = ['ditto', '--arch=x86_64', path, new_path]
- commands.append(args)
- args = ['chmod', 'u+w', new_path]
- commands.append(args)
- return new_path
- def CopyFramework(source_dylib):
- parts = source_dylib.split(os.sep)
- print "CopyFramework:", source_dylib
- for i, part in enumerate(parts):
- matchObj = re.match(r'(\w+\.framework)', part)
- if matchObj:
- framework = matchObj.group(1)
- dylib_name = parts[-1]
- source_path = os.path.join('/', *parts[:i+1])
- dest_path = os.path.join(frameworks_dir, framework)
- dest_dylib_path = os.path.join(frameworks_dir, *parts[i:-1])
- break
- if os.path.exists(dest_path):
- print dest_path, "already exists, skipping copy..."
- return os.path.join(dest_dylib_path, dylib_name)
- args = ['mkdir', '-p', dest_dylib_path]
- commands.append(args)
- args = ['ditto', '--arch=x86_64', source_dylib, dest_dylib_path]
- commands.append(args)
- args = ['chmod', 'u+w', os.path.join(dest_dylib_path, parts[-1])]
- commands.append(args)
- args = ['ln', '-s', '5', os.path.join(dest_path, 'Versions', 'Current')]
- commands.append(args)
- args = ['ln', '-s', os.path.join('Versions', 'Current', dylib_name), os.path.join(dest_path, dylib_name)]
- commands.append(args)
- args = ['ln', '-s', os.path.join('Versions', 'Current', 'Resources'), os.path.join(dest_path, 'Resources')]
- commands.append(args)
- args = ['cp', '-r', os.path.join(source_path, 'Versions', '5', 'Resources'), os.path.join(dest_path, 'Versions', '5')]
- commands.append(args)
- return os.path.join(dest_dylib_path, dylib_name)
- def FixId(path, library_name):
- id = '@executable_path/../Frameworks/%s' % library_name
- args = ['install_name_tool', '-id', id, path]
- commands.append(args)
- def FixLibraryId(path):
- library_name = os.path.basename(path)
- FixId(path, library_name)
- def FixFrameworkId(path, id):
- FixId(path, id)
- def FixInstallPath(library_path, library, new_path):
- args = ['install_name_tool', '-change', library_path, new_path, library]
- commands.append(args)
- def FindSystemLibrary(library_name):
- for path in ['/lib', '/usr/lib']:
- full_path = os.path.join(path, library_name)
- if os.path.exists(full_path):
- return full_path
- return None
- def FixLibraryInstallPath(library_path, library):
- system_library = FindSystemLibrary(os.path.basename(library_path))
- if system_library is None:
- new_path = '@executable_path/../MacOS/%s' % os.path.basename(library_path)
- FixInstallPath(library_path, library, new_path)
- else:
- FixInstallPath(library_path, library, system_library)
- def FixFrameworkInstallPath(library_path, library):
- parts = library_path.split(os.sep)
- for i, part in enumerate(parts):
- if re.match(r'\w+\.framework', part):
- full_path = os.path.join(*parts[i:])
- break
- new_path = '@executable_path/../Frameworks/%s' % full_path
- FixInstallPath(library_path, library, new_path)
- def FindQtPlugin(name):
- search_path = QT_PLUGINS_SEARCH_PATH
- search_path.insert(0, QueryQMake('QT_INSTALL_PLUGINS'))
- for path in search_path:
- if os.path.exists(path):
- if os.path.exists(os.path.join(path, name)):
- return os.path.join(path, name)
- raise CouldNotFindQtPluginError(name)
- for binary in binaries:
- FixBinary(binary)
- for plugin in QT_PLUGINS:
- FixPlugin(FindQtPlugin(plugin), os.path.dirname(plugin))
- if len(sys.argv) <= 2:
- print 'Will run %d commands:' % len(commands)
- for command in commands:
- print ' '.join(command)
- for command in commands:
- p = subprocess.Popen(command)
- os.waitpid(p.pid, 0)
- WriteQtConf()
|