1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "makefileparse.h"
27 
28 #include <qtsupport/qtversionmanager.h>
29 #include <qtsupport/baseqtversion.h>
30 #include <utils/qtcprocess.h>
31 
32 #include <QDebug>
33 #include <QDir>
34 #include <QFileInfo>
35 #include <QRegularExpression>
36 #include <QTextStream>
37 #include <QLoggingCategory>
38 
39 using namespace ProjectExplorer;
40 using namespace Utils;;
41 
42 using QtSupport::QtVersionManager;
43 using QtSupport::BaseQtVersion;
44 
45 namespace QmakeProjectManager {
46 namespace Internal {
47 
48 static QString findQMakeLine(const QString &makefile, const QString &key)
49 {
50     QFile fi(makefile);
fi.existsnull51     if (fi.exists() && fi.open(QFile::ReadOnly)) {
52         QTextStream ts(&fi);
53         while (!ts.atEnd()) {
54             const QString line = ts.readLine();
55             if (line.startsWith(key))
56                 return line;
57         }
58     }
QStringnull59     return QString();
60 }
61 
trimsnull62 /// This function trims the "#Command /path/to/qmake" from the line
63 static QString trimLine(const QString &line)
64 {
65 
66     // Actually the first space after #Command: /path/to/qmake
67     const int firstSpace = line.indexOf(QLatin1Char(' '), 11);
68     return line.mid(firstSpace).trimmed();
69 }
70 
71 void MakeFileParse::parseArgs(const QString &args, const QString &project,
72                               QList<QMakeAssignment> *assignments,
73                               QList<QMakeAssignment> *afterAssignments)
74 {
75     const QRegularExpression regExp(QLatin1String("^([^\\s\\+-]*)\\s*(\\+=|=|-=|~=)(.*)$"));
76     bool after = false;
77     bool ignoreNext = false;
78     m_unparsedArguments = args;
79     ProcessArgs::ArgIterator ait(&m_unparsedArguments);
ait.nextnull80     while (ait.next()) {
81         if (ignoreNext) {
82             // Ignoring
83             ignoreNext = false;
84             ait.deleteArg();
85         } else if (ait.value() == project) {
86             ait.deleteArg();
87         } else if (ait.value() == QLatin1String("-after")) {
88             after = true;
89             ait.deleteArg();
90         } else if (ait.value().contains(QLatin1Char('='))) {
91             const QRegularExpressionMatch match = regExp.match(ait.value());
92             if (match.hasMatch()) {
93                 QMakeAssignment qa;
94                 qa.variable = match.captured(1);
95                 qa.op = match.captured(2);
96                 qa.value = match.captured(3).trimmed();
97                 if (after)
98                     afterAssignments->append(qa);
99                 else
100                     assignments->append(qa);
101             } else {
102                 qDebug()<<"regexp did not match";
103             }
104             ait.deleteArg();
105         } else if (ait.value() == QLatin1String("-o")) {
106             ignoreNext = true;
107             ait.deleteArg();
108 #if defined(Q_OS_WIN32)
109         } else if (ait.value() == QLatin1String("-win32")) {
110 #elif defined(Q_OS_MAC)
111         } else if (ait.value() == QLatin1String("-macx")) {
112 #elif defined(Q_OS_QNX6)
113         } else if (ait.value() == QLatin1String("-qnx6")) {
114 #else
115         } else if (ait.value() == QLatin1String("-unix")) {
116 #endif
117             ait.deleteArg();
118         }
119     }
120 }
121 
122 void dumpQMakeAssignments(const QList<QMakeAssignment> &list)
123 {
124     foreach (const QMakeAssignment &qa, list) {
125         qCDebug(MakeFileParse::logging()) << "    " << qa.variable << qa.op << qa.value;
126     }
127 }
128 
129 QList<QMakeAssignment> MakeFileParse::parseAssignments(const QList<QMakeAssignment> &assignments)
130 {
131     bool foundSeparateDebugInfo = false;
132     bool foundForceDebugInfo = false;
133     QList<QMakeAssignment> filteredAssignments;
134     foreach (const QMakeAssignment &qa, assignments) {
135         if (qa.variable == QLatin1String("CONFIG")) {
136             QStringList values = qa.value.split(QLatin1Char(' '));
137             QStringList newValues;
138             foreach (const QString &value, values) {
139                 if (value == QLatin1String("debug")) {
140                     if (qa.op == QLatin1String("+=")) {
141                         m_qmakeBuildConfig.explicitDebug = true;
142                         m_qmakeBuildConfig.explicitRelease = false;
143                     } else {
144                         m_qmakeBuildConfig.explicitDebug = false;
145                         m_qmakeBuildConfig.explicitRelease = true;
146                     }
147                 } else if (value == QLatin1String("release")) {
148                     if (qa.op == QLatin1String("+=")) {
149                         m_qmakeBuildConfig.explicitDebug = false;
150                         m_qmakeBuildConfig.explicitRelease = true;
151                     } else {
152                         m_qmakeBuildConfig.explicitDebug = true;
153                         m_qmakeBuildConfig.explicitRelease = false;
154                     }
155                 } else if (value == QLatin1String("debug_and_release")) {
156                     if (qa.op == QLatin1String("+=")) {
157                         m_qmakeBuildConfig.explicitBuildAll = true;
158                         m_qmakeBuildConfig.explicitNoBuildAll = false;
159                     } else {
160                         m_qmakeBuildConfig.explicitBuildAll = false;
161                         m_qmakeBuildConfig.explicitNoBuildAll = true;
162                     }
163                 } else if (value == QLatin1String("iphonesimulator")) {
164                     if (qa.op == QLatin1String("+="))
165                         m_config.osType = QMakeStepConfig::IphoneSimulator;
166                     else
167                         m_config.osType = QMakeStepConfig::NoOsType;
168                 } else if (value == QLatin1String("iphoneos")) {
169                     if (qa.op == QLatin1String("+="))
170                         m_config.osType = QMakeStepConfig::IphoneOS;
171                     else
172                         m_config.osType = QMakeStepConfig::NoOsType;
173                 } else if (value == QLatin1String("qml_debug")) {
174                     if (qa.op == QLatin1String("+="))
175                         m_config.linkQmlDebuggingQQ2 = TriState::Enabled;
176                     else
177                         m_config.linkQmlDebuggingQQ2 = TriState::Disabled;
178                 } else if (value == QLatin1String("qtquickcompiler")) {
179                     if (qa.op == QLatin1String("+="))
180                         m_config.useQtQuickCompiler = TriState::Enabled;
181                     else
182                         m_config.useQtQuickCompiler = TriState::Disabled;
183                 } else if (value == QLatin1String("force_debug_info")) {
184                     if (qa.op == QLatin1String("+="))
185                         foundForceDebugInfo = true;
186                     else
187                         foundForceDebugInfo = false;
188                 } else if (value == QLatin1String("separate_debug_info")) {
189                     if (qa.op == QLatin1String("+=")) {
190                         foundSeparateDebugInfo = true;
191                         m_config.separateDebugInfo = TriState::Enabled;
192                     } else {
193                         foundSeparateDebugInfo = false;
194                         m_config.separateDebugInfo = TriState::Disabled;
195                     }
196                 } else {
197                     newValues.append(value);
198                 }
199             }
200             if (!newValues.isEmpty()) {
201                 QMakeAssignment newQA = qa;
202                 newQA.value = newValues.join(QLatin1Char(' '));
203                 filteredAssignments.append(newQA);
204             }
205         } else {
206             filteredAssignments.append(qa);
207         }
208     }
209 
210     if (foundForceDebugInfo && foundSeparateDebugInfo) {
211         m_config.separateDebugInfo = TriState::Enabled;
212     } else if (foundForceDebugInfo) {
213         // Found only force_debug_info, so readd it
214         QMakeAssignment newQA;
215         newQA.variable = QLatin1String("CONFIG");
216         newQA.op = QLatin1String("+=");
217         newQA.value = QLatin1String("force_debug_info");
218         filteredAssignments.append(newQA);
219     } else if (foundSeparateDebugInfo) {
220         // Found only separate_debug_info, so readd it
221         QMakeAssignment newQA;
222         newQA.variable = QLatin1String("CONFIG");
223         newQA.op = QLatin1String("+=");
224         newQA.value = QLatin1String("separate_debug_info");
225         filteredAssignments.append(newQA);
226     }
227     return filteredAssignments;
228 }
229 
230 static FilePath findQMakeBinaryFromMakefile(const QString &makefile)
231 {
232     QFile fi(makefile);
233     if (fi.exists() && fi.open(QFile::ReadOnly)) {
234         QTextStream ts(&fi);
235         const QRegularExpression r1(QLatin1String("^QMAKE\\s*=(.*)$"));
236         while (!ts.atEnd()) {
237             QString line = ts.readLine();
238             const QRegularExpressionMatch match = r1.match(line);
239             if (match.hasMatch()) {
240                 QFileInfo qmake(match.captured(1).trimmed());
241                 QString qmakePath = qmake.filePath();
242                 if (!QString::fromLatin1(QTC_HOST_EXE_SUFFIX).isEmpty()
243                         && !qmakePath.endsWith(QLatin1String(QTC_HOST_EXE_SUFFIX))) {
244                     qmakePath.append(QLatin1String(QTC_HOST_EXE_SUFFIX));
245                 }
246                 // Is qmake still installed?
247                 QFileInfo fi(qmakePath);
248                 if (fi.exists())
249                     return FilePath::fromFileInfo(fi);
250             }
251         }
252     }
253     return FilePath();
254 }
255 
256 MakeFileParse::MakeFileParse(const QString &makefile, Mode mode) : m_mode(mode)
257 {
258     qCDebug(logging()) << "Parsing makefile" << makefile;
259     if (!QFileInfo::exists(makefile)) {
260         qCDebug(logging()) << "**doesn't exist";
261         m_state = MakefileMissing;
262         return;
263     }
264 
265     // Qt Version!
266     m_qmakePath = findQMakeBinaryFromMakefile(makefile);
267     qCDebug(logging()) << "  qmake:" << m_qmakePath;
268 
269     QString project = findQMakeLine(makefile, QLatin1String("# Project:")).trimmed();
270     if (project.isEmpty()) {
271         m_state = CouldNotParse;
272         qCDebug(logging()) << "**No Project line";
273         return;
274     }
275 
276     project.remove(0, project.indexOf(QLatin1Char(':')) + 1);
277     project = project.trimmed();
278 
279     // Src Pro file
280     m_srcProFile = QDir::cleanPath(QFileInfo(makefile).absoluteDir().filePath(project));
281     qCDebug(logging()) << "  source .pro file:" << m_srcProFile;
282 
283     QString command = findQMakeLine(makefile, QLatin1String("# Command:")).trimmed();
284     if (command.isEmpty()) {
285         m_state = CouldNotParse;
286         qCDebug(logging()) << "**No Command line found";
287         return;
288     }
289 
290     command = trimLine(command);
291     parseCommandLine(command, project);
292 
293     m_state = Okay;
294 }
295 
296 MakeFileParse::MakefileState MakeFileParse::makeFileState() const
297 {
298     return m_state;
299 }
300 
301 Utils::FilePath MakeFileParse::qmakePath() const
302 {
303     return m_qmakePath;
304 }
305 
306 QString MakeFileParse::srcProFile() const
307 {
308     return m_srcProFile;
309 }
310 
311 QMakeStepConfig MakeFileParse::config() const
312 {
313     return m_config;
314 }
315 
316 
317 QString MakeFileParse::unparsedArguments() const
318 {
319     return m_unparsedArguments;
320 }
321 
322 BaseQtVersion::QmakeBuildConfigs MakeFileParse::effectiveBuildConfig(BaseQtVersion::QmakeBuildConfigs defaultBuildConfig) const
323 {
324     BaseQtVersion::QmakeBuildConfigs buildConfig = defaultBuildConfig;
325     if (m_qmakeBuildConfig.explicitDebug)
326         buildConfig = buildConfig | BaseQtVersion::DebugBuild;
327     else if (m_qmakeBuildConfig.explicitRelease)
328         buildConfig = buildConfig & ~BaseQtVersion::DebugBuild;
329     if (m_qmakeBuildConfig.explicitBuildAll)
330         buildConfig = buildConfig | BaseQtVersion::BuildAll;
331     else if (m_qmakeBuildConfig.explicitNoBuildAll)
332         buildConfig = buildConfig &~ BaseQtVersion::BuildAll;
333     return buildConfig;
334 }
335 
336 const QLoggingCategory &MakeFileParse::logging()
337 {
338     static const QLoggingCategory category("qtc.qmakeprojectmanager.import", QtWarningMsg);
339     return category;
340 }
341 
342 void MakeFileParse::parseCommandLine(const QString &command, const QString &project)
343 {
344 
345     QList<QMakeAssignment> assignments;
346     QList<QMakeAssignment> afterAssignments;
347     // Split up args into assignments and other arguments, writes m_unparsedArguments
348     parseArgs(command, project, &assignments, &afterAssignments);
349     qCDebug(logging()) << "  Initial assignments:";
350     dumpQMakeAssignments(assignments);
351 
352     // Filter out CONFIG arguments we know into m_qmakeBuildConfig and m_config
353     const QList<QMakeAssignment> filteredAssignments = parseAssignments(assignments);
354     qCDebug(logging()) << "  After parsing";
355     dumpQMakeAssignments(filteredAssignments);
356 
357     qCDebug(logging()) << "  Explicit Debug" << m_qmakeBuildConfig.explicitDebug;
358     qCDebug(logging()) << "  Explicit Release" << m_qmakeBuildConfig.explicitRelease;
359     qCDebug(logging()) << "  Explicit BuildAll" << m_qmakeBuildConfig.explicitBuildAll;
360     qCDebug(logging()) << "  Explicit NoBuildAll" << m_qmakeBuildConfig.explicitNoBuildAll;
361     qCDebug(logging()) << "  OsType" << m_config.osType;
362     qCDebug(logging()) << "  LinkQmlDebuggingQQ2"
363                        << (m_config.linkQmlDebuggingQQ2 == TriState::Enabled);
364     qCDebug(logging()) << "  Qt Quick Compiler"
365                        <<  (m_config.useQtQuickCompiler == TriState::Enabled);
366     qCDebug(logging()) << "  Separate Debug Info"
367                        << (m_config.separateDebugInfo == TriState::Enabled);
368 
369     // Create command line of all unfiltered arguments
370     const QList<QMakeAssignment> &assignmentsToUse = m_mode == Mode::FilterKnownConfigValues
371             ? filteredAssignments : assignments;
372     foreach (const QMakeAssignment &qa, assignmentsToUse)
373         ProcessArgs::addArg(&m_unparsedArguments, qa.variable + qa.op + qa.value);
374     if (!afterAssignments.isEmpty()) {
375         ProcessArgs::addArg(&m_unparsedArguments, QLatin1String("-after"));
376         foreach (const QMakeAssignment &qa, afterAssignments)
377             ProcessArgs::addArg(&m_unparsedArguments, qa.variable + qa.op + qa.value);
378     }
379 }
380 
381 } // Internal
382 } // QmakeProjectManager
383 
384 // Unit tests:
385 
386 #ifdef WITH_TESTS
387 #   include <QTest>
388 
389 #   include "qmakeprojectmanagerplugin.h"
390 
391 #   include "projectexplorer/outputparser_test.h"
392 
393 using namespace QmakeProjectManager::Internal;
394 using namespace ProjectExplorer;
395 
396 void QmakeProjectManagerPlugin::testMakefileParser_data()
397 {
398     QTest::addColumn<QString>("command");
399     QTest::addColumn<QString>("project");
400     QTest::addColumn<QString>("unparsedArguments");
401     QTest::addColumn<int>("archConfig");
402     QTest::addColumn<int>("osType");
403     QTest::addColumn<bool>("linkQmlDebuggingQQ2");
404     QTest::addColumn<bool>("useQtQuickCompiler");
405     QTest::addColumn<bool>("separateDebugInfo");
406     QTest::addColumn<int>("effectiveBuildConfig");
407 
408     QTest::newRow("Qt 5.7")
409             << QString::fromLatin1("-spec linux-g++ CONFIG+=debug CONFIG+=qml_debug -o Makefile ../untitled7/untitled7.pro")
410             << QString::fromLatin1("../untitled7/untitled7.pro")
411             << QString::fromLatin1("-spec linux-g++")
412             << static_cast<int>(QMakeStepConfig::NoArch) << static_cast<int>(QMakeStepConfig::NoOsType)
413             << true << false << false << 2;
414     QTest::newRow("Qt 5.7 extra1")
415             << QString::fromLatin1("SOMETHING=ELSE -spec linux-g++ CONFIG+=debug CONFIG+=qml_debug -o Makefile ../untitled7/untitled7.pro")
416             << QString::fromLatin1("../untitled7/untitled7.pro")
417             << QString::fromLatin1("-spec linux-g++ SOMETHING=ELSE")
418             << static_cast<int>(QMakeStepConfig::NoArch) << static_cast<int>(QMakeStepConfig::NoOsType)
419             << true << false << false << 2;
420     QTest::newRow("Qt 5.7 extra2")
421             << QString::fromLatin1("-spec linux-g++ SOMETHING=ELSE CONFIG+=debug CONFIG+=qml_debug -o Makefile ../untitled7/untitled7.pro")
422             << QString::fromLatin1("../untitled7/untitled7.pro")
423             << QString::fromLatin1("-spec linux-g++ SOMETHING=ELSE")
424             << static_cast<int>(QMakeStepConfig::NoArch) << static_cast<int>(QMakeStepConfig::NoOsType)
425             << true << false << false << 2;
426     QTest::newRow("Qt 5.7 extra3")
427             << QString::fromLatin1("-spec linux-g++ CONFIG+=debug SOMETHING=ELSE CONFIG+=qml_debug -o Makefile ../untitled7/untitled7.pro")
428             << QString::fromLatin1("../untitled7/untitled7.pro")
429             << QString::fromLatin1("-spec linux-g++ SOMETHING=ELSE")
430             << static_cast<int>(QMakeStepConfig::NoArch) << static_cast<int>(QMakeStepConfig::NoOsType)
431             << true << false << false << 2;
432     QTest::newRow("Qt 5.7 extra4")
433             << QString::fromLatin1("-spec linux-g++ CONFIG+=debug CONFIG+=qml_debug SOMETHING=ELSE -o Makefile ../untitled7/untitled7.pro")
434             << QString::fromLatin1("../untitled7/untitled7.pro")
435             << QString::fromLatin1("-spec linux-g++ SOMETHING=ELSE")
436             << static_cast<int>(QMakeStepConfig::NoArch) << static_cast<int>(QMakeStepConfig::NoOsType)
437             << true << false << false << 2;
438     QTest::newRow("Qt 5.7 extra5")
439             << QString::fromLatin1("-spec linux-g++ CONFIG+=debug CONFIG+=qml_debug -o Makefile SOMETHING=ELSE ../untitled7/untitled7.pro")
440             << QString::fromLatin1("../untitled7/untitled7.pro")
441             << QString::fromLatin1("-spec linux-g++ SOMETHING=ELSE")
442             << static_cast<int>(QMakeStepConfig::NoArch) << static_cast<int>(QMakeStepConfig::NoOsType)
443             << true << false << false << 2;
444     QTest::newRow("Qt 5.7 extra6")
445             << QString::fromLatin1("-spec linux-g++ CONFIG+=debug CONFIG+=qml_debug -o Makefile ../untitled7/untitled7.pro SOMETHING=ELSE")
446             << QString::fromLatin1("../untitled7/untitled7.pro")
447             << QString::fromLatin1("-spec linux-g++ SOMETHING=ELSE")
448             << static_cast<int>(QMakeStepConfig::NoArch) << static_cast<int>(QMakeStepConfig::NoOsType)
449             << true << false << false << 2;
450     QTest::newRow("Qt 5.8")
451             << QString::fromLatin1("-o Makefile ../untitled7/untitled7.pro -spec linux-g++ CONFIG+=debug CONFIG+=qml_debug")
452             << QString::fromLatin1("../untitled7/untitled7.pro")
453             << QString::fromLatin1("-spec linux-g++")
454             << static_cast<int>(QMakeStepConfig::NoArch) << static_cast<int>(QMakeStepConfig::NoOsType)
455             << true << false << false << 2;
456     QTest::newRow("Qt 5.8 extra1")
457             << QString::fromLatin1("SOMETHING=ELSE -o Makefile ../untitled7/untitled7.pro -spec linux-g++ CONFIG+=debug CONFIG+=qml_debug")
458             << QString::fromLatin1("../untitled7/untitled7.pro")
459             << QString::fromLatin1("-spec linux-g++ SOMETHING=ELSE")
460             << static_cast<int>(QMakeStepConfig::NoArch) << static_cast<int>(QMakeStepConfig::NoOsType)
461             << true << false << false << 2;
462     QTest::newRow("Qt 5.8 extra2")
463             << QString::fromLatin1("-o Makefile SOMETHING=ELSE ../untitled7/untitled7.pro -spec linux-g++ CONFIG+=debug CONFIG+=qml_debug")
464             << QString::fromLatin1("../untitled7/untitled7.pro")
465             << QString::fromLatin1("-spec linux-g++ SOMETHING=ELSE")
466             << static_cast<int>(QMakeStepConfig::NoArch) << static_cast<int>(QMakeStepConfig::NoOsType)
467             << true << false << false << 2;
468     QTest::newRow("Qt 5.8 extra3")
469             << QString::fromLatin1("-o Makefile ../untitled7/untitled7.pro SOMETHING=ELSE -spec linux-g++ CONFIG+=debug CONFIG+=qml_debug")
470             << QString::fromLatin1("../untitled7/untitled7.pro")
471             << QString::fromLatin1("-spec linux-g++ SOMETHING=ELSE")
472             << static_cast<int>(QMakeStepConfig::NoArch) << static_cast<int>(QMakeStepConfig::NoOsType)
473             << true << false << false << 2;
474     QTest::newRow("Qt 5.8 extra4")
475             << QString::fromLatin1("-o Makefile ../untitled7/untitled7.pro -spec linux-g++ SOMETHING=ELSE CONFIG+=debug CONFIG+=qml_debug")
476             << QString::fromLatin1("../untitled7/untitled7.pro")
477             << QString::fromLatin1("-spec linux-g++ SOMETHING=ELSE")
478             << static_cast<int>(QMakeStepConfig::NoArch) << static_cast<int>(QMakeStepConfig::NoOsType)
479             << true << false << false << 2;
480     QTest::newRow("Qt 5.8 extra5")
481             << QString::fromLatin1("-o Makefile ../untitled7/untitled7.pro -spec linux-g++ CONFIG+=debug SOMETHING=ELSE CONFIG+=qml_debug")
482             << QString::fromLatin1("../untitled7/untitled7.pro")
483             << QString::fromLatin1("-spec linux-g++ SOMETHING=ELSE")
484             << static_cast<int>(QMakeStepConfig::NoArch) << static_cast<int>(QMakeStepConfig::NoOsType)
485             << true << false << false << 2;
486     QTest::newRow("Qt 5.8 extra6")
487             << QString::fromLatin1("-o Makefile ../untitled7/untitled7.pro -spec linux-g++ CONFIG+=debug CONFIG+=qml_debug SOMETHING=ELSE")
488             << QString::fromLatin1("../untitled7/untitled7.pro")
489             << QString::fromLatin1("-spec linux-g++ SOMETHING=ELSE")
490             << static_cast<int>(QMakeStepConfig::NoArch) << static_cast<int>(QMakeStepConfig::NoOsType)
491             << true << false << false << 2;
492 }
493 
494 void QmakeProjectManagerPlugin::testMakefileParser()
495 {
496     QFETCH(QString, command);
497     QFETCH(QString, project);
498     QFETCH(QString, unparsedArguments);
499     QFETCH(int, archConfig);
500     QFETCH(int, osType);
501     QFETCH(bool, linkQmlDebuggingQQ2);
502     QFETCH(bool, useQtQuickCompiler);
503     QFETCH(bool, separateDebugInfo);
504     QFETCH(int, effectiveBuildConfig);
505 
506     MakeFileParse parser("/tmp/something", MakeFileParse::Mode::FilterKnownConfigValues);
507     parser.parseCommandLine(command, project);
508 
509     QCOMPARE(Utils::ProcessArgs::splitArgs(parser.unparsedArguments()),
510              Utils::ProcessArgs::splitArgs(unparsedArguments));
511     QCOMPARE(parser.effectiveBuildConfig({}), effectiveBuildConfig);
512 
513     const QMakeStepConfig qmsc = parser.config();
514     QCOMPARE(qmsc.osType, static_cast<QMakeStepConfig::OsType>(osType));
515     QCOMPARE(qmsc.linkQmlDebuggingQQ2 == TriState::Enabled, linkQmlDebuggingQQ2);
516     QCOMPARE(qmsc.useQtQuickCompiler == TriState::Enabled, useQtQuickCompiler);
517     QCOMPARE(qmsc.separateDebugInfo == TriState::Enabled, separateDebugInfo);
518 }
519 #endif
520