Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3#include <QCoreApplication>
4#include <QDir>
5#include <QDirListing>
6#include <QLibraryInfo>
7
8#include "../shared/shared.h"
9
10int main(int argc, char **argv)
11{
12 QCoreApplication app(argc, argv);
13
14 QString appBundlePath;
15 if (argc > 1)
16 appBundlePath = QString::fromLocal8Bit(argv[1]);
17
18 if (argc < 2 || appBundlePath.startsWith("-")) {
19 qDebug() << "Usage: macdeployqt app-bundle [options]";
20 qDebug() << "";
21 qDebug() << "Options:";
22 qDebug() << " -verbose=<0-3> : 0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug";
23 qDebug() << " -no-plugins : Skip plugin deployment";
24 qDebug() << " -dmg : Create a .dmg disk image";
25 qDebug() << " -no-strip : Don't run 'strip' on the binaries";
26 qDebug() << " -use-debug-libs : Deploy with debug versions of frameworks and plugins (implies -no-strip)";
27 qDebug() << " -executable=<path> : Let the given executable use the deployed frameworks too";
28 qDebug() << " -qmldir=<path> : Scan for QML imports in the given path";
29 qDebug() << " -qmlimport=<path> : Add the given path to the QML module search locations";
30 qDebug() << " -always-overwrite : Copy files even if the target file exists";
31 qDebug() << " -codesign=<ident> : Run codesign with the given identity on "
32 "all executables (default: ad-hoc sign)";
33 qDebug() << " -no-codesign : Disable code signing";
34 qDebug() << " -hardened-runtime : Enable Hardened Runtime when code signing";
35 qDebug() << " -timestamp : Include a secure timestamp when code signing (requires internet connection)";
36 qDebug() << " -sign-for-notarization=<ident>: Activate the necessary options for notarization (requires internet connection)";
37 qDebug() << " -appstore-compliant : Skip deployment of components that use private API";
38 qDebug() << " -libpath=<path> : Add the given path to the library search path";
39 qDebug() << " -fs=<filesystem> : Set the filesystem used for the .dmg disk image (defaults to HFS+)";
40 qDebug() << "";
41 qDebug() << "macdeployqt takes an application bundle as input and makes it";
42 qDebug() << "self-contained by copying in the Qt frameworks and plugins that";
43 qDebug() << "the application uses.";
44 qDebug() << "";
45 qDebug() << "Plugins related to a framework are copied in with the";
46 qDebug() << "framework. The accessibility, image formats, and text codec";
47 qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified.";
48 qDebug() << "";
49 qDebug() << "Qt plugins may use private API and will cause the app to be";
50 qDebug() << "rejected from the Mac App store. macdeployqt will print a warning";
51 qDebug() << "when known incompatible plugins are deployed. Use -appstore-compliant ";
52 qDebug() << "to skip these plugins. Currently two SQL plugins are known to";
53 qDebug() << "be incompatible: qsqlodbc and qsqlpsql.";
54 qDebug() << "";
55 qDebug() << "See the \"Qt for macOS - Deployment\" topic in the";
56 qDebug() << "documentation for more information about deployment on macOS.";
57
58 return 1;
59 }
60
61 appBundlePath = QDir::cleanPath(appBundlePath);
62
63 if (!QDir(appBundlePath).exists()) {
64 qDebug() << "Error: Could not find app bundle" << appBundlePath;
65 return 1;
66 }
67
68 bool plugins = true;
69 bool dmg = false;
70 QByteArray filesystem("HFS+");
71 bool useDebugLibs = false;
72 extern bool runStripEnabled;
73 extern bool alwaysOwerwriteEnabled;
75 QStringList additionalExecutables;
76 bool qmldirArgumentUsed = false;
77 QStringList qmlDirs;
78 QStringList qmlImportPaths;
79 extern bool runCodesign;
80 QString codesignIdentity = QStringLiteral("-");
81 extern bool hardenedRuntime;
82 bool noCodesignExplicit = false;
83 extern bool appstoreCompliant;
84 extern bool deployFramework;
85 extern bool secureTimestamp;
86
87 for (int i = 2; i < argc; ++i) {
88 QByteArray argument = QByteArray(argv[i]);
89 if (argument == QByteArray("-no-plugins")) {
90 LogDebug() << "Argument found:" << argument;
91 plugins = false;
92 } else if (argument == QByteArray("-dmg")) {
93 LogDebug() << "Argument found:" << argument;
94 dmg = true;
95 } else if (argument == QByteArray("-no-strip")) {
96 LogDebug() << "Argument found:" << argument;
97 runStripEnabled = false;
98 } else if (argument == QByteArray("-use-debug-libs")) {
99 LogDebug() << "Argument found:" << argument;
100 useDebugLibs = true;
101 runStripEnabled = false;
102 } else if (argument.startsWith(QByteArray("-verbose"))) {
103 LogDebug() << "Argument found:" << argument;
104 int index = argument.indexOf("=");
105 bool ok = false;
106 int number = argument.mid(index+1).toInt(&ok);
107 if (!ok) {
108 LogError() << "Could not parse verbose level";
109 return 1;
110 } else {
111 logLevel = number;
112 }
113 } else if (argument.startsWith(QByteArray("-executable"))) {
114 LogDebug() << "Argument found:" << argument;
115 int index = argument.indexOf('=');
116 if (index == -1) {
117 LogError() << "Missing executable path";
118 return 1;
119 } else {
120 additionalExecutables << argument.mid(index+1);
121 }
122 } else if (argument.startsWith(QByteArray("-qmldir"))) {
123 LogDebug() << "Argument found:" << argument;
124 qmldirArgumentUsed = true;
125 int index = argument.indexOf('=');
126 if (index == -1) {
127 LogError() << "Missing qml directory path";
128 return 1;
129 } else {
130 qmlDirs << argument.mid(index+1);
131 }
132 } else if (argument.startsWith(QByteArray("-qmlimport"))) {
133 LogDebug() << "Argument found:" << argument;
134 int index = argument.indexOf('=');
135 if (index == -1) {
136 LogError() << "Missing qml import path";
137 return 1;
138 } else {
139 qmlImportPaths << argument.mid(index+1);
140 }
141 } else if (argument.startsWith(QByteArray("-libpath"))) {
142 LogDebug() << "Argument found:" << argument;
143 int index = argument.indexOf('=');
144 if (index == -1) {
145 LogError() << "Missing library search path";
146 return 1;
147 } else {
148 librarySearchPath << argument.mid(index+1);
149 }
150 } else if (argument == QByteArray("-always-overwrite")) {
151 LogDebug() << "Argument found:" << argument;
153 } else if (argument == QByteArray("-no-codesign")) {
154 LogDebug() << "Argument found:" << argument;
155 runCodesign = false;
156 noCodesignExplicit = true;
157 } else if (argument.startsWith(QByteArray("-codesign"))) {
158 LogDebug() << "Argument found:" << argument;
159 if (noCodesignExplicit) {
160 LogError() << "Error: -no-codesign cannot be combined with -codesign\n";
161 return 1;
162 }
163 int index = argument.indexOf("=");
164 if (index < 0 || index >= argument.size()) {
165 LogError() << "Missing code signing identity";
166 return 1;
167 } else {
168 runCodesign = true;
169 codesignIdentity = argument.mid(index + 1);
170 }
171 } else if (argument.startsWith(QByteArray("-sign-for-notarization"))) {
172 LogDebug() << "Argument found:" << argument;
173 if (noCodesignExplicit) {
174 LogError() << "Error: -no-codesign cannot be combined with -sign-for-notarization\n";
175 return 1;
176 }
177 int index = argument.indexOf("=");
178 if (index < 0 || index >= argument.size()) {
179 LogError() << "Missing code signing identity";
180 return 1;
181 } else {
182 runCodesign = true;
183 hardenedRuntime = true;
184 secureTimestamp = true;
185 codesignIdentity = argument.mid(index + 1);
186 }
187 } else if (argument.startsWith(QByteArray("-hardened-runtime"))) {
188 LogDebug() << "Argument found:" << argument;
189 hardenedRuntime = true;
190 } else if (argument.startsWith(QByteArray("-timestamp"))) {
191 LogDebug() << "Argument found:" << argument;
192 secureTimestamp = true;
193 } else if (argument == QByteArray("-appstore-compliant")) {
194 LogDebug() << "Argument found:" << argument;
195 appstoreCompliant = true;
196
197 // Undocumented option, may not work as intended
198 } else if (argument == QByteArray("-deploy-framework")) {
199 LogDebug() << "Argument found:" << argument;
200 deployFramework = true;
201
202 } else if (argument.startsWith(QByteArray("-fs"))) {
203 LogDebug() << "Argument found:" << argument;
204 int index = argument.indexOf('=');
205 if (index == -1) {
206 LogError() << "Missing filesystem type";
207 return 1;
208 } else {
209 filesystem = argument.mid(index+1);
210 }
211 } else if (argument.startsWith("-")) {
212 LogError() << "Unknown argument" << argument << "\n";
213 return 1;
214 }
215 }
216
217 DeploymentInfo deploymentInfo = deployQtFrameworks(appBundlePath, additionalExecutables, useDebugLibs);
218
219 if (deploymentInfo.isDebug)
220 useDebugLibs = true;
221
222 if (deployFramework && deploymentInfo.isFramework)
223 fixupFramework(appBundlePath);
224
225 // Convenience: Look for .qml files in the current directory if no -qmldir specified.
226 if (qmlDirs.isEmpty()) {
227 using namespace Qt::StringLiterals;
228 using F = QDirListing::IteratorFlag;
229 QDirListing lister(QDir::currentPath(), QStringList(u"*.qml"_s), F::FilesOnly);
230 if (lister.cbegin() != lister.cend())
231 qmlDirs += QStringLiteral(".");
232 }
233
234 if (!qmlDirs.isEmpty()) {
235 bool ok = deployQmlImports(appBundlePath, deploymentInfo, qmlDirs, qmlImportPaths);
236 if (!ok && qmldirArgumentUsed)
237 return 1; // exit if the user explicitly asked for qml import deployment
238
239 // Update deploymentInfo.deployedFrameworks - the QML imports
240 // may have brought in extra frameworks as dependencies.
241 deploymentInfo.deployedFrameworks += findAppFrameworkNames(appBundlePath);
242 deploymentInfo.deployedFrameworks =
243 QSet<QString>(deploymentInfo.deployedFrameworks.begin(),
244 deploymentInfo.deployedFrameworks.end()).values();
245 }
246
247 // Handle plugins
248 if (plugins) {
249 // Set the plugins search directory
250 deploymentInfo.pluginPath = QLibraryInfo::path(QLibraryInfo::PluginsPath);
251
252 // Sanity checks
253 if (deploymentInfo.pluginPath.isEmpty()) {
254 LogError() << "Missing Qt plugins path\n";
255 return 1;
256 }
257
258 if (!QDir(deploymentInfo.pluginPath).exists()) {
259 LogError() << "Plugins path does not exist" << deploymentInfo.pluginPath << "\n";
260 return 1;
261 }
262
263 // Deploy plugins
264 Q_ASSERT(!deploymentInfo.pluginPath.isEmpty());
265 if (!deploymentInfo.pluginPath.isEmpty()) {
266 LogNormal();
267 deployPlugins(appBundlePath, deploymentInfo, useDebugLibs);
268 createQtConf(appBundlePath);
269 }
270 }
271
272 if (runStripEnabled)
273 stripAppBinary(appBundlePath);
274
275 if (runCodesign)
276 codesign(codesignIdentity, appBundlePath);
277
278 if (dmg) {
279 LogNormal();
280 createDiskImage(appBundlePath, filesystem);
281 }
282
283 return 0;
284}
bool isDebug
Definition shared.h:85
bool isFramework
Definition shared.h:84
bool runCodesign
Definition shared.cpp:31
QStringList librarySearchPath
Definition shared.cpp:32
bool deployFramework
Definition shared.cpp:38
bool appstoreCompliant
Definition shared.cpp:36
bool alwaysOwerwriteEnabled
Definition shared.cpp:30
bool hardenedRuntime
Definition shared.cpp:34
bool secureTimestamp
Definition shared.cpp:35
bool runStripEnabled
Definition shared.cpp:29
#define LogError()
Definition shared.h:13
#define LogDebug()
Definition shared.h:16
#define LogNormal()
Definition shared.h:15
int logLevel
Definition shared.cpp:37
int main(int argc, char *argv[])
[ctor_close]