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 "qmakestep.h"
27
28 #include "qmakemakestep.h"
29 #include "qmakebuildconfiguration.h"
30 #include "qmakekitinformation.h"
31 #include "qmakenodes.h"
32 #include "qmakeparser.h"
33 #include "qmakeproject.h"
34 #include "qmakeprojectmanagerconstants.h"
35 #include "qmakesettings.h"
36
37 #include <android/androidconstants.h>
38
39 #include <projectexplorer/buildmanager.h>
40 #include <projectexplorer/buildsteplist.h>
41 #include <projectexplorer/gnumakeparser.h>
42 #include <projectexplorer/processparameters.h>
43 #include <projectexplorer/projectexplorer.h>
44 #include <projectexplorer/runconfigurationaspects.h>
45 #include <projectexplorer/target.h>
46 #include <projectexplorer/toolchain.h>
47
48 #include <coreplugin/icore.h>
49 #include <coreplugin/icontext.h>
50 #include <qtsupport/qtkitinformation.h>
51 #include <qtsupport/qtversionmanager.h>
52 #include <qtsupport/qtsupportconstants.h>
53
54 #include <ios/iosconstants.h>
55
56 #include <utils/algorithm.h>
57 #include <utils/hostosinfo.h>
58 #include <utils/layoutbuilder.h>
59 #include <utils/qtcprocess.h>
60 #include <utils/utilsicons.h>
61 #include <utils/variablechooser.h>
62
63 #include <QDir>
64 #include <QLabel>
65 #include <QListWidget>
66 #include <QMessageBox>
67 #include <QPlainTextEdit>
68
69 using namespace QtSupport;
70 using namespace ProjectExplorer;
71 using namespace Utils;
72
73 using namespace QmakeProjectManager::Internal;
74
75 namespace QmakeProjectManager {
76
77 const char QMAKE_ARGUMENTS_KEY[] = "QtProjectManager.QMakeBuildStep.QMakeArguments";
78 const char QMAKE_FORCED_KEY[] = "QtProjectManager.QMakeBuildStep.QMakeForced";
79 const char QMAKE_SELECTED_ABIS_KEY[] = "QtProjectManager.QMakeBuildStep.SelectedAbis";
80
QMakeStep(BuildStepList * bsl,Id id)81 QMakeStep::QMakeStep(BuildStepList *bsl, Id id)
82 : AbstractProcessStep(bsl, id)
83 {
84 setLowPriority();
85
86 m_buildType = addAspect<SelectionAspect>();
87 m_buildType->setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
88 m_buildType->setDisplayName(tr("qmake build configuration:"));
89 m_buildType->addOption(tr("Debug"));
90 m_buildType->addOption(tr("Release"));
91
92 m_userArgs = addAspect<ArgumentsAspect>();
93 m_userArgs->setSettingsKey(QMAKE_ARGUMENTS_KEY);
94 m_userArgs->setLabelText(tr("Additional arguments:"));
95
96 m_effectiveCall = addAspect<StringAspect>();
97 m_effectiveCall->setDisplayStyle(StringAspect::TextEditDisplay);
98 m_effectiveCall->setLabelText(tr("Effective qmake call:"));
99 m_effectiveCall->setReadOnly(true);
100 m_effectiveCall->setUndoRedoEnabled(false);
101 m_effectiveCall->setEnabled(true);
102
103 auto updateSummary = [this] {
104 BaseQtVersion *qtVersion = QtKitAspect::qtVersion(target()->kit());
105 if (!qtVersion)
106 return tr("<b>qmake:</b> No Qt version set. Cannot run qmake.");
107 const QString program = qtVersion->qmakeFilePath().fileName();
108 return tr("<b>qmake:</b> %1 %2").arg(program, project()->projectFilePath().fileName());
109 };
110 setSummaryUpdater(updateSummary);
111
112 connect(target(), &Target::kitChanged, this, updateSummary);
113 }
114
qmakeBuildConfiguration() const115 QmakeBuildConfiguration *QMakeStep::qmakeBuildConfiguration() const
116 {
117 return qobject_cast<QmakeBuildConfiguration *>(buildConfiguration());
118 }
119
qmakeBuildSystem() const120 QmakeBuildSystem *QMakeStep::qmakeBuildSystem() const
121 {
122 return qmakeBuildConfiguration()->qmakeBuildSystem();
123 }
124
125 ///
126 /// Returns all arguments
127 /// That is: possbile subpath
128 /// spec
129 /// config arguemnts
130 /// moreArguments
131 /// user arguments
allArguments(const BaseQtVersion * v,ArgumentFlags flags) const132 QString QMakeStep::allArguments(const BaseQtVersion *v, ArgumentFlags flags) const
133 {
134 QTC_ASSERT(v, return QString());
135 QmakeBuildConfiguration *bc = qmakeBuildConfiguration();
136 QStringList arguments;
137 if (bc->subNodeBuild())
138 arguments << bc->subNodeBuild()->filePath().toUserOutput();
139 else if (flags & ArgumentFlag::OmitProjectPath)
140 arguments << project()->projectFilePath().fileName();
141 else
142 arguments << project()->projectFilePath().toUserOutput();
143
144 if (v->qtVersion() < QtVersionNumber(5, 0, 0))
145 arguments << "-r";
146 bool userProvidedMkspec = false;
147 for (ProcessArgs::ConstArgIterator ait(userArguments()); ait.next(); ) {
148 if (ait.value() == "-spec") {
149 if (ait.next()) {
150 userProvidedMkspec = true;
151 break;
152 }
153 }
154 }
155 const QString specArg = mkspec();
156 if (!userProvidedMkspec && !specArg.isEmpty())
157 arguments << "-spec" << QDir::toNativeSeparators(specArg);
158
159 // Find out what flags we pass on to qmake
160 arguments << bc->configCommandLineArguments();
161
162 arguments << deducedArguments().toArguments();
163
164 QString args = ProcessArgs::joinArgs(arguments);
165 // User arguments
166 ProcessArgs::addArgs(&args, userArguments());
167 for (QString arg : qAsConst(m_extraArgs))
168 ProcessArgs::addArgs(&args, arg);
169 return (flags & ArgumentFlag::Expand) ? bc->macroExpander()->expand(args) : args;
170 }
171
deducedArguments() const172 QMakeStepConfig QMakeStep::deducedArguments() const
173 {
174 Kit *kit = target()->kit();
175 QMakeStepConfig config;
176 Abi targetAbi;
177 if (ToolChain *tc = ToolChainKitAspect::cxxToolChain(kit)) {
178 targetAbi = tc->targetAbi();
179 if (HostOsInfo::isWindowsHost()
180 && tc->typeId() == ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID) {
181 config.sysRoot = SysRootKitAspect::sysRoot(kit).toString();
182 config.targetTriple = tc->originalTargetTriple();
183 }
184 }
185
186 BaseQtVersion *version = QtKitAspect::qtVersion(kit);
187
188 config.osType = QMakeStepConfig::osTypeFor(targetAbi, version);
189 config.separateDebugInfo = qmakeBuildConfiguration()->separateDebugInfo();
190 config.linkQmlDebuggingQQ2 = qmakeBuildConfiguration()->qmlDebugging();
191 config.useQtQuickCompiler = qmakeBuildConfiguration()->useQtQuickCompiler();
192
193 return config;
194 }
195
init()196 bool QMakeStep::init()
197 {
198 if (!AbstractProcessStep::init())
199 return false;
200
201 m_wasSuccess = true;
202 QmakeBuildConfiguration *qmakeBc = qmakeBuildConfiguration();
203 const BaseQtVersion *qtVersion = QtKitAspect::qtVersion(kit());
204
205 if (!qtVersion) {
206 emit addOutput(tr("No Qt version configured."), BuildStep::OutputFormat::ErrorMessage);
207 return false;
208 }
209
210 FilePath workingDirectory;
211
212 if (qmakeBc->subNodeBuild())
213 workingDirectory = qmakeBc->qmakeBuildSystem()->buildDir(qmakeBc->subNodeBuild()->filePath());
214 else
215 workingDirectory = qmakeBc->buildDirectory();
216
217 m_qmakeCommand = CommandLine{qtVersion->qmakeFilePath(), allArguments(qtVersion), CommandLine::Raw};
218 m_runMakeQmake = (qtVersion->qtVersion() >= QtVersionNumber(5, 0 ,0));
219
220 // The Makefile is used by qmake and make on the build device, from that
221 // perspective it is local.
222 QString makefile = workingDirectory.path() + '/';
223
224 if (qmakeBc->subNodeBuild()) {
225 QmakeProFileNode *pro = qmakeBc->subNodeBuild();
226 if (pro && !pro->makefile().isEmpty())
227 makefile.append(pro->makefile());
228 else
229 makefile.append("Makefile");
230 } else if (!qmakeBc->makefile().isEmpty()) {
231 makefile.append(qmakeBc->makefile());
232 } else {
233 makefile.append("Makefile");
234 }
235
236 if (m_runMakeQmake) {
237 const FilePath make = makeCommand();
238 if (make.isEmpty()) {
239 emit addOutput(tr("Could not determine which \"make\" command to run. "
240 "Check the \"make\" step in the build configuration."),
241 BuildStep::OutputFormat::ErrorMessage);
242 return false;
243 }
244 m_makeCommand = CommandLine{make, makeArguments(makefile), CommandLine::Raw};
245 } else {
246 m_makeCommand = {};
247 }
248
249 // Check whether we need to run qmake
250 if (m_forced || QmakeSettings::alwaysRunQmake()
251 || qmakeBc->compareToImportFrom(makefile) != QmakeBuildConfiguration::MakefileMatches) {
252 m_needToRunQMake = true;
253 }
254 m_forced = false;
255
256 processParameters()->setWorkingDirectory(workingDirectory);
257
258 QmakeProFileNode *node = static_cast<QmakeProFileNode *>(qmakeBc->project()->rootProjectNode());
259 if (qmakeBc->subNodeBuild())
260 node = qmakeBc->subNodeBuild();
261 QTC_ASSERT(node, return false);
262 QString proFile = node->filePath().toString();
263
264 Tasks tasks = qtVersion->reportIssues(proFile, workingDirectory.toString());
265 Utils::sort(tasks);
266
267 if (!tasks.isEmpty()) {
268 bool canContinue = true;
269 for (const Task &t : qAsConst(tasks)) {
270 emit addTask(t);
271 if (t.type == Task::Error)
272 canContinue = false;
273 }
274 if (!canContinue) {
275 emitFaultyConfigurationMessage();
276 return false;
277 }
278 }
279
280 m_scriptTemplate = node->projectType() == ProjectType::ScriptTemplate;
281
282 return true;
283 }
284
setupOutputFormatter(OutputFormatter * formatter)285 void QMakeStep::setupOutputFormatter(OutputFormatter *formatter)
286 {
287 formatter->addLineParser(new QMakeParser);
288 m_outputFormatter = formatter;
289 AbstractProcessStep::setupOutputFormatter(formatter);
290 }
291
doRun()292 void QMakeStep::doRun()
293 {
294 if (m_scriptTemplate) {
295 emit finished(true);
296 return;
297 }
298
299 if (!m_needToRunQMake) {
300 emit addOutput(tr("Configuration unchanged, skipping qmake step."), BuildStep::OutputFormat::NormalMessage);
301 emit finished(true);
302 return;
303 }
304
305 m_needToRunQMake = false;
306
307 m_nextState = State::RUN_QMAKE;
308 runNextCommand();
309 }
310
setForced(bool b)311 void QMakeStep::setForced(bool b)
312 {
313 m_forced = b;
314 }
315
processStartupFailed()316 void QMakeStep::processStartupFailed()
317 {
318 m_needToRunQMake = true;
319 AbstractProcessStep::processStartupFailed();
320 }
321
processSucceeded(int exitCode,QProcess::ExitStatus status)322 bool QMakeStep::processSucceeded(int exitCode, QProcess::ExitStatus status)
323 {
324 bool result = AbstractProcessStep::processSucceeded(exitCode, status);
325 if (!result)
326 m_needToRunQMake = true;
327 emit buildConfiguration()->buildDirectoryChanged();
328 return result;
329 }
330
doCancel()331 void QMakeStep::doCancel()
332 {
333 AbstractProcessStep::doCancel();
334 }
335
finish(bool success)336 void QMakeStep::finish(bool success)
337 {
338 m_wasSuccess = success;
339 runNextCommand();
340 }
341
startOneCommand(const CommandLine & command)342 void QMakeStep::startOneCommand(const CommandLine &command)
343 {
344 ProcessParameters *pp = processParameters();
345 pp->setCommandLine(command);
346
347 AbstractProcessStep::doRun();
348 }
349
runNextCommand()350 void QMakeStep::runNextCommand()
351 {
352 if (isCanceled())
353 m_wasSuccess = false;
354
355 if (!m_wasSuccess)
356 m_nextState = State::POST_PROCESS;
357
358 emit progress(static_cast<int>(m_nextState) * 100 / static_cast<int>(State::POST_PROCESS),
359 QString());
360
361 switch (m_nextState) {
362 case State::IDLE:
363 return;
364 case State::RUN_QMAKE:
365 m_outputFormatter->setLineParsers({new QMakeParser});
366 m_nextState = (m_runMakeQmake ? State::RUN_MAKE_QMAKE_ALL : State::POST_PROCESS);
367 startOneCommand(m_qmakeCommand);
368 return;
369 case State::RUN_MAKE_QMAKE_ALL:
370 {
371 auto *parser = new GnuMakeParser;
372 parser->addSearchDir(processParameters()->workingDirectory());
373 m_outputFormatter->setLineParsers({parser});
374 m_nextState = State::POST_PROCESS;
375 startOneCommand(m_makeCommand);
376 }
377 return;
378 case State::POST_PROCESS:
379 m_nextState = State::IDLE;
380 emit finished(m_wasSuccess);
381 return;
382 }
383 }
384
setUserArguments(const QString & arguments)385 void QMakeStep::setUserArguments(const QString &arguments)
386 {
387 m_userArgs->setArguments(arguments);
388 }
389
extraArguments() const390 QStringList QMakeStep::extraArguments() const
391 {
392 return m_extraArgs;
393 }
394
setExtraArguments(const QStringList & args)395 void QMakeStep::setExtraArguments(const QStringList &args)
396 {
397 if (m_extraArgs != args) {
398 m_extraArgs = args;
399 emit qmakeBuildConfiguration()->qmakeBuildConfigurationChanged();
400 qmakeBuildSystem()->scheduleUpdateAllNowOrLater();
401 }
402 }
403
extraParserArguments() const404 QStringList QMakeStep::extraParserArguments() const
405 {
406 return m_extraParserArgs;
407 }
408
setExtraParserArguments(const QStringList & args)409 void QMakeStep::setExtraParserArguments(const QStringList &args)
410 {
411 m_extraParserArgs = args;
412 }
413
makeCommand() const414 FilePath QMakeStep::makeCommand() const
415 {
416 if (auto ms = stepList()->firstOfType<MakeStep>())
417 return ms->makeExecutable();
418 return FilePath();
419 }
420
makeArguments(const QString & makefile) const421 QString QMakeStep::makeArguments(const QString &makefile) const
422 {
423 QString args;
424 if (!makefile.isEmpty()) {
425 ProcessArgs::addArg(&args, "-f");
426 ProcessArgs::addArg(&args, makefile);
427 }
428 ProcessArgs::addArg(&args, "qmake_all");
429 return args;
430 }
431
effectiveQMakeCall() const432 QString QMakeStep::effectiveQMakeCall() const
433 {
434 BaseQtVersion *qtVersion = QtKitAspect::qtVersion(kit());
435 QString qmake = qtVersion ? qtVersion->qmakeFilePath().toUserOutput() : QString();
436 if (qmake.isEmpty())
437 qmake = tr("<no Qt version>");
438 QString make = makeCommand().toUserOutput();
439 if (make.isEmpty())
440 make = tr("<no Make step found>");
441
442 QString result = qmake;
443 if (qtVersion) {
444 QmakeBuildConfiguration *qmakeBc = qmakeBuildConfiguration();
445 const QString makefile = qmakeBc ? qmakeBc->makefile() : QString();
446 result += ' ' + allArguments(qtVersion, ArgumentFlag::Expand);
447 if (qtVersion->qtVersion() >= QtVersionNumber(5, 0, 0))
448 result.append(QString::fromLatin1(" && %1 %2").arg(make).arg(makeArguments(makefile)));
449 }
450 return result;
451 }
452
parserArguments()453 QStringList QMakeStep::parserArguments()
454 {
455 // NOTE: extra parser args placed before the other args intentionally
456 QStringList result = m_extraParserArgs;
457 BaseQtVersion *qt = QtKitAspect::qtVersion(kit());
458 QTC_ASSERT(qt, return QStringList());
459 for (ProcessArgs::ConstArgIterator ait(allArguments(qt, ArgumentFlag::Expand)); ait.next(); ) {
460 if (ait.isSimple())
461 result << ait.value();
462 }
463 return result;
464 }
465
userArguments() const466 QString QMakeStep::userArguments() const
467 {
468 return m_userArgs->arguments(macroExpander());
469 }
470
mkspec() const471 QString QMakeStep::mkspec() const
472 {
473 QString additionalArguments = userArguments();
474 ProcessArgs::addArgs(&additionalArguments, m_extraArgs);
475 for (ProcessArgs::ArgIterator ait(&additionalArguments); ait.next(); ) {
476 if (ait.value() == "-spec") {
477 if (ait.next())
478 return FilePath::fromUserInput(ait.value()).toString();
479 }
480 }
481
482 return QmakeKitAspect::effectiveMkspec(target()->kit());
483 }
484
toMap() const485 QVariantMap QMakeStep::toMap() const
486 {
487 QVariantMap map(AbstractProcessStep::toMap());
488 map.insert(QMAKE_FORCED_KEY, m_forced);
489 map.insert(QMAKE_SELECTED_ABIS_KEY, m_selectedAbis);
490 return map;
491 }
492
fromMap(const QVariantMap & map)493 bool QMakeStep::fromMap(const QVariantMap &map)
494 {
495 m_forced = map.value(QMAKE_FORCED_KEY, false).toBool();
496 m_selectedAbis = map.value(QMAKE_SELECTED_ABIS_KEY).toStringList();
497
498 // Backwards compatibility with < Creator 4.12.
499 const QVariant separateDebugInfo
500 = map.value("QtProjectManager.QMakeBuildStep.SeparateDebugInfo");
501 if (separateDebugInfo.isValid())
502 qmakeBuildConfiguration()->forceSeparateDebugInfo(separateDebugInfo.toBool());
503 const QVariant qmlDebugging
504 = map.value("QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary");
505 if (qmlDebugging.isValid())
506 qmakeBuildConfiguration()->forceQmlDebugging(qmlDebugging.toBool());
507 const QVariant useQtQuickCompiler
508 = map.value("QtProjectManager.QMakeBuildStep.UseQtQuickCompiler");
509 if (useQtQuickCompiler.isValid())
510 qmakeBuildConfiguration()->forceQtQuickCompiler(useQtQuickCompiler.toBool());
511
512 return BuildStep::fromMap(map);
513 }
514
createConfigWidget()515 QWidget *QMakeStep::createConfigWidget()
516 {
517 abisLabel = new QLabel(tr("ABIs:"));
518 abisLabel->setAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop);
519
520 abisListWidget = new QListWidget;
521
522 Layouting::Form builder;
523 builder.addRow(m_buildType);
524 builder.addRow(m_userArgs);
525 builder.addRow(m_effectiveCall);
526 builder.addRow({abisLabel, abisListWidget});
527 auto widget = builder.emerge(false);
528
529 qmakeBuildConfigChanged();
530
531 updateSummary();
532 updateAbiWidgets();
533 updateEffectiveQMakeCall();
534
535 connect(m_userArgs, &BaseAspect::changed, widget, [this] {
536 updateAbiWidgets();
537 updateEffectiveQMakeCall();
538
539 emit qmakeBuildConfiguration()->qmakeBuildConfigurationChanged();
540 qmakeBuildSystem()->scheduleUpdateAllNowOrLater();
541 });
542
543 connect(m_buildType, &BaseAspect::changed,
544 widget, [this] { buildConfigurationSelected(); });
545
546 connect(qmakeBuildConfiguration(), &QmakeBuildConfiguration::qmlDebuggingChanged,
547 widget, [this] {
548 linkQmlDebuggingLibraryChanged();
549 askForRebuild(tr("QML Debugging"));
550 });
551
552 connect(project(), &Project::projectLanguagesUpdated,
553 widget, [this] { linkQmlDebuggingLibraryChanged(); });
554 connect(target(), &Target::parsingFinished,
555 widget, [this] { updateEffectiveQMakeCall(); });
556 connect(qmakeBuildConfiguration(), &QmakeBuildConfiguration::useQtQuickCompilerChanged,
557 widget, [this] { useQtQuickCompilerChanged(); });
558 connect(qmakeBuildConfiguration(), &QmakeBuildConfiguration::separateDebugInfoChanged,
559 widget, [this] { separateDebugInfoChanged(); });
560 connect(qmakeBuildConfiguration(), &QmakeBuildConfiguration::qmakeBuildConfigurationChanged,
561 widget, [this] { qmakeBuildConfigChanged(); });
562 connect(target(), &Target::kitChanged,
563 widget, [this] { qtVersionChanged(); });
564
565 connect(abisListWidget, &QListWidget::itemChanged, this, [this] {
566 abisChanged();
567 if (QmakeBuildConfiguration *bc = qmakeBuildConfiguration())
568 BuildManager::buildLists({bc->cleanSteps()});
569 });
570
571 VariableChooser::addSupportForChildWidgets(widget, macroExpander());
572
573 return widget;
574 }
575
qtVersionChanged()576 void QMakeStep::qtVersionChanged()
577 {
578 updateAbiWidgets();
579 updateEffectiveQMakeCall();
580 }
581
qmakeBuildConfigChanged()582 void QMakeStep::qmakeBuildConfigChanged()
583 {
584 QmakeBuildConfiguration *bc = qmakeBuildConfiguration();
585 bool debug = bc->qmakeBuildConfiguration() & BaseQtVersion::DebugBuild;
586 m_ignoreChange = true;
587 m_buildType->setValue(debug? 0 : 1);
588 m_ignoreChange = false;
589 updateAbiWidgets();
590 updateEffectiveQMakeCall();
591 }
592
linkQmlDebuggingLibraryChanged()593 void QMakeStep::linkQmlDebuggingLibraryChanged()
594 {
595 updateAbiWidgets();
596 updateEffectiveQMakeCall();
597 }
598
useQtQuickCompilerChanged()599 void QMakeStep::useQtQuickCompilerChanged()
600 {
601 updateAbiWidgets();
602 updateEffectiveQMakeCall();
603 askForRebuild(tr("Qt Quick Compiler"));
604 }
605
separateDebugInfoChanged()606 void QMakeStep::separateDebugInfoChanged()
607 {
608 updateAbiWidgets();
609 updateEffectiveQMakeCall();
610 askForRebuild(tr("Separate Debug Information"));
611 }
612
isIos(const Kit * k)613 static bool isIos(const Kit *k)
614 {
615 const Id deviceType = DeviceTypeKitAspect::deviceTypeId(k);
616 return deviceType == Ios::Constants::IOS_DEVICE_TYPE
617 || deviceType == Ios::Constants::IOS_SIMULATOR_TYPE;
618 }
619
abisChanged()620 void QMakeStep::abisChanged()
621 {
622 m_selectedAbis.clear();
623 for (int i = 0; i < abisListWidget->count(); ++i) {
624 auto item = abisListWidget->item(i);
625 if (item->checkState() == Qt::CheckState::Checked)
626 m_selectedAbis << item->text();
627 }
628
629 if (BaseQtVersion *qtVersion = QtKitAspect::qtVersion(target()->kit())) {
630 if (qtVersion->hasAbi(Abi::LinuxOS, Abi::AndroidLinuxFlavor)) {
631 const QString prefix = "ANDROID_ABIS=";
632 QStringList args = m_extraArgs;
633 for (auto it = args.begin(); it != args.end(); ++it) {
634 if (it->startsWith(prefix)) {
635 args.erase(it);
636 break;
637 }
638 }
639 if (!m_selectedAbis.isEmpty())
640 args << prefix + '"' + m_selectedAbis.join(' ') + '"';
641 setExtraArguments(args);
642
643 buildSystem()->setProperty(Android::Constants::ANDROID_ABIS, m_selectedAbis);
644 } else if (qtVersion->hasAbi(Abi::DarwinOS) && !isIos(target()->kit())) {
645 const QString prefix = "QMAKE_APPLE_DEVICE_ARCHS=";
646 QStringList args = m_extraArgs;
647 for (auto it = args.begin(); it != args.end(); ++it) {
648 if (it->startsWith(prefix)) {
649 args.erase(it);
650 break;
651 }
652 }
653 QStringList archs;
654 for (const QString &selectedAbi : qAsConst(m_selectedAbis)) {
655 const auto abi = Abi::abiFromTargetTriplet(selectedAbi);
656 if (abi.architecture() == Abi::X86Architecture)
657 archs << "x86_64";
658 else if (abi.architecture() == Abi::ArmArchitecture)
659 archs << "arm64";
660 }
661 if (!archs.isEmpty())
662 args << prefix + '"' + archs.join(' ') + '"';
663 setExtraArguments(args);
664 }
665 }
666
667 updateAbiWidgets();
668 updateEffectiveQMakeCall();
669 }
670
buildConfigurationSelected()671 void QMakeStep::buildConfigurationSelected()
672 {
673 if (m_ignoreChange)
674 return;
675 QmakeBuildConfiguration *bc = qmakeBuildConfiguration();
676 BaseQtVersion::QmakeBuildConfigs buildConfiguration = bc->qmakeBuildConfiguration();
677 if (m_buildType->value() == 0) { // debug
678 buildConfiguration = buildConfiguration | BaseQtVersion::DebugBuild;
679 } else {
680 buildConfiguration = buildConfiguration & ~BaseQtVersion::DebugBuild;
681 }
682 m_ignoreChange = true;
683 bc->setQMakeBuildConfiguration(buildConfiguration);
684 m_ignoreChange = false;
685
686 updateAbiWidgets();
687 updateEffectiveQMakeCall();
688 }
689
askForRebuild(const QString & title)690 void QMakeStep::askForRebuild(const QString &title)
691 {
692 auto *question = new QMessageBox(Core::ICore::dialogParent());
693 question->setWindowTitle(title);
694 question->setText(tr("The option will only take effect if the project is recompiled. Do you want to recompile now?"));
695 question->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
696 question->setModal(true);
697 connect(question, &QDialog::finished, this, &QMakeStep::recompileMessageBoxFinished);
698 question->show();
699 }
700
updateAbiWidgets()701 void QMakeStep::updateAbiWidgets()
702 {
703 if (!abisLabel)
704 return;
705
706 BaseQtVersion *qtVersion = QtKitAspect::qtVersion(target()->kit());
707 if (!qtVersion)
708 return;
709
710 const Abis abis = qtVersion->qtAbis();
711 const bool enableAbisSelect = abis.size() > 1;
712 abisLabel->setVisible(enableAbisSelect);
713 abisListWidget->setVisible(enableAbisSelect);
714
715 if (enableAbisSelect && abisListWidget->count() != abis.size()) {
716 abisListWidget->clear();
717 QStringList selectedAbis = m_selectedAbis;
718
719 if (selectedAbis.isEmpty()) {
720 if (qtVersion->hasAbi(Abi::LinuxOS, Abi::AndroidLinuxFlavor)) {
721 // Prefer ARM for Android, prefer 32bit.
722 for (const Abi &abi : abis) {
723 if (abi.param() == ProjectExplorer::Constants::ANDROID_ABI_ARMEABI_V7A)
724 selectedAbis.append(abi.param());
725 }
726 if (selectedAbis.isEmpty()) {
727 for (const Abi &abi : abis) {
728 if (abi.param() == ProjectExplorer::Constants::ANDROID_ABI_ARM64_V8A)
729 selectedAbis.append(abi.param());
730 }
731 }
732 } else if (qtVersion->hasAbi(Abi::DarwinOS) && !isIos(target()->kit()) && HostOsInfo::isRunningUnderRosetta()) {
733 // Automatically select arm64 when running under Rosetta
734 for (const Abi &abi : abis) {
735 if (abi.architecture() == Abi::ArmArchitecture)
736 selectedAbis.append(abi.param());
737 }
738 }
739 }
740
741 for (const Abi &abi : abis) {
742 const QString param = abi.param();
743 auto item = new QListWidgetItem{param, abisListWidget};
744 item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
745 item->setCheckState(selectedAbis.contains(param) ? Qt::Checked : Qt::Unchecked);
746 }
747 abisChanged();
748 }
749 }
750
updateEffectiveQMakeCall()751 void QMakeStep::updateEffectiveQMakeCall()
752 {
753 m_effectiveCall->setValue(effectiveQMakeCall());
754 }
755
recompileMessageBoxFinished(int button)756 void QMakeStep::recompileMessageBoxFinished(int button)
757 {
758 if (button == QMessageBox::Yes) {
759 if (BuildConfiguration *bc = buildConfiguration())
760 BuildManager::buildLists({bc->cleanSteps(), bc->buildSteps()});
761 }
762 }
763
764 ////
765 // QMakeStepFactory
766 ////
767
QMakeStepFactory()768 QMakeStepFactory::QMakeStepFactory()
769 {
770 registerStep<QMakeStep>(Constants::QMAKE_BS_ID);
771 setSupportedConfiguration(Constants::QMAKE_BC_ID);
772 setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD);
773 //: QMakeStep default display name
774 setDisplayName(::QmakeProjectManager::QMakeStep::tr("qmake"));
775 setFlags(BuildStepInfo::UniqueStep);
776 }
777
targetArchFor(const Abi &,const BaseQtVersion *)778 QMakeStepConfig::TargetArchConfig QMakeStepConfig::targetArchFor(const Abi &, const BaseQtVersion *)
779 {
780 return NoArch;
781 }
782
osTypeFor(const Abi & targetAbi,const BaseQtVersion * version)783 QMakeStepConfig::OsType QMakeStepConfig::osTypeFor(const Abi &targetAbi, const BaseQtVersion *version)
784 {
785 OsType os = NoOsType;
786 const char IOSQT[] = "Qt4ProjectManager.QtVersion.Ios";
787 if (!version || version->type() != IOSQT)
788 return os;
789 if (targetAbi.os() == Abi::DarwinOS && targetAbi.binaryFormat() == Abi::MachOFormat) {
790 if (targetAbi.architecture() == Abi::X86Architecture)
791 os = IphoneSimulator;
792 else if (targetAbi.architecture() == Abi::ArmArchitecture)
793 os = IphoneOS;
794 }
795 return os;
796 }
797
toArguments() const798 QStringList QMakeStepConfig::toArguments() const
799 {
800 QStringList arguments;
801
802 // TODO: make that depend on the actual Qt version that is used
803 if (osType == IphoneSimulator)
804 arguments << "CONFIG+=iphonesimulator" << "CONFIG+=simulator" /*since Qt 5.7*/;
805 else if (osType == IphoneOS)
806 arguments << "CONFIG+=iphoneos" << "CONFIG+=device" /*since Qt 5.7*/;
807
808 if (linkQmlDebuggingQQ2 == TriState::Enabled)
809 arguments << "CONFIG+=qml_debug";
810 else if (linkQmlDebuggingQQ2 == TriState::Disabled)
811 arguments << "CONFIG-=qml_debug";
812
813 if (useQtQuickCompiler == TriState::Enabled)
814 arguments << "CONFIG+=qtquickcompiler";
815 else if (useQtQuickCompiler == TriState::Disabled)
816 arguments << "CONFIG-=qtquickcompiler";
817
818 if (separateDebugInfo == TriState::Enabled)
819 arguments << "CONFIG+=force_debug_info" << "CONFIG+=separate_debug_info";
820 else if (separateDebugInfo == TriState::Disabled)
821 arguments << "CONFIG-=separate_debug_info";
822
823 if (!sysRoot.isEmpty()) {
824 arguments << ("QMAKE_CFLAGS+=--sysroot=\"" + sysRoot + "\"");
825 arguments << ("QMAKE_CXXFLAGS+=--sysroot=\"" + sysRoot + "\"");
826 arguments << ("QMAKE_LFLAGS+=--sysroot=\"" + sysRoot + "\"");
827 if (!targetTriple.isEmpty()) {
828 arguments << ("QMAKE_CFLAGS+=--target=" + targetTriple);
829 arguments << ("QMAKE_CXXFLAGS+=--target=" + targetTriple);
830 arguments << ("QMAKE_LFLAGS+=--target=" + targetTriple);
831 }
832 }
833
834 return arguments;
835 }
836
837 } // QmakeProjectManager
838