Use a modified version of macdeployqt
This commit is contained in:
parent
a7df2bc4fb
commit
8193be36e5
|
@ -0,0 +1,7 @@
|
||||||
|
add_executable(macdeployqt main.cpp shared.cpp)
|
||||||
|
target_link_libraries(macdeployqt PRIVATE
|
||||||
|
"-framework AppKit"
|
||||||
|
${QtCore_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
#execute_process(COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/macdeployqt ${CMAKE_BINARY_DIR})
|
|
@ -0,0 +1,291 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the tools applications of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#undef QT_NO_DEBUG_OUTPUT
|
||||||
|
#undef QT_NO_WARNING_OUTPUT
|
||||||
|
#undef QT_NO_INFO_OUTPUT
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QLibraryInfo>
|
||||||
|
|
||||||
|
#include "shared.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
|
QString appBundlePath;
|
||||||
|
if (argc > 1)
|
||||||
|
appBundlePath = QString::fromLocal8Bit(argv[1]);
|
||||||
|
|
||||||
|
if (argc < 2 || appBundlePath.startsWith("-")) {
|
||||||
|
qDebug() << "Usage: macdeployqt app-bundle [options]";
|
||||||
|
qDebug() << "";
|
||||||
|
qDebug() << "Options:";
|
||||||
|
qDebug() << " -verbose=<0-3> : 0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug";
|
||||||
|
qDebug() << " -no-plugins : Skip plugin deployment";
|
||||||
|
qDebug() << " -dmg : Create a .dmg disk image";
|
||||||
|
qDebug() << " -no-strip : Don't run 'strip' on the binaries";
|
||||||
|
qDebug() << " -use-debug-libs : Deploy with debug versions of frameworks and plugins (implies -no-strip)";
|
||||||
|
qDebug() << " -executable=<path> : Let the given executable use the deployed frameworks too";
|
||||||
|
qDebug() << " -qmldir=<path> : Scan for QML imports in the given path";
|
||||||
|
qDebug() << " -qmlimport=<path> : Add the given path to the QML module search locations";
|
||||||
|
qDebug() << " -always-overwrite : Copy files even if the target file exists";
|
||||||
|
qDebug() << " -codesign=<ident> : Run codesign with the given identity on all executables";
|
||||||
|
qDebug() << " -hardened-runtime : Enable Hardened Runtime when code signing";
|
||||||
|
qDebug() << " -timestamp : Include a secure timestamp when code signing (requires internet connection)";
|
||||||
|
qDebug() << " -sign-for-notarization=<ident>: Activate the necessary options for notarization (requires internet connection)";
|
||||||
|
qDebug() << " -appstore-compliant : Skip deployment of components that use private API";
|
||||||
|
qDebug() << " -libpath=<path> : Add the given path to the library search path";
|
||||||
|
qDebug() << " -fs=<filesystem> : Set the filesystem used for the .dmg disk image (defaults to HFS+)";
|
||||||
|
qDebug() << " -plugins-dir=<path> : Set plugins directory";
|
||||||
|
qDebug() << "";
|
||||||
|
qDebug() << "macdeployqt takes an application bundle as input and makes it";
|
||||||
|
qDebug() << "self-contained by copying in the Qt frameworks and plugins that";
|
||||||
|
qDebug() << "the application uses.";
|
||||||
|
qDebug() << "";
|
||||||
|
qDebug() << "Plugins related to a framework are copied in with the";
|
||||||
|
qDebug() << "framework. The accessibility, image formats, and text codec";
|
||||||
|
qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified.";
|
||||||
|
qDebug() << "";
|
||||||
|
qDebug() << "Qt plugins may use private API and will cause the app to be";
|
||||||
|
qDebug() << "rejected from the Mac App store. MacDeployQt will print a warning";
|
||||||
|
qDebug() << "when known incompatible plugins are deployed. Use -appstore-compliant ";
|
||||||
|
qDebug() << "to skip these plugins. Currently two SQL plugins are known to";
|
||||||
|
qDebug() << "be incompatible: qsqlodbc and qsqlpsql.";
|
||||||
|
qDebug() << "";
|
||||||
|
qDebug() << "See the \"Deploying Applications on OS X\" topic in the";
|
||||||
|
qDebug() << "documentation for more information about deployment on OS X.";
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
appBundlePath = QDir::cleanPath(appBundlePath);
|
||||||
|
|
||||||
|
if (QDir().exists(appBundlePath) == false) {
|
||||||
|
qDebug() << "Error: Could not find app bundle" << appBundlePath;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plugins = true;
|
||||||
|
bool dmg = false;
|
||||||
|
QByteArray filesystem("HFS+");
|
||||||
|
bool useDebugLibs = false;
|
||||||
|
extern bool runStripEnabled;
|
||||||
|
extern bool alwaysOwerwriteEnabled;
|
||||||
|
extern QStringList librarySearchPath;
|
||||||
|
QStringList additionalExecutables;
|
||||||
|
bool qmldirArgumentUsed = false;
|
||||||
|
QStringList qmlDirs;
|
||||||
|
QStringList qmlImportPaths;
|
||||||
|
extern bool runCodesign;
|
||||||
|
extern QString codesignIdentiy;
|
||||||
|
extern bool hardenedRuntime;
|
||||||
|
extern bool appstoreCompliant;
|
||||||
|
extern bool deployFramework;
|
||||||
|
extern bool secureTimestamp;
|
||||||
|
QString plugin_dir;
|
||||||
|
|
||||||
|
for (int i = 2; i < argc; ++i) {
|
||||||
|
QByteArray argument = QByteArray(argv[i]);
|
||||||
|
if (argument == QByteArray("-no-plugins")) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
plugins = false;
|
||||||
|
} else if (argument == QByteArray("-dmg")) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
dmg = true;
|
||||||
|
} else if (argument == QByteArray("-no-strip")) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
runStripEnabled = false;
|
||||||
|
} else if (argument == QByteArray("-use-debug-libs")) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
useDebugLibs = true;
|
||||||
|
runStripEnabled = false;
|
||||||
|
} else if (argument.startsWith(QByteArray("-verbose"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf("=");
|
||||||
|
bool ok = false;
|
||||||
|
int number = argument.mid(index+1).toInt(&ok);
|
||||||
|
if (!ok)
|
||||||
|
LogError() << "Could not parse verbose level";
|
||||||
|
else
|
||||||
|
logLevel = number;
|
||||||
|
} else if (argument.startsWith(QByteArray("-executable"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf('=');
|
||||||
|
if (index == -1)
|
||||||
|
LogError() << "Missing executable path";
|
||||||
|
else
|
||||||
|
additionalExecutables << argument.mid(index+1);
|
||||||
|
} else if (argument.startsWith(QByteArray("-qmldir"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
qmldirArgumentUsed = true;
|
||||||
|
int index = argument.indexOf('=');
|
||||||
|
if (index == -1)
|
||||||
|
LogError() << "Missing qml directory path";
|
||||||
|
else
|
||||||
|
qmlDirs << argument.mid(index+1);
|
||||||
|
} else if (argument.startsWith(QByteArray("-qmlimport"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf('=');
|
||||||
|
if (index == -1)
|
||||||
|
LogError() << "Missing qml import path";
|
||||||
|
else
|
||||||
|
qmlImportPaths << argument.mid(index+1);
|
||||||
|
} else if (argument.startsWith(QByteArray("-libpath"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf('=');
|
||||||
|
if (index == -1)
|
||||||
|
LogError() << "Missing library search path";
|
||||||
|
else
|
||||||
|
librarySearchPath << argument.mid(index+1);
|
||||||
|
} else if (argument == QByteArray("-always-overwrite")) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
alwaysOwerwriteEnabled = true;
|
||||||
|
} else if (argument.startsWith(QByteArray("-codesign"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf("=");
|
||||||
|
if (index < 0 || index >= argument.size()) {
|
||||||
|
LogError() << "Missing code signing identity";
|
||||||
|
} else {
|
||||||
|
runCodesign = true;
|
||||||
|
codesignIdentiy = argument.mid(index+1);
|
||||||
|
}
|
||||||
|
} else if (argument.startsWith(QByteArray("-sign-for-notarization"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf("=");
|
||||||
|
if (index < 0 || index >= argument.size()) {
|
||||||
|
LogError() << "Missing code signing identity";
|
||||||
|
} else {
|
||||||
|
runCodesign = true;
|
||||||
|
hardenedRuntime = true;
|
||||||
|
secureTimestamp = true;
|
||||||
|
codesignIdentiy = argument.mid(index+1);
|
||||||
|
}
|
||||||
|
} else if (argument.startsWith(QByteArray("-hardened-runtime"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
hardenedRuntime = true;
|
||||||
|
} else if (argument.startsWith(QByteArray("-timestamp"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
secureTimestamp = true;
|
||||||
|
} else if (argument == QByteArray("-appstore-compliant")) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
appstoreCompliant = true;
|
||||||
|
|
||||||
|
// Undocumented option, may not work as intented
|
||||||
|
} else if (argument == QByteArray("-deploy-framework")) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
deployFramework = true;
|
||||||
|
|
||||||
|
} else if (argument.startsWith(QByteArray("-fs"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf('=');
|
||||||
|
if (index == -1)
|
||||||
|
LogError() << "Missing filesystem type";
|
||||||
|
else
|
||||||
|
filesystem = argument.mid(index+1);
|
||||||
|
} else if (argument.startsWith(QByteArray("-plugins-dir"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf('=');
|
||||||
|
if (index == -1)
|
||||||
|
LogError() << "Missing filesystem type";
|
||||||
|
else
|
||||||
|
plugin_dir = argument.mid(index+1);
|
||||||
|
} else if (argument.startsWith("-")) {
|
||||||
|
LogError() << "Unknown argument" << argument << "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeploymentInfo deploymentInfo = deployQtFrameworks(appBundlePath, additionalExecutables, useDebugLibs);
|
||||||
|
|
||||||
|
if (deploymentInfo.isDebug)
|
||||||
|
useDebugLibs = true;
|
||||||
|
|
||||||
|
if (deployFramework && deploymentInfo.isFramework)
|
||||||
|
fixupFramework(appBundlePath);
|
||||||
|
|
||||||
|
// Convenience: Look for .qml files in the current directoty if no -qmldir specified.
|
||||||
|
if (qmlDirs.isEmpty()) {
|
||||||
|
QDir dir;
|
||||||
|
if (!dir.entryList(QStringList() << QStringLiteral("*.qml")).isEmpty()) {
|
||||||
|
qmlDirs += QStringLiteral(".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qmlDirs.isEmpty()) {
|
||||||
|
bool ok = deployQmlImports(appBundlePath, deploymentInfo, qmlDirs, qmlImportPaths);
|
||||||
|
if (!ok && qmldirArgumentUsed)
|
||||||
|
return 1; // exit if the user explicitly asked for qml import deployment
|
||||||
|
|
||||||
|
// Update deploymentInfo.deployedFrameworks - the QML imports
|
||||||
|
// may have brought in extra frameworks as dependencies.
|
||||||
|
deploymentInfo.deployedFrameworks += findAppFrameworkNames(appBundlePath);
|
||||||
|
deploymentInfo.deployedFrameworks =
|
||||||
|
QSet<QString>(deploymentInfo.deployedFrameworks.begin(),
|
||||||
|
deploymentInfo.deployedFrameworks.end()).values();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plugins) {
|
||||||
|
if (plugin_dir.isEmpty()) {
|
||||||
|
deploymentInfo.pluginPath = QLibraryInfo::path(QLibraryInfo::PluginsPath);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
deploymentInfo.pluginPath = plugin_dir;
|
||||||
|
}
|
||||||
|
if (deploymentInfo.pluginPath.isEmpty()) {
|
||||||
|
LogError() << "Missing Qt plugins path\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!QDir(deploymentInfo.pluginPath).exists()) {
|
||||||
|
LogError() << "Plugins path does not exist\n" << deploymentInfo.pluginPath;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Q_ASSERT(!deploymentInfo.pluginPath.isEmpty());
|
||||||
|
if (!deploymentInfo.pluginPath.isEmpty()) {
|
||||||
|
LogNormal();
|
||||||
|
deployPlugins(appBundlePath, deploymentInfo, useDebugLibs);
|
||||||
|
createQtConf(appBundlePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runStripEnabled)
|
||||||
|
stripAppBinary(appBundlePath);
|
||||||
|
|
||||||
|
if (runCodesign)
|
||||||
|
codesign(codesignIdentiy, appBundlePath);
|
||||||
|
|
||||||
|
if (dmg) {
|
||||||
|
LogNormal();
|
||||||
|
createDiskImage(appBundlePath, filesystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,141 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the tools applications of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
#ifndef MAC_DEPLOMYMENT_SHARED_H
|
||||||
|
#define MAC_DEPLOMYMENT_SHARED_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QVersionNumber>
|
||||||
|
|
||||||
|
extern int logLevel;
|
||||||
|
#define LogError() if (logLevel < 0) {} else qDebug() << "ERROR:"
|
||||||
|
#define LogWarning() if (logLevel < 1) {} else qDebug() << "WARNING:"
|
||||||
|
#define LogNormal() if (logLevel < 2) {} else qDebug() << "Log:"
|
||||||
|
#define LogDebug() if (logLevel < 3) {} else qDebug() << "Log:"
|
||||||
|
|
||||||
|
extern bool runStripEnabled;
|
||||||
|
|
||||||
|
class FrameworkInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool isDylib;
|
||||||
|
QString frameworkDirectory;
|
||||||
|
QString frameworkName;
|
||||||
|
QString frameworkPath;
|
||||||
|
QString binaryDirectory;
|
||||||
|
QString binaryName;
|
||||||
|
QString binaryPath;
|
||||||
|
QString rpathUsed;
|
||||||
|
QString version;
|
||||||
|
QString installName;
|
||||||
|
QString deployedInstallName;
|
||||||
|
QString sourceFilePath;
|
||||||
|
QString frameworkDestinationDirectory;
|
||||||
|
QString binaryDestinationDirectory;
|
||||||
|
|
||||||
|
bool isDebugLibrary() const
|
||||||
|
{
|
||||||
|
return binaryName.contains(QLatin1String("_debug"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DylibInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString binaryPath;
|
||||||
|
QVersionNumber currentVersion;
|
||||||
|
QVersionNumber compatibilityVersion;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OtoolInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString installName;
|
||||||
|
QString binaryPath;
|
||||||
|
QVersionNumber currentVersion;
|
||||||
|
QVersionNumber compatibilityVersion;
|
||||||
|
QList<DylibInfo> dependencies;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator==(const FrameworkInfo &a, const FrameworkInfo &b);
|
||||||
|
QDebug operator<<(QDebug debug, const FrameworkInfo &info);
|
||||||
|
|
||||||
|
class ApplicationBundleInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString path;
|
||||||
|
QString binaryPath;
|
||||||
|
QStringList libraryPaths;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeploymentInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString qtPath;
|
||||||
|
QString pluginPath;
|
||||||
|
QStringList deployedFrameworks;
|
||||||
|
QSet<QString> rpathsUsed;
|
||||||
|
bool useLoaderPath;
|
||||||
|
bool isFramework;
|
||||||
|
bool isDebug;
|
||||||
|
|
||||||
|
bool containsModule(const QString &module, const QString &libInFix) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info);
|
||||||
|
|
||||||
|
OtoolInfo findDependencyInfo(const QString &binaryPath);
|
||||||
|
FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
|
||||||
|
QString findAppBinary(const QString &appBundlePath);
|
||||||
|
QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
|
||||||
|
QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
|
||||||
|
QString copyFramework(const FrameworkInfo &framework, const QString path);
|
||||||
|
DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringList &additionalExecutables, bool useDebugLibs);
|
||||||
|
DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,const QString &bundlePath, const QStringList &binaryPaths, bool useDebugLibs, bool useLoaderPath);
|
||||||
|
void createQtConf(const QString &appBundlePath);
|
||||||
|
void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs);
|
||||||
|
bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInfo, QStringList &qmlDirs, QStringList &qmlImportPaths);
|
||||||
|
void changeIdentification(const QString &id, const QString &binaryPath);
|
||||||
|
void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath);
|
||||||
|
void runStrip(const QString &binaryPath);
|
||||||
|
void stripAppBinary(const QString &bundlePath);
|
||||||
|
QString findAppBinary(const QString &appBundlePath);
|
||||||
|
QStringList findAppFrameworkNames(const QString &appBundlePath);
|
||||||
|
QStringList findAppFrameworkPaths(const QString &appBundlePath);
|
||||||
|
void codesignFile(const QString &identity, const QString &filePath);
|
||||||
|
QSet<QString> codesignBundle(const QString &identity,
|
||||||
|
const QString &appBundlePath,
|
||||||
|
QList<QString> additionalBinariesContainingRpaths);
|
||||||
|
void codesign(const QString &identity, const QString &appBundlePath);
|
||||||
|
void createDiskImage(const QString &appBundlePath, const QString &filesystemType);
|
||||||
|
void fixupFramework(const QString &appBundlePath);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -252,6 +252,7 @@ set(SINGLECOREAPPLICATION_LIBRARIES singlecoreapplication)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
find_library(SPARKLE Sparkle)
|
find_library(SPARKLE Sparkle)
|
||||||
|
add_subdirectory(3rdparty/macdeployqt)
|
||||||
add_subdirectory(3rdparty/SPMediaKeyTap)
|
add_subdirectory(3rdparty/SPMediaKeyTap)
|
||||||
set(SPMEDIAKEYTAP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/SPMediaKeyTap)
|
set(SPMEDIAKEYTAP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/SPMediaKeyTap)
|
||||||
set(SPMEDIAKEYTAP_LIBRARIES SPMediaKeyTap)
|
set(SPMEDIAKEYTAP_LIBRARIES SPMediaKeyTap)
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
find_program(MACDEPLOYQT_EXECUTABLE NAMES macdeployqt PATHS /usr/local/opt/qt6/bin /usr/local/opt/qt5/bin /usr/local/bin REQUIRED)
|
#find_program(MACDEPLOYQT_EXECUTABLE NAMES macdeployqt PATHS /usr/local/opt/qt6/bin /usr/local/opt/qt5/bin /usr/local/bin REQUIRED)
|
||||||
if(MACDEPLOYQT_EXECUTABLE)
|
#if(MACDEPLOYQT_EXECUTABLE)
|
||||||
message(STATUS "Found macdeployqt: ${MACDEPLOYQT_EXECUTABLE}")
|
# message(STATUS "Found macdeployqt: ${MACDEPLOYQT_EXECUTABLE}")
|
||||||
else()
|
#else()
|
||||||
message(WARNING "Missing macdeployqt executable.")
|
# message(WARNING "Missing macdeployqt executable.")
|
||||||
endif()
|
#endif()
|
||||||
|
|
||||||
|
set(MACDEPLOYQT_EXECUTABLE "${CMAKE_BINARY_DIR}/3rdparty/macdeployqt/macdeployqt")
|
||||||
|
|
||||||
find_program(CREATEDMG_EXECUTABLE NAMES create-dmg REQUIRED)
|
find_program(CREATEDMG_EXECUTABLE NAMES create-dmg REQUIRED)
|
||||||
if(CREATEDMG_EXECUTABLE)
|
if(CREATEDMG_EXECUTABLE)
|
||||||
|
@ -20,13 +22,11 @@ endif()
|
||||||
if(MACDEPLOYQT_EXECUTABLE AND CREATEDMG_EXECUTABLE AND MACOS_VERSION_PACKAGE)
|
if(MACDEPLOYQT_EXECUTABLE AND CREATEDMG_EXECUTABLE AND MACOS_VERSION_PACKAGE)
|
||||||
add_custom_target(dmg
|
add_custom_target(dmg
|
||||||
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader -verbose=3
|
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader -verbose=3
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
|
|
||||||
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}-${MACOS_VERSION_PACKAGE}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
|
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}-${MACOS_VERSION_PACKAGE}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
)
|
)
|
||||||
add_custom_target(dmg2
|
add_custom_target(dmg2
|
||||||
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader -verbose=3
|
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader -verbose=3
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
|
|
||||||
COMMAND ${CREATEDMG_EXECUTABLE} --skip-jenkins --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}-${MACOS_VERSION_PACKAGE}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
|
COMMAND ${CREATEDMG_EXECUTABLE} --skip-jenkins --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}-${MACOS_VERSION_PACKAGE}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,513 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# Strawberry Music Player
|
|
||||||
# This file was part of Clementine.
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from distutils import spawn
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
LOGGER = logging.getLogger('macdeploy')
|
|
||||||
|
|
||||||
LIBRARY_SEARCH_PATH = ['/usr/local/lib', '/usr/local/opt/icu4c/lib']
|
|
||||||
|
|
||||||
FRAMEWORK_SEARCH_PATH = [
|
|
||||||
'/Library/Frameworks',
|
|
||||||
os.path.join(os.environ['HOME'], 'Library/Frameworks'),
|
|
||||||
'/Library/Frameworks/Sparkle.framework/Versions'
|
|
||||||
]
|
|
||||||
|
|
||||||
QT_PLUGINS = [
|
|
||||||
'platforms/libqcocoa.dylib',
|
|
||||||
'styles/libqmacstyle.dylib',
|
|
||||||
'sqldrivers/libqsqlite.dylib',
|
|
||||||
'iconengines/libqsvgicon.dylib',
|
|
||||||
'imageformats/libqgif.dylib',
|
|
||||||
'imageformats/libqicns.dylib',
|
|
||||||
'imageformats/libqico.dylib',
|
|
||||||
'imageformats/libqjpeg.dylib',
|
|
||||||
'imageformats/libqjp2.dylib',
|
|
||||||
'imageformats/libqsvg.dylib',
|
|
||||||
'imageformats/libqtiff.dylib',
|
|
||||||
]
|
|
||||||
|
|
||||||
QT_PLUGINS_SEARCH_PATH = [
|
|
||||||
'/usr/local/opt/qt6/share/qt/plugins',
|
|
||||||
]
|
|
||||||
|
|
||||||
GSTREAMER_SEARCH_PATH = [
|
|
||||||
'/usr/local/lib/gstreamer-1.0',
|
|
||||||
'/usr/local/Cellar/gstreamer',
|
|
||||||
]
|
|
||||||
|
|
||||||
GSTREAMER_PLUGINS = [
|
|
||||||
|
|
||||||
'libgstapetag.dylib',
|
|
||||||
'libgstapp.dylib',
|
|
||||||
'libgstaudioconvert.dylib',
|
|
||||||
'libgstaudiofx.dylib',
|
|
||||||
'libgstaudiomixer.dylib',
|
|
||||||
'libgstaudioparsers.dylib',
|
|
||||||
'libgstaudiorate.dylib',
|
|
||||||
'libgstaudioresample.dylib',
|
|
||||||
'libgstaudiotestsrc.dylib',
|
|
||||||
'libgstaudiovisualizers.dylib',
|
|
||||||
'libgstauparse.dylib',
|
|
||||||
'libgstautoconvert.dylib',
|
|
||||||
'libgstautodetect.dylib',
|
|
||||||
'libgstcoreelements.dylib',
|
|
||||||
'libgstequalizer.dylib',
|
|
||||||
'libgstgio.dylib',
|
|
||||||
'libgsticydemux.dylib',
|
|
||||||
'libgstid3demux.dylib',
|
|
||||||
'libgstlevel.dylib',
|
|
||||||
'libgstosxaudio.dylib',
|
|
||||||
'libgstplayback.dylib',
|
|
||||||
'libgstrawparse.dylib',
|
|
||||||
'libgstreplaygain.dylib',
|
|
||||||
'libgstsoup.dylib',
|
|
||||||
'libgstspectrum.dylib',
|
|
||||||
'libgsttypefindfunctions.dylib',
|
|
||||||
'libgstvolume.dylib',
|
|
||||||
'libgstxingmux.dylib',
|
|
||||||
'libgsttcp.dylib',
|
|
||||||
'libgstudp.dylib',
|
|
||||||
'libgstpbtypes.dylib',
|
|
||||||
'libgstrtp.dylib',
|
|
||||||
'libgstrtsp.dylib',
|
|
||||||
|
|
||||||
'libgstflac.dylib',
|
|
||||||
'libgstwavparse.dylib',
|
|
||||||
'libgstfaac.dylib',
|
|
||||||
'libgstfaad.dylib',
|
|
||||||
'libgstogg.dylib',
|
|
||||||
'libgstopus.dylib',
|
|
||||||
'libgstopusparse.dylib',
|
|
||||||
'libgstasf.dylib',
|
|
||||||
'libgstspeex.dylib',
|
|
||||||
'libgsttaglib.dylib',
|
|
||||||
'libgstvorbis.dylib',
|
|
||||||
'libgstisomp4.dylib',
|
|
||||||
'libgstlibav.dylib',
|
|
||||||
'libgstaiff.dylib',
|
|
||||||
'libgstlame.dylib',
|
|
||||||
'libgstmusepack.dylib',
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
GIO_MODULES_SEARCH_PATH = ['/usr/local/lib/gio/modules',]
|
|
||||||
|
|
||||||
INSTALL_NAME_TOOL_APPLE = 'install_name_tool'
|
|
||||||
INSTALL_NAME_TOOL_CROSS = 'x86_64-apple-darwin-%s' % INSTALL_NAME_TOOL_APPLE
|
|
||||||
INSTALL_NAME_TOOL = INSTALL_NAME_TOOL_CROSS if spawn.find_executable(INSTALL_NAME_TOOL_CROSS) else INSTALL_NAME_TOOL_APPLE
|
|
||||||
|
|
||||||
OTOOL_APPLE = 'otool'
|
|
||||||
OTOOL_CROSS = 'x86_64-apple-darwin-%s' % OTOOL_APPLE
|
|
||||||
OTOOL = OTOOL_CROSS if spawn.find_executable(OTOOL_CROSS) else OTOOL_APPLE
|
|
||||||
|
|
||||||
|
|
||||||
class Error(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class CouldNotFindFrameworkError(Error):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class CouldNotFindGioModuleError(Error):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class CouldNotFindQtPluginError(Error):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class CouldNotParseFrameworkNameError(Error):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class InstallNameToolError(Error):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class CouldNotFindGstreamerPluginError(Error):
|
|
||||||
pass
|
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print('Usage: %s <bundle.app>' % sys.argv[0])
|
|
||||||
|
|
||||||
bundle_dir = sys.argv[1]
|
|
||||||
|
|
||||||
bundle_name = os.path.basename(bundle_dir).split('.')[0]
|
|
||||||
|
|
||||||
commands = []
|
|
||||||
|
|
||||||
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')
|
|
||||||
binary = os.path.join(bundle_dir, 'Contents', 'MacOS', bundle_name)
|
|
||||||
tagreader_binary = os.path.join(plugins_dir, bundle_name + "-tagreader")
|
|
||||||
|
|
||||||
fixed_libraries = set()
|
|
||||||
fixed_frameworks = set()
|
|
||||||
|
|
||||||
|
|
||||||
def GetBrokenLibraries(binary):
|
|
||||||
#print("Checking libs for binary: %s" % binary)
|
|
||||||
output = subprocess.Popen([OTOOL, '-L', binary], stdout=subprocess.PIPE).communicate()[0].decode('utf-8')
|
|
||||||
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):
|
|
||||||
#print("system framework: %s" % line)
|
|
||||||
continue # System framework
|
|
||||||
elif re.match(r'^\s*/usr/lib/', line):
|
|
||||||
#print("unix style system lib: %s" % line)
|
|
||||||
continue # unix style system library
|
|
||||||
elif re.match(r'^\s*@executable_path', line) or re.match(r'^\s*@rpath', line) or re.match(r'^\s*@loader_path', line):
|
|
||||||
# Potentially already fixed library
|
|
||||||
if line.count('/') == 1:
|
|
||||||
relative_path = os.path.join(*line.split('/')[1:])
|
|
||||||
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
|
|
||||||
broken_libs['libs'].append(relative_path)
|
|
||||||
elif line.count('/') == 2:
|
|
||||||
relative_path = os.path.join(*line.split('/')[2:])
|
|
||||||
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
|
|
||||||
broken_libs['libs'].append(relative_path)
|
|
||||||
elif line.count('/') >= 3:
|
|
||||||
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:
|
|
||||||
print("GetBrokenLibraries Error: %s" % line)
|
|
||||||
elif re.search(r'\w+\.framework', line):
|
|
||||||
#print("framework: %s" % line)
|
|
||||||
broken_libs['frameworks'].append(line)
|
|
||||||
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):
|
|
||||||
LOGGER.debug("Found framework '%s' in '%s'", path, search_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):
|
|
||||||
LOGGER.debug("Found library '%s' in '%s'", path, search_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
|
|
||||||
|
|
||||||
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_frameworks:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
fixed_frameworks.add(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:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Always bundle libraries provided by homebrew (/usr/local).
|
|
||||||
if not re.match(r'^\s*/usr/local', path) and FindSystemLibrary(os.path.basename(path)) is not None:
|
|
||||||
return
|
|
||||||
|
|
||||||
fixed_libraries.add(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(frameworks_dir, os.path.basename(path))
|
|
||||||
#args = ['cp', path, new_path]
|
|
||||||
args = ['ditto', '--arch=i386', '--arch=x86_64', path, new_path]
|
|
||||||
commands.append(args)
|
|
||||||
commands.append(['chmod', '+w', new_path])
|
|
||||||
LOGGER.info("Copying library '%s'", path)
|
|
||||||
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 = ['cp', path, new_path]
|
|
||||||
args = ['ditto', '--arch=i386', '--arch=x86_64', path, new_path]
|
|
||||||
commands.append(args)
|
|
||||||
commands.append(['chmod', '+w', new_path])
|
|
||||||
LOGGER.info("Copying plugin '%s'", path)
|
|
||||||
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])
|
|
||||||
framework_name = part.split(".framework")[0]
|
|
||||||
break
|
|
||||||
|
|
||||||
def CopyFramework(src_binary):
|
|
||||||
while os.path.islink(src_binary):
|
|
||||||
src_binary = os.path.realpath(src_binary)
|
|
||||||
|
|
||||||
m = re.match(r'(.*/([^/]+)\.framework)/Versions/([^/]+)/.*', src_binary)
|
|
||||||
if not m:
|
|
||||||
raise CouldNotParseFrameworkNameError(src_binary)
|
|
||||||
|
|
||||||
src_base = m.group(1)
|
|
||||||
name = m.group(2)
|
|
||||||
version = m.group(3)
|
|
||||||
|
|
||||||
LOGGER.info('Copying framework %s version %s', name, version)
|
|
||||||
|
|
||||||
dest_base = os.path.join(frameworks_dir, '%s.framework' % name)
|
|
||||||
dest_dir = os.path.join(dest_base, 'Versions', version)
|
|
||||||
dest_binary = os.path.join(dest_dir, name)
|
|
||||||
|
|
||||||
commands.append(['mkdir', '-p', dest_dir])
|
|
||||||
commands.append(['cp', src_binary, dest_binary])
|
|
||||||
commands.append(['chmod', '+w', dest_binary])
|
|
||||||
|
|
||||||
# Copy special files from various places:
|
|
||||||
# QtCore has Resources/qt_menu.nib (copy to app's Resources)
|
|
||||||
# Sparkle has Resources/*
|
|
||||||
# Qt* have Resources/Info.plist
|
|
||||||
resources_src = os.path.join(src_base, 'Resources')
|
|
||||||
menu_nib = os.path.join(resources_src, 'qt_menu.nib')
|
|
||||||
if os.path.exists(menu_nib):
|
|
||||||
LOGGER.info("Copying qt_menu.nib '%s'", menu_nib)
|
|
||||||
commands.append(['cp', '-r', menu_nib, resources_dir])
|
|
||||||
elif os.path.exists(resources_src):
|
|
||||||
LOGGER.info("Copying resources dir '%s'", resources_src)
|
|
||||||
commands.append(['cp', '-r', resources_src, dest_dir])
|
|
||||||
|
|
||||||
info_plist = os.path.join(src_base, 'Contents', 'Info.plist')
|
|
||||||
if os.path.exists(info_plist):
|
|
||||||
LOGGER.info("Copying special file '%s'", info_plist)
|
|
||||||
resources_dest = os.path.join(dest_dir, 'Resources')
|
|
||||||
commands.append(['mkdir', resources_dest])
|
|
||||||
commands.append(['cp', '-r', info_plist, resources_dest])
|
|
||||||
|
|
||||||
# Create symlinks in the Framework to make it look like
|
|
||||||
# https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html
|
|
||||||
commands.append([
|
|
||||||
'ln', '-sf', 'Versions/Current/%s' % name, os.path.join(dest_base, name)
|
|
||||||
])
|
|
||||||
commands.append([
|
|
||||||
'ln', '-sf', 'Versions/Current/Resources',
|
|
||||||
os.path.join(dest_base, 'Resources')
|
|
||||||
])
|
|
||||||
commands.append(
|
|
||||||
['ln', '-sf', version, os.path.join(dest_base, 'Versions/Current')])
|
|
||||||
|
|
||||||
return dest_binary
|
|
||||||
|
|
||||||
|
|
||||||
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 or re.match(r'^\s*/usr/local', library_path):
|
|
||||||
new_path = '@executable_path/../Frameworks/%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)
|
|
||||||
full_path = ""
|
|
||||||
for i, part in enumerate(parts):
|
|
||||||
if re.match(r'\w+\.framework', part):
|
|
||||||
full_path = os.path.join(*parts[i:])
|
|
||||||
break
|
|
||||||
if full_path:
|
|
||||||
new_path = '@executable_path/../Frameworks/%s' % full_path
|
|
||||||
FixInstallPath(library_path, library, new_path)
|
|
||||||
|
|
||||||
|
|
||||||
def FindQtPlugin(name):
|
|
||||||
for path in QT_PLUGINS_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)
|
|
||||||
|
|
||||||
|
|
||||||
def FindGstreamerPlugin(name):
|
|
||||||
for path in GSTREAMER_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 CouldNotFindGstreamerPluginError(name)
|
|
||||||
|
|
||||||
|
|
||||||
def FindGioModule(name):
|
|
||||||
for path in GIO_MODULES_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 CouldNotFindGioModuleError(name)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
logging.basicConfig(filename='macdeploy.log', level=logging.DEBUG, format='%(asctime)s %(levelname)-8s %(message)s')
|
|
||||||
|
|
||||||
fixed_frameworks.add('A/QtCore')
|
|
||||||
fixed_frameworks.add('A/QtSql')
|
|
||||||
fixed_frameworks.add('A/QtGui')
|
|
||||||
fixed_frameworks.add('A/QtWidgets')
|
|
||||||
fixed_frameworks.add('A/QtSvg')
|
|
||||||
|
|
||||||
FixBinary(binary)
|
|
||||||
FixBinary(tagreader_binary)
|
|
||||||
|
|
||||||
# macdeployqt needs to handle strawberry-tagreader for Qt deployment, so we can't use FixPlugin() here.
|
|
||||||
#try:
|
|
||||||
# FixPlugin('strawberry-tagreader', '.')
|
|
||||||
#except:
|
|
||||||
# print('Failed to find blob: %s' % traceback.format_exc())
|
|
||||||
|
|
||||||
for plugin in GSTREAMER_PLUGINS:
|
|
||||||
FixPlugin(FindGstreamerPlugin(plugin), 'gstreamer')
|
|
||||||
|
|
||||||
FixPlugin(FindGstreamerPlugin('gst-plugin-scanner'), '.')
|
|
||||||
FixPlugin(FindGioModule('libgiognutls.so'), 'gio-modules')
|
|
||||||
#FixPlugin(FindGioModule('libgiognomeproxy.so'), 'gio-modules')
|
|
||||||
|
|
||||||
for plugin in QT_PLUGINS:
|
|
||||||
FixPlugin(FindQtPlugin(plugin), os.path.dirname(plugin))
|
|
||||||
|
|
||||||
if len(sys.argv) <= 2:
|
|
||||||
print('Would run %d commands:' % len(commands))
|
|
||||||
for command in commands:
|
|
||||||
print(' '.join(command))
|
|
||||||
|
|
||||||
#print('OK?')
|
|
||||||
#raw_input()
|
|
||||||
|
|
||||||
for command in commands:
|
|
||||||
p = subprocess.Popen(command)
|
|
||||||
os.waitpid(p.pid, 0)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
Loading…
Reference in New Issue