20constexpr QLatin1StringView kOptSystemId = QLatin1StringView(
"system-id");
21constexpr QLatin1StringView kOptClearSettings = QLatin1StringView(
"clear-settings");
22constexpr QLatin1StringView kOptClearCache = QLatin1StringView(
"clear-cache");
23constexpr QLatin1StringView kOptLogging = QLatin1StringView(
"logging");
24constexpr QLatin1StringView kOptLogOutput = QLatin1StringView(
"log-output");
25constexpr QLatin1StringView kOptSimpleBoot = QLatin1StringView(
"simple-boot-test");
27#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
29constexpr QLatin1StringView kOptFakeMobile = QLatin1StringView(
"fake-mobile");
30constexpr QLatin1StringView kOptAllowMultiple = QLatin1StringView(
"allow-multiple");
33#ifdef QGC_UNITTEST_BUILD
35constexpr QLatin1StringView kOptUnittest = QLatin1StringView(
"unittest");
36constexpr QLatin1StringView kOptUnittestStress = QLatin1StringView(
"unittest-stress");
37constexpr QLatin1StringView kOptUnittestOutput = QLatin1StringView(
"unittest-output");
38constexpr QLatin1StringView kOptUnittestLabel = QLatin1StringView(
"label");
39constexpr QLatin1StringView kOptListTests = QLatin1StringView(
"list-tests");
44constexpr QLatin1StringView kOptDesktop = QLatin1StringView(
"desktop");
45constexpr QLatin1StringView kOptNoWinAssertUI = QLatin1StringView(
"no-windows-assert-ui");
48#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
50constexpr QLatin1StringView kOptSwrast = QLatin1StringView(
"swrast");
62static QStringList normalizeArgs(
const QStringList &args)
65 out.reserve(args.size() + 4);
67 for (
const QString &arg : args) {
68#ifdef QGC_UNITTEST_BUILD
70 if (arg == QLatin1String(
"--unittest")) {
71 out << arg << QString();
77 if (arg.startsWith(QLatin1String(
"--")) && arg.contains(QLatin1Char(
':'))) {
78 const qsizetype idx = arg.indexOf(QLatin1Char(
':'));
79 const QString opt = arg.left(idx);
80 const QString val = arg.mid(idx + 1);
85#ifdef QGC_UNITTEST_BUILD
86 else if (opt == QLatin1String(
"--unittest")) {
95 qCDebug(QGCCommandLineParserLog) <<
"Normalized arguments:" << out;
103CommandLineParseResult parseCommandLine()
106 QCoreApplication::setApplicationName(QLatin1String(QGC_APP_NAME));
107 QCoreApplication::setApplicationVersion(QLatin1String(QGC_APP_VERSION_STR));
109 CommandLineParseResult out{};
110 out.parser = std::make_unique<QCommandLineParser>();
112 QCommandLineParser& parser = *out.parser;
113 parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
114 parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsOptions);
115 parser.setApplicationDescription(QStringLiteral(QGC_APP_DESCRIPTION));
118 const QCommandLineOption helpOption = parser.addHelpOption();
119 const QCommandLineOption versionOption = parser.addVersionOption();
122 const QCommandLineOption systemIdOpt(
123 QString(kOptSystemId),
124 QCoreApplication::translate(
"main",
"MAVLink GCS system id."),
125 QCoreApplication::translate(
"main",
"id"));
126 (void) parser.addOption(systemIdOpt);
128 const QCommandLineOption clearSettingsOpt(
129 QString(kOptClearSettings),
130 QCoreApplication::translate(
"main",
"Clear stored application settings."));
131 (void) parser.addOption(clearSettingsOpt);
133 const QCommandLineOption clearCacheOpt(
134 QString(kOptClearCache),
135 QCoreApplication::translate(
"main",
"Clear parameter and airframe caches."));
136 (void) parser.addOption(clearCacheOpt);
138 const QCommandLineOption loggingOpt(
139 QString(kOptLogging),
140 QCoreApplication::translate(
"main",
"Enable logging with optional rules string."),
141 QCoreApplication::translate(
"main",
"rules"));
142 (void) parser.addOption(loggingOpt);
144 const QCommandLineOption logOutputOpt(
145 QString(kOptLogOutput),
146 QCoreApplication::translate(
"main",
"Log to console."));
147 (void) parser.addOption(logOutputOpt);
149 const QCommandLineOption simpleBootOpt(
150 QString(kOptSimpleBoot),
151 QCoreApplication::translate(
"main",
"Initialize subsystems and exit."));
152 (void) parser.addOption(simpleBootOpt);
154#ifdef QGC_UNITTEST_BUILD
156 const QCommandLineOption unittestOpt(
157 QString(kOptUnittest),
158 QCoreApplication::translate(
"main",
"Run unit tests (optional filter value)."),
159 QCoreApplication::translate(
"main",
"filter"));
160 (void) parser.addOption(unittestOpt);
162 const QCommandLineOption unittestStressOpt(
163 QString(kOptUnittestStress),
164 QCoreApplication::translate(
"main",
"Stress unit tests."),
165 QCoreApplication::translate(
"main",
"count"));
166 (void) parser.addOption(unittestStressOpt);
168 const QCommandLineOption unittestOutputOpt(
169 QString(kOptUnittestOutput),
170 QCoreApplication::translate(
"main",
"Output test results to file (JUnit XML format)."),
171 QCoreApplication::translate(
"main",
"file"));
172 (void) parser.addOption(unittestOutputOpt);
174 const QCommandLineOption unittestLabelOpt(
175 QString(kOptUnittestLabel),
176 QCoreApplication::translate(
"main",
"Filter tests by label (unit, integration, vehicle, missionmanager, etc.)."),
177 QCoreApplication::translate(
"main",
"labels"));
178 (void) parser.addOption(unittestLabelOpt);
180 const QCommandLineOption listTestsOpt(
181 QString(kOptListTests),
182 QCoreApplication::translate(
"main",
"List available unit tests and exit."));
183 (void) parser.addOption(listTestsOpt);
186#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
188 const QCommandLineOption fakeMobileOpt(
189 QString(kOptFakeMobile),
190 QCoreApplication::translate(
"main",
"Run with mobile-style UI."));
191 (void) parser.addOption(fakeMobileOpt);
193 const QCommandLineOption allowMultipleOpt(
194 QString(kOptAllowMultiple),
195 QCoreApplication::translate(
"main",
"Bypass single-instance guard."));
196 (void) parser.addOption(allowMultipleOpt);
199#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
201 const QCommandLineOption swrastOpt(
203 QCoreApplication::translate(
"main",
"Force software OpenGL."));
204 (void) parser.addOption(swrastOpt);
209 const QCommandLineOption desktopOpt(
210 QString(kOptDesktop),
211 QCoreApplication::translate(
"main",
"Force Desktop OpenGL."));
212 (void) parser.addOption(desktopOpt);
214 const QCommandLineOption quietWinAssertOpt(
215 QString(kOptNoWinAssertUI),
216 QCoreApplication::translate(
"main",
"Disable Windows assert dialog boxes."));
217 (void) parser.addOption(quietWinAssertOpt);
221 const QStringList normalizedArgs = normalizeArgs(QCoreApplication::arguments());
222 parser.process(normalizedArgs);
225 out.unknownOptions = parser.unknownOptionNames();
228 if (!out.unknownOptions.isEmpty()) {
230 if (out.unknownOptions.contains(QLatin1String(
"unittest")) ||
231 out.unknownOptions.contains(QLatin1String(
"unittest-stress")) ||
232 out.unknownOptions.contains(QLatin1String(
"unittest-output")) ||
233 out.unknownOptions.contains(QLatin1String(
"list-tests"))) {
234#ifndef QGC_UNITTEST_BUILD
235 out.statusCode = CommandLineParseResult::Status::Error;
236 out.errorString = QCoreApplication::translate(
"main",
237 "--unittest/--unittest-stress/--unittest-output/--list-tests options are only available in unittest builds.");
238 qCWarning(QGCCommandLineParserLog) << out.errorString.value();
243#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
245 if (out.unknownOptions.contains(QLatin1String(
"fake-mobile")) ||
246 out.unknownOptions.contains(QLatin1String(
"allow-multiple"))) {
247 out.statusCode = CommandLineParseResult::Status::Error;
248 out.errorString = QCoreApplication::translate(
"main",
249 "--fake-mobile/--allow-multiple are not supported on mobile platforms.");
250 qCWarning(QGCCommandLineParserLog) << out.errorString.value();
257 if (out.unknownOptions.contains(QLatin1String(
"desktop")) ||
258 out.unknownOptions.contains(QLatin1String(
"no-windows-assert-ui"))) {
259 out.statusCode = CommandLineParseResult::Status::Error;
260 out.errorString = QCoreApplication::translate(
"main",
261 "--desktop/--no-windows-assert-ui are only supported on Windows.");
262 qCWarning(QGCCommandLineParserLog) << out.errorString.value();
267#if !defined(Q_OS_WIN) && !defined(Q_OS_MACOS)
269 if (out.unknownOptions.contains(QLatin1String(
"swrast"))) {
270 out.statusCode = CommandLineParseResult::Status::Error;
271 out.errorString = QCoreApplication::translate(
"main",
272 "--swrast is only supported on Windows and macOS.");
273 qCWarning(QGCCommandLineParserLog) << out.errorString.value();
279 out.statusCode = CommandLineParseResult::Status::Error;
280 out.errorString = QCoreApplication::translate(
"main",
"Unknown options: %1")
281 .arg(out.unknownOptions.join(QLatin1String(
", ")));
282 qCWarning(QGCCommandLineParserLog) << out.errorString.value();
287 out.positional = parser.positionalArguments();
288 if (!out.positional.isEmpty()) {
289 out.statusCode = CommandLineParseResult::Status::Error;
290 out.errorString = QCoreApplication::translate(
"main",
"Unexpected positional arguments: %1")
291 .arg(out.positional.join(QLatin1String(
", ")));
292 qCWarning(QGCCommandLineParserLog) << out.errorString.value();
297 if (parser.isSet(helpOption)) {
298 out.statusCode = CommandLineParseResult::Status::HelpRequested;
299 out.helpText = parser.helpText();
303 if (parser.isSet(versionOption)) {
304 out.statusCode = CommandLineParseResult::Status::VersionRequested;
305 out.versionText = QCoreApplication::applicationVersion();
310 if (parser.isSet(systemIdOpt)) {
311 const QString systemIdStr = parser.value(systemIdOpt);
313 const uint systemId = systemIdStr.toUInt(&ok);
314 if (!ok || (systemId < 1) || (systemId > 255)) {
315 out.statusCode = CommandLineParseResult::Status::Error;
316 out.errorString = QCoreApplication::translate(
"main",
"Invalid System ID (must be 1-255): %1")
318 qCWarning(QGCCommandLineParserLog) << out.errorString.value();
321 out.systemId =
static_cast<quint8
>(systemId);
322 qCDebug(QGCCommandLineParserLog) <<
"System ID:" << systemId;
325 out.clearSettingsOptions = parser.isSet(clearSettingsOpt);
326 out.clearCache = parser.isSet(clearCacheOpt);
327 if (parser.isSet(loggingOpt)) {
328 out.loggingOptions = parser.value(loggingOpt);
329 qCDebug(QGCCommandLineParserLog) <<
"Logging options:" << out.loggingOptions.value();
331 out.logOutput = parser.isSet(logOutputOpt);
332 out.simpleBootTest = parser.isSet(simpleBootOpt);
334#ifdef QGC_UNITTEST_BUILD
336 if (parser.isSet(unittestOpt)) {
337 out.runningUnitTests =
true;
338 const QStringList vals = parser.values(unittestOpt);
340 for (
const QString& val : vals) {
341 if (!val.isEmpty()) {
342 out.unitTests.append(val);
345 qCDebug(QGCCommandLineParserLog) <<
"Unit tests:" << (out.unitTests.isEmpty() ? QStringLiteral(
"all") : out.unitTests.join(QLatin1String(
", ")));
348 if (parser.isSet(unittestStressOpt)) {
349 out.runningUnitTests =
true;
350 out.stressUnitTests =
true;
351 const QString stress = parser.value(unittestStressOpt);
352 if (stress.isEmpty()) {
353 out.stressUnitTestsCount = 20;
356 const uint count = stress.toUInt(&ok);
357 if (!ok || (count == 0)) {
358 out.statusCode = CommandLineParseResult::Status::Error;
359 out.errorString = QCoreApplication::translate(
"main",
"Invalid stress test count (must be > 0): %1")
361 qCWarning(QGCCommandLineParserLog) << out.errorString.value();
364 out.stressUnitTestsCount = count;
366 qCDebug(QGCCommandLineParserLog) <<
"Stress test iterations:" << out.stressUnitTestsCount;
369 if (parser.isSet(unittestOutputOpt)) {
370 out.unitTestOutput = parser.value(unittestOutputOpt);
371 qCDebug(QGCCommandLineParserLog) <<
"Test output file:" << out.unitTestOutput.value();
374 if (parser.isSet(unittestLabelOpt)) {
375 out.labelFilter = parser.value(unittestLabelOpt);
376 qCDebug(QGCCommandLineParserLog) <<
"Label filter:" << out.labelFilter.value();
379 out.listTests = parser.isSet(listTestsOpt);
381 qCDebug(QGCCommandLineParserLog) <<
"List tests requested";
386#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
387 out.fakeMobile = parser.isSet(fakeMobileOpt);
388 out.allowMultiple = parser.isSet(allowMultipleOpt);
390 out.fakeMobile =
false;
391 out.allowMultiple =
false;
396 out.useDesktopGL = parser.isSet(desktopOpt);
397 out.quietWindowsAsserts = parser.isSet(quietWinAssertOpt);
399 out.useDesktopGL =
false;
400 out.quietWindowsAsserts =
false;
403#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
404 out.useSwRast = parser.isSet(swrastOpt);
406 out.useSwRast =
false;
409 out.statusCode = CommandLineParseResult::Status::Ok;
410 qCDebug(QGCCommandLineParserLog) <<
"Command line parsing completed successfully";
415CommandLineParseResult parse(
int argc,
char* argv[])
417#ifdef QGC_UNITTEST_BUILD
418 overrideCommandLine(argc, argv);
420 const QCoreApplication app(argc, argv);
421 return parseCommandLine();
424std::optional<int> handleParseResult(
const CommandLineParseResult& result)
426 switch (result.statusCode) {
427 case CommandLineParseResult::Status::Error: {
428 const QString msg = result.errorString.value_or(QStringLiteral(
"Unknown error"));
429 QCommandLineParser::showMessageAndExit(QCommandLineParser::MessageType::Error, msg, 1);
432 case CommandLineParseResult::Status::HelpRequested:
433 case CommandLineParseResult::Status::VersionRequested:
435 case CommandLineParseResult::Status::Ok:
441AppMode determineAppMode(
const CommandLineParseResult& args)
443#ifdef QGC_UNITTEST_BUILD
444 if (args.listTests) {
445 return AppMode::ListTests;
447 if (args.runningUnitTests) {
448 return AppMode::Test;
451 if (args.simpleBootTest) {
452 return AppMode::BootTest;
457#ifdef QGC_UNITTEST_BUILD
458void overrideCommandLine(
int& argc,
char**& argv)
460 constexpr bool enabled =
false;
461 if constexpr (!enabled) {
465 static char arg0[256];
466 static char arg1[] =
"--unittest:FTPManagerTest";
467 static char arg2[] =
"--logging:Vehicle.FTPManager";
468 static char* newArgv[] = { arg0, arg1, arg2,
nullptr };
470 qstrncpy(arg0, argv[0],
sizeof(arg0));