From ad52c825ae2d6c3f69a6abf5fba256b04e4860f8 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Sun, 28 Feb 2010 20:55:10 +0000 Subject: [PATCH] Mac deploy script - generates most of the commands necessary for an app bundle. --- macdeploy.py | 233 +++++++++++++++++++++++++++++++++++++++++++++ src/CMakeLists.txt | 22 ++++- 2 files changed, 254 insertions(+), 1 deletion(-) create mode 100755 macdeploy.py diff --git a/macdeploy.py b/macdeploy.py new file mode 100755 index 000000000..215cc865e --- /dev/null +++ b/macdeploy.py @@ -0,0 +1,233 @@ +#!/usr/bin/python + +import os +import re +import subprocess +import sys + +FRAMEWORK_SEARCH_PATH=['/Library/Frameworks', '~/Library/Frameworks'] +LIBRARY_SEARCH_PATH=['/usr/local/lib', '/sw/lib'] + +XINE_PLUGINS = [ + #'xineplug_decode_vorbis.so', + #'xineplug_dmx_ogg.so', + 'xineplug_ao_out_coreaudio.so', + 'xineplug_decode_a52.so', + 'xineplug_decode_dts.so', + 'xineplug_decode_faad.so', + 'xineplug_decode_mad.so', + 'xineplug_decode_mpc.so', + 'xineplug_dmx_audio.so', + 'xineplug_inp_cdda.so', + 'xineplug_inp_file.so', + 'xineplug_inp_http.so', + 'xineplug_inp_net.so', + 'xineplug_inp_rtp.so', + 'xineplug_inp_rtsp.so', + 'xineplug_post_audio_filters.so', +] +XINEPLUGIN_SEARCH_PATH=[ + '/usr/local/lib/xine/plugins', + '/usr/lib/xine/plugins', + '/sw/lib/xine/plugins', +] + + +class Error(Exception): + pass + + +class CouldNotFindFrameworkError(Error): + pass + + +class InstallNameToolError(Error): + pass + + +class CouldNotFindXinePluginError(Error): + pass + + +if len(sys.argv) < 2: + print 'Usage: %s ' % sys.argv[0] + +bundle_dir = sys.argv[1] + +bundle_name = bundle_dir.split('.')[0] + +frameworks_dir = os.path.join(bundle_dir, 'Contents', 'Frameworks') +resources_dir = os.path.join(bundle_dir, 'Contents', 'Resources') +plugins_dir = os.path.join(bundle_dir, 'Contents', 'PlugIns') +binary = os.path.join(bundle_dir, 'Contents', 'MacOS', bundle_name) + +fixed_libraries = [] +fixed_frameworks = [] + + +def GetBrokenLibraries(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:]]: + if not line: # skip empty lines + continue + if os.path.basename(binary) in line: + continue + if re.match(r'^\s*/System/', line): + continue # System framework + elif re.match(r'^\s*/usr/lib/', line): + continue # unix style system library + elif re.match(r'^\s*\w+\.framework', line): + broken_libs['frameworks'].append(line) + elif re.match(r'^\s*@executable_path', line): + continue # Already fixed library + else: + broken_libs['libs'].append(line) + + return broken_libs + +def FindFramework(path): + for search_path in FRAMEWORK_SEARCH_PATH: + 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 + for search_path in LIBRARY_SEARCH_PATH: + abs_path = os.path.join(search_path, path) + if os.path.exists(abs_path): + return abs_path + + 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) + FixFrameworkId(new_path, path) + 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: + return + else: + fixed_libraries.append(path) + abs_path = FindLibrary(path) + 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(path): + abs_path = FindXinePlugin(path) + broken_libs = GetBrokenLibraries(abs_path) + FixAllLibraries(broken_libs) + + new_path = CopyPlugin(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 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(frameworks_dir, os.path.basename(path)) + args = ['cp', '-f', path, new_path] + print ' '.join(args) + return new_path + +def CopyPlugin(path): + new_path = os.path.join(plugins_dir, os.path.basename(path)) + args = ['cp', '-f', path, new_path] + print ' '.join(args) + return new_path + +def CopyFramework(path): + parts = path.split(os.sep) + for i, part in enumerate(parts): + if re.match(r'\w+\.framework', part): + full_path = os.path.join(frameworks_dir, *parts[i:-1]) + break + args = ['mkdir', '-p', full_path] + print ' '.join(args) + args = ['cp', '-fR', path, full_path] + print ' '.join(args) + return os.path.join(frameworks_dir, path) + +def FixId(path, library_name): + id = '@executable_path/../Frameworks/%s' % library_name + args = ['install_name_tool', '-id', id, path] + print ' '.join(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] + print ' '.join(args) + +def FixLibraryInstallPath(library_path, library): + new_path = '@executable_path/../Frameworks/%s' % os.path.basename(library_path) + FixInstallPath(library_path, library, new_path) + +def FixFrameworkInstallPath(library_path, library): + new_path = '@executable_path/../Frameworks/%s' % library_path + FixInstallPath(library_path, library, new_path) + +def FindXinePlugin(name): + for path in XINEPLUGIN_SEARCH_PATH: + if os.path.exists(path): + for dir, dirs, files in os.walk(path): + if name in files: + return os.path.join(dir, name) + raise CouldNotFindXinePluginError(name) + + +FixBinary(binary) + +for plugin in XINE_PLUGINS: + FixPlugin(plugin) + +qtconf = open(os.path.join(resources_dir, 'qt.conf'), 'w') +qtconf.write('''[Paths] +Plugins = PlugIns +''') +qtconf.close() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f90b23417..2b858a867 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -152,6 +152,7 @@ include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/qxt") set(EXECUTABLE_OUTPUT_PATH ..) add_executable(clementine + MACOSX_BUNDLE ${CLEMENTINE-SOURCES} ${CLEMENTINE-SOURCES-MOC} ${CLEMENTINE-SOURCES-UI} @@ -166,6 +167,11 @@ target_link_libraries(clementine ${TAGLIB_LIBRARIES} lastfm ) +if (APPLE) + target_link_libraries(clementine + ${GROWL} + ) +endif (APPLE) add_dependencies(clementine qtsingleapplication qxt) set_property(TARGET clementine @@ -173,5 +179,19 @@ set_property(TARGET clementine "-Werror=non-virtual-dtor -Woverloaded-virtual -Wall" ) +set_target_properties(clementine PROPERTIES + MACOSX_BUNDLE_INFO_STRING "Clementine 0.1" + MACOSX_BUNDLE_ICON_FILE "clementine" + MACOSX_BUNDLE_SHORT_VERSION_STRING "0.1" +) + +add_custom_target(bundle + mkdir -p Frameworks/ + COMMAND mkdir -p PlugIns/xine + COMMAND mkdir -p Resources/ + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../clementine.app/Contents/ +) +add_dependencies(bundle clementine) + install(TARGETS clementine - RUNTIME DESTINATION bin) + BUNDLE DESTINATION bin)