From acb56be5288af3469575f13dc5b14da8f73ae890 Mon Sep 17 00:00:00 2001 From: Michael Schuster Date: Wed, 22 Jan 2020 04:53:03 +0100 Subject: [PATCH] macOS: Use macdeployqt instead of the old admin/osx/macdeployqt.py In order to build the new 2.7 releases (new QML dependencies) without manually modifying the old Python script each time, rely on Qt's standard tool instead. This should streamline the build systems for upcoming dependency changes. Signed-off-by: Michael Schuster --- admin/osx/macdeployqt.py | 366 ------------------------------- src/cmd/CMakeLists.txt | 17 +- src/crashreporter/CMakeLists.txt | 17 +- src/gui/CMakeLists.txt | 17 +- 4 files changed, 33 insertions(+), 384 deletions(-) delete mode 100755 admin/osx/macdeployqt.py diff --git a/admin/osx/macdeployqt.py b/admin/osx/macdeployqt.py deleted file mode 100755 index fbf4a1952..000000000 --- a/admin/osx/macdeployqt.py +++ /dev/null @@ -1,366 +0,0 @@ -#!/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 . - -from __future__ import print_function -import os -import re -import subprocess -import commands -import sys -from glob import glob -from distutils.version import LooseVersion - -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', '/usr/local/Qt-5.12.5/lib', '.'] - -QT_PLUGINS = [ - 'sqldrivers/libqsqlite.dylib', - 'platforms/libqcocoa.dylib', - 'styles/libqmacstyle.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', - '/usr/local/Qt-5.12.5/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 ' % 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)]; - -qt_version = QueryQMake('QT_VERSION') -print("Using Qt", qt_version) - -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) - args = ['ln', '-s', os.path.join('Versions', 'Current', 'Helpers'), os.path.join(dest_path, 'Helpers')] - commands.append(args) - args = ['cp', '-r', os.path.join(source_path, 'Versions', '5', 'Helpers'), 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 LooseVersion(qt_version) >= LooseVersion("5.10.0"): - args = ['plutil', '-insert', 'LSMinimumSystemVersion', '-string', '10.10.0', os.path.join(bundle_dir, 'Contents', 'Info.plist')] - commands.append(args) -else: - args = ['plutil', '-insert', 'LSMinimumSystemVersion', '-string', '10.7.0', os.path.join(bundle_dir, 'Contents', 'Info.plist')] - commands.append(args) - -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() diff --git a/src/cmd/CMakeLists.txt b/src/cmd/CMakeLists.txt index 6b0d0b0dc..722fc1284 100644 --- a/src/cmd/CMakeLists.txt +++ b/src/cmd/CMakeLists.txt @@ -46,10 +46,15 @@ endif() # currently it needs to be done because the code right above needs to be executed no matter # if building a bundle or not and the install_qt4_executable needs to be called afterwards if(BUILD_OWNCLOUD_OSX_BUNDLE AND NOT BUILD_LIBRARIES_ONLY) - get_target_property (QT_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) - install(CODE " - message(STATUS \"Deploying (Qt) dependencies and fixing library paths...\") - execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/admin/osx/macdeployqt.py\" ${CMAKE_INSTALL_PREFIX}/${OWNCLOUD_OSX_BUNDLE} ${QT_QMAKE_EXECUTABLE}) - execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/admin/osx/macdeployqt.py\" ${CMAKE_INSTALL_PREFIX}/${OWNCLOUD_OSX_BUNDLE}/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app ${QT_QMAKE_EXECUTABLE}) - " COMPONENT RUNTIME) + get_target_property (QT_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) + get_filename_component(QT_BIN_DIR "${QT_QMAKE_EXECUTABLE}" DIRECTORY) + find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${QT_BIN_DIR}") + + add_custom_command(TARGET ${cmd_NAME} POST_BUILD + COMMAND "${MACDEPLOYQT_EXECUTABLE}" + "$/../.." + -qmldir=${CMAKE_SOURCE_DIR}/src/gui + -always-overwrite + COMMENT "Running macdeployqt..." + ) endif() diff --git a/src/crashreporter/CMakeLists.txt b/src/crashreporter/CMakeLists.txt index 57043f6ff..eb68418dc 100644 --- a/src/crashreporter/CMakeLists.txt +++ b/src/crashreporter/CMakeLists.txt @@ -49,11 +49,16 @@ if(NOT BUILD_LIBRARIES_ONLY) # currently it needs to be done because the code right above needs to be executed no matter # if building a bundle or not and the install_qt4_executable needs to be called afterwards if(BUILD_OWNCLOUD_OSX_BUNDLE) - get_target_property (QT_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) - install(CODE " - message(STATUS \"Deploying (Qt) dependencies and fixing library paths...\") - execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/admin/osx/macdeployqt.py\" ${CMAKE_INSTALL_PREFIX}/${OWNCLOUD_OSX_BUNDLE} ${QT_QMAKE_EXECUTABLE}) - execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/admin/osx/macdeployqt.py\" ${CMAKE_INSTALL_PREFIX}/${OWNCLOUD_OSX_BUNDLE}/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app ${QT_QMAKE_EXECUTABLE}) - " COMPONENT RUNTIME) + get_target_property (QT_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) + get_filename_component(QT_BIN_DIR "${QT_QMAKE_EXECUTABLE}" DIRECTORY) + find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${QT_BIN_DIR}") + + add_custom_command(TARGET ${CRASHREPORTER_EXECUTABLE} POST_BUILD + COMMAND "${MACDEPLOYQT_EXECUTABLE}" + "$/../.." + -qmldir=${CMAKE_SOURCE_DIR}/src/gui + -always-overwrite + COMMENT "Running macdeployqt..." + ) endif() endif() diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 93f63450e..ed17270cb 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -389,12 +389,17 @@ install(TARGETS ${APPLICATION_EXECUTABLE} # currently it needs to be done because the code right above needs to be executed no matter # if building a bundle or not and the install_qt4_executable needs to be called afterwards if(BUILD_OWNCLOUD_OSX_BUNDLE AND NOT BUILD_LIBRARIES_ONLY) - get_target_property (QT_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) - install(CODE " - message(STATUS \"Deploying (Qt) dependencies and fixing library paths...\") - execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/admin/osx/macdeployqt.py\" ${CMAKE_INSTALL_PREFIX}/${OWNCLOUD_OSX_BUNDLE} ${QT_QMAKE_EXECUTABLE}) - execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/admin/osx/macdeployqt.py\" ${CMAKE_INSTALL_PREFIX}/${OWNCLOUD_OSX_BUNDLE}/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app ${QT_QMAKE_EXECUTABLE}) - " COMPONENT RUNTIME) + get_target_property (QT_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) + get_filename_component(QT_BIN_DIR "${QT_QMAKE_EXECUTABLE}" DIRECTORY) + find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${QT_BIN_DIR}") + + add_custom_command(TARGET ${APPLICATION_EXECUTABLE} POST_BUILD + COMMAND "${MACDEPLOYQT_EXECUTABLE}" + "$/../.." + -qmldir=${CMAKE_SOURCE_DIR}/src/gui + -always-overwrite + COMMENT "Running macdeployqt..." + ) endif() if(NOT BUILD_OWNCLOUD_OSX_BUNDLE AND NOT WIN32)