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() << " -exclude-plugins=<list> : Do not deploy the given plugins, a comma-separated";
40 qDebug() << " list of plugin names such as qjpeg,qsvgicon";
41 qDebug() << " -include-plugins=<list> : Also deploy the given plugins, a comma-separated";
42 qDebug() << " list of plugin names";
43 qDebug() << " -exclude-plugin-types=<list> : Do not deploy plugins of the given types, a comma-separated";
44 qDebug() << " list of plugin types such as imageformats,iconengines";
45 qDebug() << " -include-plugin-types=<list> : Also deploy all plugins of the given types";
46 qDebug() << " -fs=<filesystem> : Set the filesystem used for the .dmg disk image (defaults to HFS+)";
47 qDebug() << "";
48 qDebug() << "macdeployqt takes an application bundle as input and makes it";
49 qDebug() << "self-contained by copying in the Qt frameworks and plugins that";
50 qDebug() << "the application uses.";
51 qDebug() << "";
52 qDebug() << "Plugins related to a framework are copied in with the";
53 qDebug() << "framework. The accessibility, image formats, and text codec";
54 qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified.";
55 qDebug() << "";
56 qDebug() << "Qt plugins may use private API and will cause the app to be";
57 qDebug() << "rejected from the Mac App store. macdeployqt will print a warning";
58 qDebug() << "when known incompatible plugins are deployed. Use -appstore-compliant ";
59 qDebug() << "to skip these plugins. Currently two SQL plugins are known to";
60 qDebug() << "be incompatible: qsqlodbc and qsqlpsql.";
61 qDebug() << "";
62 qDebug() << "See the \"Qt for macOS - Deployment\" topic in the";
63 qDebug() << "documentation for more information about deployment on macOS.";
64
65 return 1;
66 }
67
68 appBundlePath = QDir::cleanPath(appBundlePath);
69
70 if (!QDir(appBundlePath).exists()) {
71 qDebug() << "Error: Could not find app bundle" << appBundlePath;
72 return 1;
73 }
74
75 bool plugins = true;
76 bool dmg = false;
77 QByteArray filesystem("HFS+");
78 bool useDebugLibs = false;
79 extern bool runStripEnabled;
80 extern bool alwaysOwerwriteEnabled;
82 QStringList additionalExecutables;
83 bool qmldirArgumentUsed = false;
84 QStringList qmlDirs;
85 QStringList qmlImportPaths;
86 extern bool runCodesign;
87 QString codesignIdentity = QStringLiteral("-");
88 extern bool hardenedRuntime;
89 bool noCodesignExplicit = false;
90 extern bool appstoreCompliant;
91 extern bool deployFramework;
92 extern bool secureTimestamp;
93
94 for (int i = 2; i < argc; ++i) {
95 QByteArray argument = QByteArray(argv[i]);
96 if (argument == QByteArrayView("-no-plugins")) {
97 LogDebug() << "Argument found:" << argument;
98 plugins = false;
99 } else if (argument == QByteArrayView("-dmg")) {
100 LogDebug() << "Argument found:" << argument;
101 dmg = true;
102 } else if (argument == QByteArrayView("-no-strip")) {
103 LogDebug() << "Argument found:" << argument;
104 runStripEnabled = false;
105 } else if (argument == QByteArrayView("-use-debug-libs")) {
106 LogDebug() << "Argument found:" << argument;
107 useDebugLibs = true;
108 runStripEnabled = false;
109 } else if (argument.startsWith(QByteArrayView("-verbose"))) {
110 LogDebug() << "Argument found:" << argument;
111 int index = argument.indexOf('=');
112 bool ok = false;
113 int number = argument.mid(index+1).toInt(&ok);
114 if (!ok) {
115 LogError() << "Could not parse verbose level";
116 return 1;
117 } else {
118 logLevel = number;
119 }
120 } else if (argument.startsWith(QByteArrayView("-executable"))) {
121 LogDebug() << "Argument found:" << argument;
122 int index = argument.indexOf('=');
123 if (index == -1) {
124 LogError() << "Missing executable path";
125 return 1;
126 } else {
127 additionalExecutables << argument.mid(index+1);
128 }
129 } else if (argument.startsWith(QByteArrayView("-qmldir"))) {
130 LogDebug() << "Argument found:" << argument;
131 qmldirArgumentUsed = true;
132 int index = argument.indexOf('=');
133 if (index == -1) {
134 LogError() << "Missing qml directory path";
135 return 1;
136 } else {
137 qmlDirs << argument.mid(index+1);
138 }
139 } else if (argument.startsWith(QByteArrayView("-qmlimport"))) {
140 LogDebug() << "Argument found:" << argument;
141 int index = argument.indexOf('=');
142 if (index == -1) {
143 LogError() << "Missing qml import path";
144 return 1;
145 } else {
146 qmlImportPaths << argument.mid(index+1);
147 }
148 } else if (argument.startsWith(QByteArrayView("-include-plugin-types"))) {
149 LogDebug() << "Argument found:" << argument;
150 int index = argument.indexOf('=');
151 if (index == -1) {
152 LogError() << "Missing plugin type list";
153 return 1;
154 }
155 includedPluginTypes += QString::fromLocal8Bit(argument.mid(index + 1))
156 .split(u',', Qt::SkipEmptyParts);
157 } else if (argument.startsWith(QByteArrayView("-exclude-plugin-types"))) {
158 LogDebug() << "Argument found:" << argument;
159 int index = argument.indexOf('=');
160 if (index == -1) {
161 LogError() << "Missing plugin type list";
162 return 1;
163 }
164 excludedPluginTypes += QString::fromLocal8Bit(argument.mid(index + 1))
165 .split(u',', Qt::SkipEmptyParts);
166 } else if (argument.startsWith(QByteArrayView("-include-plugins"))) {
167 LogDebug() << "Argument found:" << argument;
168 int index = argument.indexOf('=');
169 if (index == -1) {
170 LogError() << "Missing plugin list";
171 return 1;
172 }
173 includedPlugins += QString::fromLocal8Bit(argument.mid(index + 1))
174 .split(u',', Qt::SkipEmptyParts);
175 } else if (argument.startsWith(QByteArrayView("-exclude-plugins"))) {
176 LogDebug() << "Argument found:" << argument;
177 int index = argument.indexOf('=');
178 if (index == -1) {
179 LogError() << "Missing plugin list";
180 return 1;
181 }
182 excludedPlugins += QString::fromLocal8Bit(argument.mid(index + 1))
183 .split(u',', Qt::SkipEmptyParts);
184 } else if (argument.startsWith(QByteArrayView("-libpath"))) {
185 LogDebug() << "Argument found:" << argument;
186 int index = argument.indexOf('=');
187 if (index == -1) {
188 LogError() << "Missing library search path";
189 return 1;
190 } else {
191 librarySearchPath << argument.mid(index+1);
192 }
193 } else if (argument == QByteArrayView("-always-overwrite")) {
194 LogDebug() << "Argument found:" << argument;
196 } else if (argument == QByteArrayView("-no-codesign")) {
197 LogDebug() << "Argument found:" << argument;
198 runCodesign = false;
199 noCodesignExplicit = true;
200 } else if (argument.startsWith(QByteArrayView("-codesign"))) {
201 LogDebug() << "Argument found:" << argument;
202 if (noCodesignExplicit) {
203 LogError() << "Error: -no-codesign cannot be combined with -codesign\n";
204 return 1;
205 }
206 int index = argument.indexOf('=');
207 if (index < 0 || index >= argument.size()) {
208 LogError() << "Missing code signing identity";
209 return 1;
210 } else {
211 runCodesign = true;
212 codesignIdentity = argument.mid(index + 1);
213 }
214 } else if (argument.startsWith(QByteArrayView("-sign-for-notarization"))) {
215 LogDebug() << "Argument found:" << argument;
216 if (noCodesignExplicit) {
217 LogError() << "Error: -no-codesign cannot be combined with -sign-for-notarization\n";
218 return 1;
219 }
220 int index = argument.indexOf('=');
221 if (index < 0 || index >= argument.size()) {
222 LogError() << "Missing code signing identity";
223 return 1;
224 } else {
225 runCodesign = true;
226 hardenedRuntime = true;
227 secureTimestamp = true;
228 codesignIdentity = argument.mid(index + 1);
229 }
230 } else if (argument.startsWith(QByteArrayView("-hardened-runtime"))) {
231 LogDebug() << "Argument found:" << argument;
232 hardenedRuntime = true;
233 } else if (argument.startsWith(QByteArrayView("-timestamp"))) {
234 LogDebug() << "Argument found:" << argument;
235 secureTimestamp = true;
236 } else if (argument == QByteArrayView("-appstore-compliant")) {
237 LogDebug() << "Argument found:" << argument;
238 appstoreCompliant = true;
239
240 // Undocumented option, may not work as intended
241 } else if (argument == QByteArrayView("-deploy-framework")) {
242 LogDebug() << "Argument found:" << argument;
243 deployFramework = true;
244
245 } else if (argument.startsWith(QByteArrayView("-fs"))) {
246 LogDebug() << "Argument found:" << argument;
247 int index = argument.indexOf('=');
248 if (index == -1) {
249 LogError() << "Missing filesystem type";
250 return 1;
251 } else {
252 filesystem = argument.mid(index+1);
253 }
254 } else if (argument.startsWith('-')) {
255 LogError() << "Unknown argument" << argument << "\n";
256 return 1;
257 }
258 }
259
260 DeploymentInfo deploymentInfo = deployQtFrameworks(appBundlePath, additionalExecutables, useDebugLibs);
261
262 if (deploymentInfo.isDebug)
263 useDebugLibs = true;
264
265 if (deployFramework && deploymentInfo.isFramework)
266 fixupFramework(appBundlePath);
267
268 // Convenience: Look for .qml files in the current directory if no -qmldir specified.
269 if (qmlDirs.isEmpty()) {
270 using namespace Qt::StringLiterals;
271 using F = QDirListing::IteratorFlag;
272 QDirListing lister(QDir::currentPath(), QStringList(u"*.qml"_s), F::FilesOnly);
273 if (lister.cbegin() != lister.cend())
274 qmlDirs += QStringLiteral(".");
275 }
276
277 if (!qmlDirs.isEmpty()) {
278 bool ok = deployQmlImports(appBundlePath, deploymentInfo, qmlDirs, qmlImportPaths);
279 if (!ok && qmldirArgumentUsed)
280 return 1; // exit if the user explicitly asked for qml import deployment
281
282 // Update deploymentInfo.deployedFrameworks - the QML imports
283 // may have brought in extra frameworks as dependencies.
284 deploymentInfo.deployedFrameworks += findAppFrameworkNames(appBundlePath);
285 deploymentInfo.deployedFrameworks =
286 QSet<QString>(deploymentInfo.deployedFrameworks.begin(),
287 deploymentInfo.deployedFrameworks.end()).values();
288 }
289
290 // Handle plugins
291 if (plugins) {
292 // Set the plugins search directory
293 QStringList pluginPaths = QLibraryInfo::paths(QLibraryInfo::PluginsPath);
294
295 // Sanity checks
296 if (pluginPaths.isEmpty()) {
297 LogError() << "Missing Qt plugins path\n";
298 return 1;
299 }
300
301 deploymentInfo.pluginPath = pluginPaths.takeFirst();
302 if (!QDir(deploymentInfo.pluginPath).exists()) {
303 LogError() << "Plugins path does not exist" << deploymentInfo.pluginPath << "\n";
304 return 1;
305 }
306
307 // Deploy plugins
308 Q_ASSERT(!deploymentInfo.pluginPath.isEmpty());
309 if (!deploymentInfo.pluginPath.isEmpty()) {
310 LogNormal();
311 deployPlugins(appBundlePath, deploymentInfo, useDebugLibs);
312 }
313 }
314
315 if (runStripEnabled)
316 stripAppBinary(appBundlePath);
317
318 if (runCodesign)
319 codesign(codesignIdentity, appBundlePath);
320
321 if (dmg) {
322 LogNormal();
323 createDiskImage(appBundlePath, filesystem);
324 }
325
326 return 0;
327}
bool isDebug
Definition shared.h:89
bool isFramework
Definition shared.h:88
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]