3#include <QCoreApplication>
15#include <QDirIterator>
16#include <QLibraryInfo>
17#include <QJsonDocument>
21#include <QRegularExpression>
25#include <CoreFoundation/CoreFoundation.h>
46 return ((
a.frameworkPath ==
b.frameworkPath) && (
a.binaryPath ==
b.binaryPath));
51 debug <<
"Framework name" <<
info.frameworkName <<
"\n";
52 debug <<
"Framework directory" <<
info.frameworkDirectory <<
"\n";
53 debug <<
"Framework path" <<
info.frameworkPath <<
"\n";
54 debug <<
"Binary directory" <<
info.binaryDirectory <<
"\n";
55 debug <<
"Binary name" <<
info.binaryName <<
"\n";
56 debug <<
"Binary path" <<
info.binaryPath <<
"\n";
57 debug <<
"Version" <<
info.version <<
"\n";
58 debug <<
"Install name" <<
info.installName <<
"\n";
59 debug <<
"Deployed install name" <<
info.deployedInstallName <<
"\n";
60 debug <<
"Source file Path" <<
info.sourceFilePath <<
"\n";
61 debug <<
"Framework Destination Directory (relative to bundle)" <<
info.frameworkDestinationDirectory <<
"\n";
62 debug <<
"Binary Destination Directory (relative to bundle)" <<
info.binaryDestinationDirectory <<
"\n";
71 debug <<
"Application bundle path" <<
info.path <<
"\n";
72 debug <<
"Binary path" <<
info.binaryPath <<
"\n";
73 debug <<
"Additional libraries" <<
info.libraryPaths <<
"\n";
83 qDebug() <<
"File exists, skip copy:" << to;
102 LogError() <<
"Failed to set u+w permissions on target file: " << to;
108 LogError() <<
"file copy failed from" << from;
117 if (
QFile(link).symLinkTarget().isEmpty())
118 LogError() << link <<
"exists but it's a file.";
120 LogNormal() <<
"Symlink exists, skipping:" << link;
127 LogError() <<
"failed to symlink" << link;
137 QFile infoPlist(infoPlistPath);
149 info.binaryPath = binaryPath;
152 LogDebug() <<
" inspecting" << binaryPath;
154 otool.start(
"otool",
QStringList() <<
"-L" << binaryPath);
155 otool.waitForFinished(-1);
157 if (otool.exitStatus() != QProcess::NormalExit || otool.exitCode() != 0) {
158 LogError() << otool.readAllStandardError();
163 "^\\t(.+) \\(compatibility version (\\d+\\.\\d+\\.\\d+), "
164 "current version (\\d+\\.\\d+\\.\\d+)(, weak|, reexport)?\\)$"));
168 if (outputLines.size() < 2) {
173 outputLines.removeFirst();
175 const auto match = regexp.match(outputLines.constFirst());
176 if (
match.hasMatch()) {
179 info.installName = installname;
182 outputLines.removeFirst();
184 info.installName = binaryPath;
187 LogDebug() <<
"Could not parse otool output line:" << outputLines.constFirst();
188 outputLines.removeFirst();
192 for (
const QString &outputLine : outputLines) {
193 const auto match = regexp.match(outputLine);
194 if (
match.hasMatch()) {
195 if (
match.captured(1) ==
info.installName)
201 info.dependencies << dylib;
203 LogDebug() <<
"Could not parse otool output line:" << outputLine;
215 if (trimmed.isEmpty())
219 if (trimmed.startsWith(
"/System/Library/") ||
220 (trimmed.startsWith(
"/usr/lib/") && trimmed.contains(
"libQt") ==
false)
221 || trimmed.startsWith(
"@executable_path") || trimmed.startsWith(
"@loader_path"))
225 if (trimmed.startsWith(
"@rpath/")) {
227 bool foundInsideBundle =
false;
228 for (
const QString &rpath : std::as_const(rpaths)) {
231 if (!appBundlePath.isEmpty()) {
234 foundInsideBundle =
true;
239 foundInsideBundle =
true;
246 if (!resolvedInfo.frameworkName.isEmpty() &&
QFile::exists(resolvedInfo.frameworkPath)) {
247 resolvedInfo.rpathUsed = rpath;
248 resolvedInfo.installName = trimmed;
252 if (!rpaths.isEmpty() && !foundInsideBundle) {
253 LogError() <<
"Cannot resolve rpath" << trimmed;
259 enum State {QtPath, FrameworkName, DylibName,
Version, FrameworkBinary,
End};
264 QString suffix = useDebugLibs ?
"_debug" :
"";
268 while (part < parts.count()) {
269 const QString currentPart = parts.at(part).simplified();
271 if (currentPart ==
"")
274 if (
state == QtPath) {
276 if (part < parts.count() && parts.at(part).contains(
".dylib")) {
280 }
else if (part < parts.count() && parts.at(part).endsWith(
".framework")) {
282 state = FrameworkName;
284 }
else if (trimmed.startsWith(
"/") ==
false) {
286 partsCopy.removeLast();
288 if (!
path.endsWith(
"/"))
292 info.frameworkDirectory =
path + partsCopy.join(u
'/');
296 if (currentPart.contains(
".framework")) {
297 if (
info.frameworkDirectory.isEmpty())
298 info.frameworkDirectory =
"/Library/Frameworks/" + partsCopy.join(u
'/');
299 if (!
info.frameworkDirectory.endsWith(
"/"))
300 info.frameworkDirectory +=
"/";
301 state = FrameworkName;
304 }
else if (currentPart.contains(
".dylib")) {
305 if (
info.frameworkDirectory.isEmpty())
306 info.frameworkDirectory =
"/usr/lib/" + partsCopy.join(u
'/');
307 if (!
info.frameworkDirectory.endsWith(
"/"))
308 info.frameworkDirectory +=
"/";
314 qtPath += (currentPart +
"/");
316 }
if (
state == FrameworkName) {
320 info.isDylib =
false;
321 info.frameworkName = currentPart;
325 }
if (
state == DylibName) {
330 info.deployedInstallName =
"@executable_path/../Frameworks/" +
info.binaryName;
331 info.frameworkPath =
info.frameworkDirectory +
info.binaryName;
332 info.sourceFilePath =
info.frameworkPath;
334 info.binaryDestinationDirectory =
info.frameworkDestinationDirectory;
335 info.binaryDirectory =
info.frameworkDirectory;
336 info.binaryPath =
info.frameworkPath;
341 info.version = currentPart;
342 info.binaryDirectory =
"Versions/" +
info.version;
343 info.frameworkPath =
info.frameworkDirectory +
info.frameworkName;
345 info.binaryDestinationDirectory =
info.frameworkDestinationDirectory +
"/" +
info.binaryDirectory;
346 state = FrameworkBinary;
347 }
else if (
state == FrameworkBinary) {
348 info.binaryName = currentPart.contains(suffix) ? currentPart : currentPart + suffix;
349 info.binaryPath =
"/" +
info.binaryDirectory +
"/" +
info.binaryName;
350 info.deployedInstallName =
"@executable_path/../Frameworks/" +
info.frameworkName +
info.binaryPath;
351 info.sourceFilePath =
info.frameworkPath +
info.binaryPath;
360 if (
info.installName.startsWith(
"@rpath/"))
361 info.deployedInstallName =
info.installName;
372 CFStringRef bundlePath = appBundlePath.toCFString();
373 CFURLRef bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundlePath,
374 kCFURLPOSIXPathStyle,
true);
375 CFRelease(bundlePath);
376 CFBundleRef bundle = CFBundleCreate(kCFAllocatorDefault, bundleURL);
378 CFURLRef executableURL = CFBundleCopyExecutableURL(bundle);
380 CFURLRef absoluteExecutableURL = CFURLCopyAbsoluteURL(executableURL);
381 if (absoluteExecutableURL) {
382 CFStringRef executablePath = CFURLCopyFileSystemPath(absoluteExecutableURL,
383 kCFURLPOSIXPathStyle);
384 if (executablePath) {
385 binaryPath = QString::fromCFString(executablePath);
386 CFRelease(executablePath);
388 CFRelease(absoluteExecutableURL);
390 CFRelease(executableURL);
394 CFRelease(bundleURL);
399 LogError() <<
"Could not find bundle binary for" << appBundlePath;
409 QString searchPath = appBundlePath +
"/Contents/Frameworks/";
412 while (
iter.hasNext()) {
414 frameworks <<
iter.fileInfo().fileName();
423 QString searchPath = appBundlePath +
"/Contents/Frameworks/";
426 while (
iter.hasNext()) {
428 frameworks <<
iter.fileInfo().filePath();
440 while (
iter.hasNext()) {
454 while (
iter.hasNext()) {
456 if (
iter.fileInfo().isSymLink())
469 while (
iter.hasNext()) {
471 if (
iter.fileInfo().isSymLink())
475 return iter.fileInfo().absoluteFilePath();
481QList<FrameworkInfo>
getQtFrameworks(
const QList<DylibInfo> &dependencies,
const QString &appBundlePath,
const QList<QString> &rpaths,
bool useDebugLibs)
483 QList<FrameworkInfo> libraries;
484 for (
const DylibInfo &dylibInfo : dependencies) {
486 if (
info.frameworkName.isEmpty() ==
false) {
489 libraries.append(
info);
497 if (
path.startsWith(
"@")) {
523 QList<QString> rpaths;
527 otool.waitForFinished();
529 if (otool.exitCode() != 0) {
530 LogError() << otool.readAllStandardError();
533 if (
resolve && executablePath.isEmpty()) {
534 executablePath =
path;
540 for (
auto i = outputLines.cbegin(),
end = outputLines.cend();
i !=
end; ++
i) {
541 if (
i->contains(
"cmd LC_RPATH") && ++
i !=
end &&
542 i->contains(
"cmdsize") && ++
i !=
end) {
544 int pathStart = rpathCmd.
indexOf(
"path ");
545 int pathEnd = rpathCmd.indexOf(
" (");
546 if (pathStart >= 0 && pathEnd >= 0 && pathStart < pathEnd) {
547 QString rpath = rpathCmd.
mid(pathStart + 5, pathEnd - pathStart - 5);
564 allRPaths.removeDuplicates();
570 QList<FrameworkInfo>
result;
571 QSet<QString> existing;
574 if (!existing.contains(
info.frameworkPath)) {
575 existing.insert(
info.frameworkPath);
585 const QList<QString> &additionalBinariesContainingRpaths)
591 bool rpathsLoaded =
false;
592 QList<QString> rpaths;
597 if (trimmedLine.startsWith(
"@executable_path/")) {
601 }
else if (trimmedLine.startsWith(
"@loader_path/")) {
605 }
else if (trimmedLine.startsWith(
"@rpath/")) {
608 for (
const QString &binaryPath : additionalBinariesContainingRpaths)
610 rpaths.removeDuplicates();
613 bool resolved =
false;
614 for (
const QString &rpath : std::as_const(rpaths)) {
623 if (!resolved && !rpaths.isEmpty()) {
624 LogError() <<
"Cannot resolve rpath" << trimmedLine;
637 const QDir sourceDir(sourcePath);
642 LogNormal() <<
"copy:" << sourcePath << destinationPath;
645 const bool hasValidRegExp = ignoreRegExp.isValid() && ignoreRegExp.pattern().length() > 0;
647 if (hasValidRegExp && ignoreRegExp.match(
file).hasMatch())
649 const QString fileSourcePath = sourcePath +
"/" +
file;
650 const QString fileDestinationPath = destinationPath +
"/" +
file;
665 LogNormal() <<
"copy:" << sourcePath << destinationPath;
666 const bool isDwarfPath = sourcePath.
endsWith(
"DWARF");
668 const QDir sourceDir(sourcePath);
672 const QString fileSourcePath = sourcePath + u
'/' +
file;
674 if (
file.endsWith(
"_debug.dylib")) {
690 QString fileDestinationPath = fileDestinationDir +
file;
693 QString linkDestinationPath = destinationPath + u
'/' +
file;
698 for (
int i = 0;
i < cdupCount - 2; ++
i)
699 linkPath.prepend(
"../");
705 bool useDebugLibs =
false;
706 bool useLoaderPath =
false;
707 QList<FrameworkInfo> frameworks =
getQtFrameworks(fileDestinationPath, appBundlePath, rpaths, useDebugLibs);
711 QString fileDestinationPath = destinationPath + u
'/' +
file;
725 LogError() <<
"no file at" << framework.sourceFilePath;
731 QString dylibDestinationDirectory =
path + u
'/' + framework.frameworkDestinationDirectory;
732 QString dylibDestinationBinaryPath = dylibDestinationDirectory + u
'/' + framework.binaryName;
735 if (!
QDir().mkpath(dylibDestinationDirectory)) {
736 LogError() <<
"could not create destination directory" << dylibDestinationDirectory;
742 return dylibDestinationBinaryPath;
746 return dylibDestinationBinaryPath;
752 LogError() <<
"no file at" << framework.sourceFilePath;
758 QString frameworkDestinationDirectory =
path + u
'/' + framework.frameworkDestinationDirectory;
759 QString frameworkBinaryDestinationDirectory = frameworkDestinationDirectory + u
'/' + framework.binaryDirectory;
760 QString frameworkDestinationBinaryPath = frameworkBinaryDestinationDirectory + u
'/' + framework.binaryName;
767 if (!
QDir().mkpath(frameworkBinaryDestinationDirectory)) {
768 LogError() <<
"could not create destination directory" << frameworkBinaryDestinationDirectory;
781 const QString resourcesSourcePath = framework.frameworkPath +
"/Resources";
782 const QString resourcesDestinationPath = frameworkDestinationDirectory +
"/Versions/" + framework.version +
"/Resources";
786 const QString librariesSourcePath = framework.frameworkPath +
"/Libraries";
787 const QString librariesDestinationPath = frameworkDestinationDirectory +
"/Versions/" + framework.version +
"/Libraries";
788 bool createdLibraries =
recursiveCopy(librariesSourcePath, librariesDestinationPath);
789 const QString helpersSourcePath = framework.frameworkPath +
"/Helpers";
790 const QString helpersDestinationPath = frameworkDestinationDirectory +
"/Versions/" + framework.version +
"/Helpers";
791 bool createdHelpers =
recursiveCopy(helpersSourcePath, helpersDestinationPath);
798 linkFilePrintStatus(
"Versions/Current/" + framework.binaryName, frameworkDestinationDirectory +
"/" + framework.binaryName);
799 linkFilePrintStatus(
"Versions/Current/Resources", frameworkDestinationDirectory +
"/Resources");
800 if (createdLibraries)
801 linkFilePrintStatus(
"Versions/Current/Libraries", frameworkDestinationDirectory +
"/Libraries");
803 linkFilePrintStatus(
"Versions/Current/Helpers", frameworkDestinationDirectory +
"/Helpers");
804 linkFilePrintStatus(framework.version, frameworkDestinationDirectory +
"/Versions/Current");
808 const QString legacyInfoPlistPath = framework.frameworkPath +
"/Contents/Info.plist";
809 const QString correctInfoPlistPath = frameworkDestinationDirectory +
"/Resources/Info.plist";
814 return frameworkDestinationBinaryPath;
819 QProcess installNametool;
820 installNametool.start(
"install_name_tool", options);
821 installNametool.waitForFinished();
822 if (installNametool.exitCode() != 0) {
823 LogError() << installNametool.readAllStandardError();
824 LogError() << installNametool.readAllStandardOutput();
830 LogDebug() <<
"Using install_name_tool:";
831 LogDebug() <<
" change identification in" << binaryPath;
842 deployedInstallName =
"@loader_path/"_L1
845 deployedInstallName = framework.deployedInstallName;
852 if (!canonicalInstallName.isEmpty() && canonicalInstallName != framework.installName) {
856 if (innerDependency != canonicalInstallName && innerDependency != framework.installName) {
871 +
"/Contents/Frameworks"_L1;
873 const QString loaderPathToFrameworks =
"@loader_path/"_L1 + relativeFrameworkPath;
874 bool rpathToFrameworksFound =
false;
877 for (
const QString &rpath : std::as_const(binaryRPaths)) {
878 if (rpath ==
"@executable_path/../Frameworks" ||
879 rpath == loaderPathToFrameworks) {
880 rpathToFrameworksFound =
true;
885 args <<
"-delete_rpath" << rpath;
891 if (!rpathToFrameworksFound) {
892 if (!useLoaderPath) {
893 args <<
"-add_rpath" <<
"@executable_path/../Frameworks";
895 args <<
"-add_rpath" << loaderPathToFrameworks;
898 LogDebug() <<
"Using install_name_tool:";
899 LogDebug() <<
" change rpaths in" << binaryPath;
913 LogDebug() <<
"Using install_name_tool:";
915 LogDebug() <<
" change reference" << oldName;
926 LogDebug() <<
" stripped" << binaryPath;
928 strip.start(
"strip",
QStringList() <<
"-x" << binaryPath);
929 strip.waitForFinished();
930 if (strip.exitCode() != 0) {
931 LogError() << strip.readAllStandardError();
932 LogError() << strip.readAllStandardOutput();
950 +
".[0-9]+.dylib"_L1);
966 LogNormal() <<
"Deploying Qt frameworks found inside:" << binaryPaths;
970 deploymentInfo.isFramework = bundlePath.contains(
".framework");
971 deploymentInfo.isDebug =
false;
972 QList<QString> rpathsUsed;
974 while (frameworks.isEmpty() ==
false) {
976 copiedFrameworks.append(framework.frameworkName);
981 if (framework.isDebugLibrary())
982 deploymentInfo.isDebug =
true;
984 if (deploymentInfo.qtPath.isNull())
987 if (framework.frameworkDirectory.startsWith(bundlePath)) {
988 LogError() << framework.frameworkName <<
"already deployed, skipping.";
992 if (!framework.rpathUsed.isEmpty() && !rpathsUsed.contains(framework.rpathUsed)) {
993 rpathsUsed.append(framework.rpathUsed);
997 const QString deployedBinaryPath = framework.isDylib ?
copyDylib(framework, bundlePath)
1004 if (deployedBinaryPath.isNull())
1010 if (!framework.rpathUsed.length()) {
1015 QList<FrameworkInfo> dependencies =
getQtFrameworks(deployedBinaryPath, bundlePath, rpathsUsed, useDebugLibs);
1018 if (dependency.rpathUsed.isEmpty()) {
1021 rpathsUsed.append(dependency.rpathUsed);
1025 if (copiedFrameworks.contains(dependency.frameworkName) ==
false && frameworks.contains(dependency) ==
false) {
1026 frameworks.append(dependency);
1030 deploymentInfo.deployedFrameworks = copiedFrameworks;
1031 deployRPaths(bundlePath, rpathsUsed, binaryPaths, useLoaderPath);
1032 deploymentInfo.rpathsUsed += rpathsUsed;
1033 deploymentInfo.rpathsUsed.removeDuplicates();
1034 return deploymentInfo;
1040 applicationBundle.
path = appBundlePath;
1041 applicationBundle.binaryPath =
findAppBinary(appBundlePath);
1043 QStringList allBinaryPaths =
QStringList() << applicationBundle.binaryPath << applicationBundle.libraryPaths
1044 << additionalExecutables;
1046 QList<QString> allLibraryPaths =
getBinaryRPaths(applicationBundle.binaryPath,
true);
1048 allLibraryPaths.removeDuplicates();
1050 QList<FrameworkInfo> frameworks =
getQtFrameworksForPaths(allBinaryPaths, appBundlePath, allLibraryPaths, useDebugLibs);
1053 LogWarning() <<
"Could not find any external Qt frameworks to deploy in" << appBundlePath;
1054 LogWarning() <<
"Perhaps macdeployqt was already used on" << appBundlePath <<
"?";
1055 LogWarning() <<
"If so, you will need to rebuild" << appBundlePath <<
"before trying again.";
1058 return deployQtFrameworks(frameworks, applicationBundle.path, allBinaryPaths, useDebugLibs, !additionalExecutables.isEmpty());
1065 for (
const QString &framework : deployedFrameworks) {
1068 Q_ASSERT(framework.length() >= 16);
1070 const int lengthOfLibInfix = framework.length() - 16;
1071 if (lengthOfLibInfix)
1072 libInfix = framework.mid(6, lengthOfLibInfix);
1082 LogNormal() <<
"Deploying plugins from" << pluginSourcePath;
1084 if (!pluginSourcePath.contains(deploymentInfo.pluginPath))
1090 const auto addPlugins = [&pluginSourcePath,&pluginList,useDebugLibs](
const QString &subDirectory,
1091 const std::function<bool(
QString)> &
predicate = std::function<bool(QString)>()) {
1094 for (
const QString &lib : libs) {
1095 if (lib.endsWith(
QStringLiteral(
"_debug.dylib")) != useDebugLibs)
1098 pluginList.append(subDirectory + u
'/' + lib);
1120 if (deploymentInfo.containsModule(
"Network", libInfix)) {
1126 const bool usesSvg = deploymentInfo.containsModule(
"Svg", libInfix);
1136 if (deploymentInfo.containsModule(
"Gui", libInfix)) {
1146 if (deploymentInfo.containsModule(
"Sql", libInfix)) {
1149 LogWarning() <<
"Plugin" << lib <<
"uses private API and is not Mac App store compliant.";
1160 if (deploymentInfo.containsModule(
"WebView", libInfix)) {
1163 LogWarning() <<
"Plugin" << lib <<
"uses QtWebEngine and is not Mac App store compliant.";
1173 static const std::map<QString, std::vector<QString>>
map {
1182 for (
const auto &
it :
map) {
1183 if (deploymentInfo.containsModule(
it.first, libInfix)) {
1184 for (
const auto &pluginType :
it.second) {
1185 addPlugins(pluginType);
1190 for (
const QString &plugin : pluginList) {
1191 QString sourcePath = pluginSourcePath +
"/" + plugin;
1192 const QString destinationPath = pluginDestinationPath +
"/" + plugin;
1198 QList<FrameworkInfo> frameworks =
getQtFrameworks(destinationPath, appBundleInfo.path, deploymentInfo.rpathsUsed, useDebugLibs);
1208 "Plugins = PlugIns\n"
1209 "Imports = Resources/qml\n"
1210 "QmlImports = Resources/qml\n";
1212 QString filePath = appBundlePath +
"/Contents/Resources/";
1221 LogWarning() <<
"To make sure the plugins are loaded from the correct location,";
1222 LogWarning() <<
"please make sure qt.conf contains the following lines:";
1229 if (qtconf.write(
contents) != -1) {
1231 LogNormal() <<
"This file sets the plugin search path to" << appBundlePath +
"/Contents/PlugIns";
1238 applicationBundle.
path = appBundlePath;
1239 applicationBundle.binaryPath =
findAppBinary(appBundlePath);
1241 const QString pluginDestinationPath = appBundlePath +
"/" +
"Contents/PlugIns";
1242 deployPlugins(applicationBundle, deploymentInfo.pluginPath, pluginDestinationPath, deploymentInfo, useDebugLibs);
1247 QString importDestinationPath = appBundlePath +
"/Contents/Resources/qml/" + importName;
1251 if (
QDir().exists(importDestinationPath))
1261 QString path1 = import1[
"path"].toString();
1262 QString path2 = import2[
"path"].toString();
1263 return path1 < path2;
1270 LogNormal() <<
"Deploying QML imports ";
1271 LogNormal() <<
"Application QML file path(s) is" << qmlDirs;
1272 LogNormal() <<
"QML module search path(s) is" << qmlImportPaths;
1275 QString qmlImportScannerPath =
1277 +
"/qmlimportscanner");
1285 LogError() <<
"qmlimportscanner not found at" << qmlImportScannerPath;
1286 LogError() <<
"Rebuild qtdeclarative/tools/qmlimportscanner";
1293 for (
const QString &qmlDir : qmlDirs) {
1294 argumentList.append(
"-rootPath");
1295 argumentList.append(qmlDir);
1297 for (
const QString &importPath : qmlImportPaths)
1298 argumentList <<
"-importPath" << importPath;
1300 argumentList.append(
"-importPath");
1301 argumentList.append(qmlImportsPath);
1304 QProcess qmlImportScanner;
1305 qmlImportScanner.start(qmlImportScannerPath, argumentList);
1306 if (!qmlImportScanner.waitForStarted()) {
1307 LogError() <<
"Could not start qmlimpoortscanner. Process error is" << qmlImportScanner.errorString();
1310 qmlImportScanner.waitForFinished(-1);
1313 qmlImportScanner.setReadChannel(QProcess::StandardError);
1314 QByteArray errors = qmlImportScanner.readAll();
1316 LogWarning() <<
"QML file parse error (deployment will continue):";
1321 qmlImportScanner.setReadChannel(QProcess::StandardOutput);
1322 QByteArray json = qmlImportScanner.readAll();
1325 LogError() <<
"qmlimportscanner output error. Expected json array, got:";
1346 if (
name.isEmpty() ||
path.isEmpty()) {
1347 LogNormal() <<
" Skip import: name or path is empty";
1355 LogNormal() <<
" Skip non-module import";
1363 name.replace(u
'.', u
'/');
1364 int secondTolast =
path.length() - 2;
1380 QString codeSignLogMessage =
"codesign";
1382 codeSignLogMessage +=
", enable hardened runtime";
1384 codeSignLogMessage +=
", include secure timestamp";
1385 LogNormal() << codeSignLogMessage << filePath;
1387 QStringList codeSignOptions = {
"--preserve-metadata=identifier,entitlements",
"--force",
"-s",
1388 identity, filePath };
1390 codeSignOptions <<
"-o" <<
"runtime";
1393 codeSignOptions <<
"--timestamp";
1399 codesign.start(
"codesign", codeSignOptions);
1404 LogError() <<
"Codesign signing error:";
1413 QList<QString> additionalBinariesContainingRpaths)
1423 LogNormal() <<
"Signing" << appBundlePath <<
"with identity" << identity;
1425 QStack<QString> pendingBinaries;
1426 QSet<QString> pendingBinariesSet;
1427 QSet<QString> signedBinaries;
1432 QString rootBinariesPath = appBundleAbsolutePath +
"/Contents/MacOS/";
1436 pendingBinaries.push(binaryPath);
1437 pendingBinariesSet.
insert(binaryPath);
1438 additionalBinariesContainingRpaths.
append(binaryPath);
1441 bool getAbsoltuePath =
true;
1444 pendingBinaries.push(
binary);
1445 pendingBinariesSet.insert(
binary);
1450 for (
const QString &frameworkPath : frameworkPaths) {
1455 while (helpersIterator.hasNext()) {
1456 helpersIterator.next();
1457 QString helpersPath = helpersIterator.filePath();
1459 for (
const QString &innerBundleName : innerBundleNames)
1461 helpersPath +
"/" + innerBundleName,
1462 additionalBinariesContainingRpaths);
1468 while (librariesIterator.hasNext()) {
1469 librariesIterator.next();
1470 QString librariesPath = librariesIterator.filePath();
1473 pendingBinaries.push(
binary);
1474 pendingBinariesSet.insert(
binary);
1480 while (!pendingBinaries.isEmpty()) {
1482 if (signedBinaries.contains(
binary))
1487 additionalBinariesContainingRpaths);
1488 dependencies = QSet<QString>(dependencies.begin(), dependencies.end())
1489 .subtract(signedBinaries)
1490 .subtract(pendingBinariesSet)
1493 if (!dependencies.isEmpty()) {
1494 pendingBinaries.push(
binary);
1495 pendingBinariesSet.insert(
binary);
1496 int dependenciesSkipped = 0;
1497 for (
const QString &dependency : std::as_const(dependencies)) {
1502 if (!dependency.startsWith(appBundleAbsolutePath)) {
1503 ++dependenciesSkipped;
1504 LogNormal() <<
"Skipping outside dependency: " << dependency;
1507 pendingBinaries.push(dependency);
1508 pendingBinariesSet.insert(dependency);
1513 if (dependenciesSkipped == dependencies.size()) {
1514 pendingBinaries.pop();
1525 signedBinaries.insert(
binary);
1526 pendingBinariesSet.remove(
binary);
1529 LogNormal() <<
"Finished codesigning " << appBundlePath <<
"with identity" << identity;
1537 LogError() <<
"codesign verification error:";
1543 return signedBinaries;
1552 QString appBaseName = appBundlePath;
1553 appBaseName.
chop(4);
1555 QString dmgName = appBaseName +
".dmg";
1563 LogNormal() <<
"Disk image already exists, skipping .dmg creation for" << dmg.fileName();
1565 LogNormal() <<
"Creating disk image (.dmg) for" << appBundlePath;
1568 LogNormal() <<
"Image will use" << filesystemType;
1572 <<
"create" << dmgName
1573 <<
"-srcfolder" << appBundlePath
1574 <<
"-format" <<
"UDZO"
1575 <<
"-fs" << filesystemType
1576 <<
"-volname" << appBaseName;
1579 hdutil.start(
"hdiutil", options);
1580 hdutil.waitForFinished(-1);
1581 if (hdutil.exitCode() != 0) {
1582 LogError() <<
"Bundle creation error:" << hdutil.readAllStandardError();
1590 if (parts.count() < 2) {
1591 LogError() <<
"fixupFramework: Unexpected framework name" << frameworkName;
1605 addRPath(
"@loader_path/../../Contents/Frameworks/", frameworkBinary);
QStringList deployedFrameworks
bool containsModule(const QString &module, const QString &libInFix) const
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
static QString applicationDirPath()
Returns the directory that contains the application executable.
The QDirIterator class provides an iterator for directory entrylists.
QStringList entryList(Filters filters=NoFilter, SortFlags sort=NoSort) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
static bool isAbsolutePath(const QString &path)
Returns true if path is absolute; returns false if it is relative.
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool mkpath(const QString &dirPath) const
Creates the directory path dirPath.
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
QString relativeFilePath(const QString &fileName) const
Returns the path to fileName relative to the directory.
static QString currentPath()
Returns the absolute path of the application's current directory.
QString absoluteFilePath() const
QString canonicalPath() const
Returns the file system entry's canonical path (excluding the entry's name), i.e.
QString canonicalFilePath() const
Returns the file system entry's canonical path, including the entry's name, that is,...
QDir absoluteDir() const
Returns a QDir object representing the absolute path of the parent directory of the file system entry...
QString path() const
Returns the path of the file system entry this QFileInfo refers to, excluding the entry's name.
bool exists() const
Returns true if the file system entry this QFileInfo refers to exists; otherwise returns false.
bool setPermissions(Permissions permissionSpec) override
Sets the permissions for the file to the permissions specified.
bool link(const QString &newName)
Creates a link named linkName that points to the file currently specified by fileName().
bool copy(const QString &newName)
Copies the file named fileName() to newName.
bool remove()
Removes the file specified by fileName().
Permissions permissions() const override
\reimp
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QVariantList toVariantList() const
Converts this object to a QVariantList.
\inmodule QtCore\reentrant
bool isArray() const
Returns true if the document contains an array.
QJsonArray array() const
Returns the QJsonArray contained in the document.
static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error=nullptr)
Parses json as a UTF-8 encoded JSON document, and creates a QJsonDocument from it.
static QString path(LibraryPath p)
qsizetype length() const noexcept
\inmodule QtCore \reentrant
\macro QT_RESTRICTED_CAST_FROM_ASCII
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
void chop(qsizetype n)
Removes n characters from the end of the string.
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
QString mid(qsizetype position, qsizetype n=-1) const &
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
QString simplified() const &
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
QString & insert(qsizetype i, QChar c)
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
QString & append(QChar c)
QString trimmed() const &
qsizetype length() const noexcept
Returns the number of characters in this string.
static Q_CORE_EXPORT QVersionNumber fromString(QAnyStringView string, qsizetype *suffixIndex=nullptr)
QMap< QString, QString > map
[6]
QSet< QString >::iterator it
QList< QString > QStringList
Constructs a string list that contains the given string, str.
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
GLint GLfloat GLfloat GLfloat v2
GLsizei GLsizei GLenum void * binary
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLenum GLsizei length
GLsizei const GLuint * paths
GLsizei const GLchar *const * path
static const QQmlJSScope * resolve(const QQmlJSScope *current, const QStringList &names)
static QString absolutePath(const QString &path)
#define QStringLiteral(str)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
QT_BEGIN_NAMESPACE typedef uchar * output
bool recursiveCopy(const QString &sourcePath, const QString &destinationPath, const QRegularExpression &ignoreRegExp=QRegularExpression())
QSet< QString > codesignBundle(const QString &identity, const QString &appBundlePath, QList< QString > additionalBinariesContainingRpaths)
void addRPath(const QString &rpath, const QString &binaryPath)
void codesignFile(const QString &identity, const QString &filePath)
QStringList getBinaryDependencies(const QString executablePath, const QString &path, const QList< QString > &additionalBinariesContainingRpaths)
bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInfo, QStringList &qmlDirs, QStringList &qmlImportPaths)
QString extraEntitlements
QStringList librarySearchPath
void createQtConf(const QString &appBundlePath)
const QString bundleFrameworkDirectory
void runStrip(const QString &binaryPath)
void fixupFramework(const QString &frameworkName)
QString findAppBinary(const QString &appBundlePath)
QStringList findAppBundleFiles(const QString &appBundlePath, bool absolutePath=false)
static bool importLessThan(const QVariant &v1, const QVariant &v2)
bool operator==(const FrameworkInfo &a, const FrameworkInfo &b)
QList< FrameworkInfo > getQtFrameworksForPaths(const QStringList &paths, const QString &appBundlePath, const QList< QString > &rpaths, bool useDebugLibs)
bool copyFilePrintStatus(const QString &from, const QString &to)
void deployRPaths(const QString &bundlePath, const QList< QString > &rpaths, const QString &binaryPath, bool useLoaderPath)
QString getLibInfix(const QStringList &deployedFrameworks)
QList< QString > getBinaryRPaths(const QString &path, bool resolve=true, QString executablePath=QString())
void patch_debugInInfoPlist(const QString &infoPlistPath)
void changeIdentification(const QString &id, const QString &binaryPath)
OtoolInfo findDependencyInfo(const QString &binaryPath)
void createDiskImage(const QString &appBundlePath, const QString &filesystemType)
void runInstallNameTool(QStringList options)
QList< FrameworkInfo > getQtFrameworks(const QList< DylibInfo > &dependencies, const QString &appBundlePath, const QList< QString > &rpaths, bool useDebugLibs)
QString copyFramework(const FrameworkInfo &framework, const QString path)
void stripAppBinary(const QString &bundlePath)
QStringList findAppFrameworkPaths(const QString &appBundlePath)
bool alwaysOwerwriteEnabled
void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pluginSourcePath, const QString pluginDestinationPath, DeploymentInfo deploymentInfo, bool useDebugLibs)
void deployQmlImport(const QString &appBundlePath, const QList< QString > &rpaths, const QString &importSourcePath, const QString &importName)
DeploymentInfo deployQtFrameworks(QList< FrameworkInfo > frameworks, const QString &bundlePath, const QStringList &binaryPaths, bool useDebugLibs, bool useLoaderPath)
void recursiveCopyAndDeploy(const QString &appBundlePath, const QList< QString > &rpaths, const QString &sourcePath, const QString &destinationPath)
QString resolveDyldPrefix(const QString &path, const QString &loaderPath, const QString &executablePath)
bool linkFilePrintStatus(const QString &file, const QString &link)
QStringList findAppFrameworkNames(const QString &appBundlePath)
void changeInstallName(const QString &bundlePath, const FrameworkInfo &framework, const QStringList &binaryPaths, bool useLoaderPath)
QString findEntitlementsFile(const QString &path)
void codesign(const QString &identity, const QString &appBundlePath)
QString copyDylib(const FrameworkInfo &framework, const QString path)
QStringList findAppLibraries(const QString &appBundlePath)
FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QList< QString > &rpaths, bool useDebugLibs)
QDebug operator<<(QDebug debug, const FrameworkInfo &info)
bool contains(const AT &t) const noexcept