diff --git a/3rdparty/macdeployqt/main.cpp b/3rdparty/macdeployqt/main.cpp index 74a6ac914..7353418d7 100644 --- a/3rdparty/macdeployqt/main.cpp +++ b/3rdparty/macdeployqt/main.cpp @@ -274,10 +274,6 @@ int main(int argc, char **argv) if (runStripEnabled) stripAppBinary(appBundlePath); - if (!FinalCheck(appBundlePath)) { - return 1; - } - if (runCodesign) codesign(codesignIdentiy, appBundlePath); diff --git a/3rdparty/macdeployqt/shared.cpp b/3rdparty/macdeployqt/shared.cpp index fa3bfbb24..77c71fec3 100644 --- a/3rdparty/macdeployqt/shared.cpp +++ b/3rdparty/macdeployqt/shared.cpp @@ -1764,109 +1764,3 @@ void fixupFramework(const QString &frameworkName) addRPath("@loader_path/../../Contents/Frameworks/", frameworkBinary); } -bool FinalCheck(const QString &appBundlePath) { - - bool success = true; - - QDirIterator iter(appBundlePath, QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories); - while (iter.hasNext()) { - iter.next(); - QString filepath = iter.fileInfo().filePath(); - - if (filepath.endsWith(".plist") || - filepath.endsWith(".icns") || - filepath.endsWith(".prl") || - filepath.endsWith(".conf") || - filepath.endsWith(".h") || - filepath.endsWith(".nib") || - filepath.endsWith(".strings") || - filepath.endsWith(".css") || - filepath.endsWith("CodeResources") || - filepath.endsWith("PkgInfo") || - filepath.endsWith(".modulemap")) { - continue; - } - - //qDebug() << "Final check on" << filepath; - - QProcess otool; - otool.start("otool", QStringList() << "-L" << filepath); - otool.waitForFinished(); - if (otool.exitStatus() != QProcess::NormalExit || otool.exitCode() != 0) { - LogError() << otool.readAllStandardError(); - success = false; - continue; - } - QString output = otool.readAllStandardOutput(); - QStringList output_lines = output.split("\n", Qt::SkipEmptyParts); - if (output_lines.size() < 2) { - LogError() << "Could not parse otool output:" << output; - success = false; - continue; - } - QString first_line = output_lines.first(); - if (first_line.endsWith(':')) first_line.chop(1); - if (first_line == filepath) { - output_lines.removeFirst(); - } - else { - LogError() << "First line" << first_line << "does not match" << filepath; - success = false; - } - static const QRegularExpression regexp(QStringLiteral("^\\t(.+) \\(compatibility version (\\d+\\.\\d+\\.\\d+), current version (\\d+\\.\\d+\\.\\d+)(, weak)?\\)$")); - for (const QString &output_line : output_lines) { - - //qDebug() << "Final check on" << filepath << output_line; - - const auto match = regexp.match(output_line); - if (match.hasMatch()) { - QString library = match.captured(1); - if (QFileInfo(library).fileName() == QFileInfo(filepath).fileName()) { // It's this. - continue; - } - else if (library.startsWith("@executable_path")) { - QString real_path = library; - real_path = real_path.replace("@executable_path", appBundlePath + "/Contents/MacOS"); - if (!QFile(real_path).exists()) { - LogError() << real_path << "does not exist for" << filepath; - success = false; - } - } - else if (library.startsWith("@rpath")) { - QString real_path = library; - real_path = real_path.replace("@rpath", appBundlePath + "/Contents/Frameworks"); - if (!QFile(real_path).exists() && !real_path.endsWith("QtSvg")) { // FIXME: Ignore broken svg image plugin. - LogError() << real_path << "does not exist for" << filepath; - success = false; - } - } - else if (library.startsWith("@loader_path")) { - QString loader_path = QFileInfo(filepath).path(); - QString real_path = library; - real_path = real_path.replace("@loader_path", loader_path); - if (!QFile(real_path).exists()) { - LogError() << real_path << "does not exist for" << filepath; - success = false; - } - } - else if (library.startsWith("/System/Library/") || library.startsWith("/usr/lib/")) { // System library - continue; - } - else if (library.endsWith("libgcc_s.1.dylib")) { // fftw points to it for some reason. - continue; - } - else { - LogError() << "File" << filepath << "points to" << library; - success = false; - } - } - else { - LogError() << "Could not parse otool output line:" << output_line; - success = false; - } - } - } - - return success; - -} diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b317de05..e88f165f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -289,10 +289,11 @@ set(SINGLECOREAPPLICATION_LIBRARIES singlecoreapplication) if(APPLE) find_library(SPARKLE Sparkle PATHS "/usr/local/opt/sparkle") - add_subdirectory(3rdparty/macdeployqt) add_subdirectory(3rdparty/SPMediaKeyTap) set(SPMEDIAKEYTAP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/SPMediaKeyTap) set(SPMEDIAKEYTAP_LIBRARIES SPMediaKeyTap) + add_subdirectory(3rdparty/macdeployqt) + add_subdirectory(ext/macdeploycheck) endif() if(NOT SPARKLE AND (APPLE OR WIN32)) diff --git a/cmake/Dmg.cmake b/cmake/Dmg.cmake index 039f8b5a7..3a19996e3 100644 --- a/cmake/Dmg.cmake +++ b/cmake/Dmg.cmake @@ -22,19 +22,29 @@ add_custom_target(copy_sparkle ) if(MACDEPLOYQT_EXECUTABLE) + add_custom_target(copy_gstreamer_plugins + #COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macgstcopy.sh strawberry.app + ) add_custom_target(deploy COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/{Frameworks,Resources} COMMAND cp ${CMAKE_SOURCE_DIR}/dist/macos/Info.plist ${CMAKE_BINARY_DIR}/strawberry.app/Contents/ COMMAND cp ${CMAKE_SOURCE_DIR}/dist/macos/strawberry.icns ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources/ - COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader + COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 + -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader + -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/gio-modules/libgiognutls.so + #-executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/gst-plugin-scanner WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS strawberry strawberry-tagreader macdeployqt + DEPENDS strawberry strawberry-tagreader copy_gstreamer_plugins macdeployqt + ) + add_custom_target(deploycheck + COMMAND ${CMAKE_BINARY_DIR}/ext/macdeploycheck/macdeploycheck strawberry.app + DEPENDS macdeploycheck ) if(CREATEDMG_EXECUTABLE) add_custom_target(dmg COMMAND ${CREATEDMG_EXECUTABLE} --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 strawberry-${STRAWBERRY_VERSION_PACKAGE}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS deploy + DEPENDS deploy deploycheck ) endif() endif() diff --git a/ext/macdeploycheck/CMakeLists.txt b/ext/macdeploycheck/CMakeLists.txt new file mode 100644 index 000000000..a041c909c --- /dev/null +++ b/ext/macdeploycheck/CMakeLists.txt @@ -0,0 +1,15 @@ +qt_wrap_cpp(MACDEPLOYCHECK_MOC ${CMAKE_SOURCE_DIR}/ext/libstrawberry-common/core/logging.h) +add_executable(macdeploycheck macdeploycheck.cpp ${CMAKE_SOURCE_DIR}/ext/libstrawberry-common/core/logging.cpp ${MACDEPLOYCHECK_MOC}) +link_directories(macdeploycheck ${GLIB_LIBRARY_DIRS}) +target_include_directories(macdeploycheck PUBLIC SYSTEM + ${GLIB_INCLUDE_DIRS} +) +target_include_directories(macdeploycheck PUBLIC + ${CMAKE_SOURCE_DIR}/ext/libstrawberry-common + ${CMAKE_BINARY_DIR}/src +) +target_link_libraries(macdeploycheck PUBLIC + "-framework AppKit" + ${GLIB_LIBRARIES} + ${QtCore_LIBRARIES} +) diff --git a/ext/macdeploycheck/macdeploycheck.cpp b/ext/macdeploycheck/macdeploycheck.cpp new file mode 100644 index 000000000..38e4491ac --- /dev/null +++ b/ext/macdeploycheck/macdeploycheck.cpp @@ -0,0 +1,150 @@ +/* This file is part of Strawberry. + Copyright 2021, Jonas Kvinge + + Strawberry 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 3 of the License, or + (at your option) any later version. + + Strawberry 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 Strawberry. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/logging.h" + +int main(int argc, char **argv); +int main(int argc, char **argv) { + + QCoreApplication app(argc, argv); + + logging::Init(); + + qLog(Info) << "Running macdeploycheck"; + + if (argc < 1) { + qLog(Error) << "Usage: macdeploycheck "; + return 1; + } + QString bundle_path = QString::fromLocal8Bit(argv[1]); + + bool success = true; + + QDirIterator iter(bundle_path, QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories); + while (iter.hasNext()) { + + iter.next(); + + QString filepath = iter.fileInfo().filePath(); + + // Ignore these files. + if (filepath.endsWith(".plist") || + filepath.endsWith(".icns") || + filepath.endsWith(".prl") || + filepath.endsWith(".conf") || + filepath.endsWith(".h") || + filepath.endsWith(".nib") || + filepath.endsWith(".strings") || + filepath.endsWith(".css") || + filepath.endsWith("CodeResources") || + filepath.endsWith("PkgInfo") || + filepath.endsWith(".modulemap")) { + continue; + } + + QProcess otool; + otool.start("otool", QStringList() << "-L" << filepath); + otool.waitForFinished(); + if (otool.exitStatus() != QProcess::NormalExit || otool.exitCode() != 0) { + qLog(Error) << "otool failed for" << filepath << ":" << otool.readAllStandardError(); + success = false; + continue; + } + QString output = otool.readAllStandardOutput(); + QStringList output_lines = output.split("\n", Qt::SkipEmptyParts); + if (output_lines.size() < 2) { + qLog(Error) << "Could not parse otool output:" << output; + success = false; + continue; + } + QString first_line = output_lines.first(); + if (first_line.endsWith(':')) first_line.chop(1); + if (first_line == filepath) { + output_lines.removeFirst(); + } + else { + qLog(Error) << "First line" << first_line << "does not match" << filepath; + success = false; + } + QRegularExpression regexp(QStringLiteral("^\\t(.+) \\(compatibility version (\\d+\\.\\d+\\.\\d+), current version (\\d+\\.\\d+\\.\\d+)(, weak)?\\)$")); + for (const QString &output_line : output_lines) { + + //qDebug() << "Final check on" << filepath << output_line; + + QRegularExpressionMatch match = regexp.match(output_line); + if (match.hasMatch()) { + QString library = match.captured(1); + if (QFileInfo(library).fileName() == QFileInfo(filepath).fileName()) { // It's this. + continue; + } + else if (library.startsWith("@executable_path")) { + QString real_path = library; + real_path = real_path.replace("@executable_path", bundle_path + "/Contents/MacOS"); + if (!QFile(real_path).exists()) { + qLog(Error) << real_path << "does not exist for" << filepath; + success = false; + } + } + else if (library.startsWith("@rpath")) { + QString real_path = library; + real_path = real_path.replace("@rpath", bundle_path + "/Contents/Frameworks"); + if (!QFile(real_path).exists() && !real_path.endsWith("QtSvg")) { // FIXME: Ignore broken svg image plugin. + qLog(Error) << real_path << "does not exist for" << filepath; + success = false; + } + } + else if (library.startsWith("@loader_path")) { + QString loader_path = QFileInfo(filepath).path(); + QString real_path = library; + real_path = real_path.replace("@loader_path", loader_path); + if (!QFile(real_path).exists()) { + qLog(Error) << real_path << "does not exist for" << filepath; + success = false; + } + } + else if (library.startsWith("/System/Library/") || library.startsWith("/usr/lib/")) { // System library + continue; + } + else if (library.endsWith("libgcc_s.1.dylib")) { // fftw points to it for some reason. + continue; + } + else { + qLog(Error) << "File" << filepath << "points to" << library; + success = false; + } + } + else { + qLog(Error) << "Could not parse otool output line:" << output_line; + success = false; + } + } + } + + return success ? 0 : 1; + +}