diff options
Diffstat (limited to 'src/plugins/projectexplorer')
214 files changed, 9430 insertions, 6656 deletions
diff --git a/src/plugins/projectexplorer/CMakeLists.txt b/src/plugins/projectexplorer/CMakeLists.txt new file mode 100644 index 0000000000..c9413c2385 --- /dev/null +++ b/src/plugins/projectexplorer/CMakeLists.txt @@ -0,0 +1,215 @@ +add_qtc_plugin(ProjectExplorer + DEPENDS QtcSsh Qt5::Qml + PLUGIN_DEPENDS Core TextEditor + SOURCES + abi.cpp abi.h + abiwidget.cpp abiwidget.h + abstractprocessstep.cpp abstractprocessstep.h + addrunconfigdialog.cpp addrunconfigdialog.h + allprojectsfilter.cpp allprojectsfilter.h + allprojectsfind.cpp allprojectsfind.h + ansifilterparser.cpp ansifilterparser.h + applicationlauncher.cpp applicationlauncher.h + appoutputpane.cpp appoutputpane.h + baseprojectwizarddialog.cpp baseprojectwizarddialog.h + buildconfiguration.cpp buildconfiguration.h + buildenvironmentwidget.cpp buildenvironmentwidget.h + buildinfo.cpp buildinfo.h + buildmanager.cpp buildmanager.h + buildprogress.cpp buildprogress.h + buildsettingspropertiespage.cpp buildsettingspropertiespage.h + buildstep.cpp buildstep.h + buildsteplist.cpp buildsteplist.h + buildstepspage.cpp buildstepspage.h + buildtargetinfo.h + clangparser.cpp clangparser.h + codestylesettingspropertiespage.cpp codestylesettingspropertiespage.h codestylesettingspropertiespage.ui + compileoutputwindow.cpp compileoutputwindow.h + configtaskhandler.cpp configtaskhandler.h + copytaskhandler.cpp copytaskhandler.h + currentprojectfilter.cpp currentprojectfilter.h + currentprojectfind.cpp currentprojectfind.h + customexecutablerunconfiguration.cpp customexecutablerunconfiguration.h + customparser.cpp customparser.h + customparserconfigdialog.cpp customparserconfigdialog.h customparserconfigdialog.ui + customtoolchain.cpp customtoolchain.h + customwizard/customwizard.cpp customwizard/customwizard.h + customwizard/customwizardpage.cpp customwizard/customwizardpage.h + customwizard/customwizardparameters.cpp customwizard/customwizardparameters.h + customwizard/customwizardscriptgenerator.cpp customwizard/customwizardscriptgenerator.h + dependenciespanel.cpp dependenciespanel.h + deployablefile.cpp deployablefile.h + deployconfiguration.cpp deployconfiguration.h + deploymentdata.cpp deploymentdata.h + deploymentdatamodel.cpp deploymentdatamodel.h + deploymentdataview.cpp deploymentdataview.h deploymentdataview.ui + devicesupport/desktopdevice.cpp devicesupport/desktopdevice.h + devicesupport/desktopdeviceconfigurationwidget.cpp devicesupport/desktopdeviceconfigurationwidget.h devicesupport/desktopdeviceconfigurationwidget.ui + devicesupport/desktopdevicefactory.cpp devicesupport/desktopdevicefactory.h + devicesupport/desktopdeviceprocess.cpp devicesupport/desktopdeviceprocess.h + devicesupport/desktopprocesssignaloperation.cpp devicesupport/desktopprocesssignaloperation.h + devicesupport/devicecheckbuildstep.cpp devicesupport/devicecheckbuildstep.h + devicesupport/devicefactoryselectiondialog.cpp devicesupport/devicefactoryselectiondialog.h devicesupport/devicefactoryselectiondialog.ui + devicesupport/devicemanager.cpp devicesupport/devicemanager.h + devicesupport/devicemanagermodel.cpp devicesupport/devicemanagermodel.h + devicesupport/deviceprocess.cpp devicesupport/deviceprocess.h + devicesupport/deviceprocessesdialog.cpp devicesupport/deviceprocessesdialog.h + devicesupport/deviceprocesslist.cpp devicesupport/deviceprocesslist.h + devicesupport/devicesettingspage.cpp devicesupport/devicesettingspage.h + devicesupport/devicesettingswidget.cpp devicesupport/devicesettingswidget.h devicesupport/devicesettingswidget.ui + devicesupport/devicetestdialog.cpp devicesupport/devicetestdialog.h devicesupport/devicetestdialog.ui + devicesupport/deviceusedportsgatherer.cpp devicesupport/deviceusedportsgatherer.h + devicesupport/idevice.cpp devicesupport/idevice.h + devicesupport/idevicefactory.cpp devicesupport/idevicefactory.h + devicesupport/idevicewidget.h + devicesupport/localprocesslist.cpp devicesupport/localprocesslist.h + devicesupport/sshdeviceprocess.cpp devicesupport/sshdeviceprocess.h + devicesupport/sshdeviceprocess.cpp devicesupport/sshdeviceprocess.h + devicesupport/sshdeviceprocesslist.cpp devicesupport/sshdeviceprocesslist.h + devicesupport/sshsettingspage.cpp devicesupport/sshsettingspage.h + editorconfiguration.cpp editorconfiguration.h + editorsettingspropertiespage.cpp editorsettingspropertiespage.h editorsettingspropertiespage.ui + environmentaspect.cpp environmentaspect.h + environmentaspectwidget.cpp environmentaspectwidget.h + environmentwidget.cpp environmentwidget.h + expanddata.cpp expanddata.h + extraabi.cpp extraabi.h + extracompiler.cpp extracompiler.h + fileinsessionfinder.cpp fileinsessionfinder.h + filterkitaspectsdialog.cpp filterkitaspectsdialog.h + foldernavigationwidget.cpp foldernavigationwidget.h + gccparser.cpp gccparser.h + gcctoolchain.cpp gcctoolchain.h + gcctoolchainfactories.h + gnumakeparser.cpp gnumakeparser.h + headerpath.h + importwidget.cpp importwidget.h + ioutputparser.cpp ioutputparser.h + ipotentialkit.h + itaskhandler.h + jsonwizard/jsonfieldpage.cpp jsonwizard/jsonfieldpage.h jsonwizard/jsonfieldpage_p.h + jsonwizard/jsonfilepage.cpp jsonwizard/jsonfilepage.h + jsonwizard/jsonkitspage.cpp jsonwizard/jsonkitspage.h + jsonwizard/jsonprojectpage.cpp jsonwizard/jsonprojectpage.h + jsonwizard/jsonsummarypage.cpp jsonwizard/jsonsummarypage.h + jsonwizard/jsonwizard.cpp jsonwizard/jsonwizard.h + jsonwizard/jsonwizardfactory.cpp jsonwizard/jsonwizardfactory.h + jsonwizard/jsonwizardfilegenerator.cpp jsonwizard/jsonwizardfilegenerator.h + jsonwizard/jsonwizardgeneratorfactory.cpp jsonwizard/jsonwizardgeneratorfactory.h + jsonwizard/jsonwizardpagefactory.cpp jsonwizard/jsonwizardpagefactory.h + jsonwizard/jsonwizardpagefactory_p.cpp + jsonwizard/jsonwizardpagefactory_p.h + jsonwizard/jsonwizardscannergenerator.cpp jsonwizard/jsonwizardscannergenerator.h + kit.cpp kit.h + kitchooser.cpp kitchooser.h + kitfeatureprovider.h + kitinformation.cpp kitinformation.h + kitmanager.cpp kitmanager.h + kitmanagerconfigwidget.cpp kitmanagerconfigwidget.h + kitmodel.cpp kitmodel.h + kitoptionspage.cpp kitoptionspage.h + ldparser.cpp ldparser.h + linuxiccparser.cpp linuxiccparser.h + localenvironmentaspect.cpp localenvironmentaspect.h + makestep.cpp makestep.h makestep.ui + miniprojecttargetselector.cpp miniprojecttargetselector.h + msvcparser.cpp msvcparser.h + msvctoolchain.cpp msvctoolchain.h + namedwidget.cpp namedwidget.h + osparser.cpp osparser.h + panelswidget.cpp panelswidget.h + parseissuesdialog.cpp parseissuesdialog.h + processparameters.cpp processparameters.h + processstep.cpp processstep.h + project.cpp project.h + projectconfiguration.cpp projectconfiguration.h + projectconfigurationaspects.cpp projectconfigurationaspects.h + projectconfigurationmodel.cpp projectconfigurationmodel.h + projectexplorer.cpp projectexplorer.h + projectexplorer.qrc + projectexplorer_export.h + projectexplorer_global.h + projectexplorerconstants.h + projectexplorericons.cpp projectexplorericons.h + projectexplorersettings.h + projectexplorersettingspage.cpp projectexplorersettingspage.h projectexplorersettingspage.ui + projectfilewizardextension.cpp projectfilewizardextension.h + projectimporter.cpp projectimporter.h + projectmacro.cpp projectmacro.h + projectmacroexpander.cpp projectmacroexpander.h + projectmanager.h + projectmodels.cpp projectmodels.h + projectnodes.cpp projectnodes.h + projectpanelfactory.cpp projectpanelfactory.h + projecttree.cpp projecttree.h + projecttreewidget.cpp projecttreewidget.h + projectwelcomepage.cpp projectwelcomepage.h + projectwindow.cpp projectwindow.h + projectwizardpage.cpp projectwizardpage.h projectwizardpage.ui + removetaskhandler.cpp removetaskhandler.h + runconfiguration.cpp runconfiguration.h + runconfigurationaspects.cpp runconfigurationaspects.h + runcontrol.cpp runcontrol.h + runsettingspropertiespage.cpp runsettingspropertiespage.h + selectablefilesmodel.cpp selectablefilesmodel.h + session.cpp session.h + sessiondialog.cpp sessiondialog.h sessiondialog.ui + sessionmodel.cpp sessionmodel.h + sessionview.cpp sessionview.h + showineditortaskhandler.cpp showineditortaskhandler.h + showoutputtaskhandler.cpp showoutputtaskhandler.h + subscription.cpp subscription.h + target.cpp target.h + targetsettingspanel.cpp targetsettingspanel.h + targetsetuppage.cpp targetsetuppage.h + targetsetupwidget.cpp targetsetupwidget.h + task.cpp task.h + taskhub.cpp taskhub.h + taskmodel.cpp taskmodel.h + taskwindow.cpp taskwindow.h + toolchain.cpp toolchain.h + toolchaincache.h + toolchainconfigwidget.cpp toolchainconfigwidget.h + toolchainmanager.cpp toolchainmanager.h + toolchainoptionspage.cpp toolchainoptionspage.h + toolchainsettingsaccessor.cpp toolchainsettingsaccessor.h + treescanner.cpp treescanner.h + userfileaccessor.cpp userfileaccessor.h + vcsannotatetaskhandler.cpp vcsannotatetaskhandler.h + waitforstopdialog.cpp waitforstopdialog.h + xcodebuildparser.cpp xcodebuildparser.h +) + +if (TARGET libclang) + set(CLANG_BINDIR "$<TARGET_FILE_DIR:libclang>") +endif() +extend_qtc_plugin(ProjectExplorer + DEFINES "CLANG_BINDIR=\"${CLANG_BINDIR}\"" +) + +extend_qtc_plugin(ProjectExplorer + CONDITION WIN32 + SOURCES windebuginterface.cpp windebuginterface.h + DEFINES UNICODE _UNICODE +) + +extend_qtc_plugin(ProjectExplorer + CONDITION journald + DEPENDS systemd + SOURCES journaldwatcher.cpp journaldwatcher.h + DEFINES WITH_JOURNALD +) + +extend_qtc_plugin(ProjectExplorer + CONDITION WITH_TESTS + SOURCES + jsonwizard/jsonwizard_test.cpp + outputparser_test.cpp outputparser_test.h +) +qtc_plugin_enabled(_projectexplorer_enabled ProjectExplorer) +if (WITH_TESTS AND _projectexplorer_enabled) + set_source_files_properties(jsonwizard/jsonwizard_test.cpp + PROPERTIES HEADER_FILE_ONLY ON + ) +endif() + diff --git a/src/plugins/projectexplorer/abi.cpp b/src/plugins/projectexplorer/abi.cpp index 0de4ea655d..e4e4c13e64 100644 --- a/src/plugins/projectexplorer/abi.cpp +++ b/src/plugins/projectexplorer/abi.cpp @@ -219,9 +219,9 @@ static Abi macAbiForCpu(quint32 type) { } } -static QList<Abi> parseCoffHeader(const QByteArray &data) +static Abis parseCoffHeader(const QByteArray &data) { - QList<Abi> result; + Abis result; if (data.size() < 20) return result; @@ -305,9 +305,9 @@ static QList<Abi> parseCoffHeader(const QByteArray &data) return result; } -static QList<Abi> abiOf(const QByteArray &data) +static Abis abiOf(const QByteArray &data) { - QList<Abi> result; + Abis result; if (data.size() <= 8) return result; @@ -626,8 +626,10 @@ bool Abi::isCompatibleWith(const Abi &other) const if (isCompat && (osFlavor() == AndroidLinuxFlavor || other.osFlavor() == AndroidLinuxFlavor)) isCompat = (architecture() == other.architecture()) && (osFlavor() == other.osFlavor()); - if (!isCompat && compatibleMSVCFlavors(osFlavor(), other.osFlavor())) + if (!isCompat && wordWidth() == other.wordWidth() + && compatibleMSVCFlavors(osFlavor(), other.osFlavor())) { isCompat = true; + } return isCompat; } @@ -661,6 +663,8 @@ QString Abi::toString(const Architecture &a) return QLatin1String("xtensa"); case X86Architecture: return QLatin1String("x86"); + case Mcs51Architecture: + return QLatin1String("mcs51"); case MipsArchitecture: return QLatin1String("mips"); case PowerPCArchitecture: @@ -722,6 +726,10 @@ QString Abi::toString(const BinaryFormat &bf) return QLatin1String("mach_o"); case RuntimeQmlFormat: return QLatin1String("qml_rt"); + case UbrofFormat: + return QLatin1String("ubrof"); + case OmfFormat: + return QLatin1String("omf"); case UnknownFormat: Q_FALLTHROUGH(); default: @@ -789,6 +797,8 @@ Abi::Architecture Abi::architectureFromString(const QStringRef &a) return AvrArchitecture; if (a == "x86") return X86Architecture; + if (a == "mcs51") + return Mcs51Architecture; if (a == "mips") return MipsArchitecture; if (a == "ppc") @@ -845,6 +855,10 @@ Abi::BinaryFormat Abi::binaryFormatFromString(const QStringRef &bf) return PEFormat; if (bf == "mach_o") return MachOFormat; + if (bf == "ubrof") + return UbrofFormat; + if (bf == "omf") + return OmfFormat; if (bf == "qml_rt") return RuntimeQmlFormat; return UnknownFormat; @@ -969,9 +983,9 @@ Abi Abi::hostAbi() } //! Extract available ABIs from a binary using heuristics. -QList<Abi> Abi::abisOfBinary(const Utils::FileName &path) +Abis Abi::abisOfBinary(const Utils::FilePath &path) { - QList<Abi> tmp; + Abis tmp; if (path.isEmpty()) return tmp; @@ -1024,8 +1038,8 @@ QList<Abi> Abi::abisOfBinary(const Utils::FileName &path) f.close(); // Remove duplicates: - QList<Abi> result; - foreach (const Abi &a, tmp) { + Abis result; + for (const Abi &a : qAsConst(tmp)) { if (!result.contains(a)) result.append(a); } @@ -1219,7 +1233,7 @@ void ProjectExplorer::ProjectExplorerPlugin::testAbiOfBinary() QFETCH(QString, file); QFETCH(QStringList, abis); - QList<Abi> result = Abi::abisOfBinary(Utils::FileName::fromString(file)); + const Abis result = Abi::abisOfBinary(Utils::FilePath::fromString(file)); QCOMPARE(result.count(), abis.count()); for (int i = 0; i < abis.count(); ++i) QCOMPARE(result.at(i).toString(), abis.at(i)); @@ -1301,26 +1315,10 @@ void ProjectExplorer::ProjectExplorerPlugin::testAbiFromTargetTriplet_data() << int(Abi::LinuxOS) << int(Abi::GenericFlavor) << int(Abi::ElfFormat) << 32; - QTest::newRow("mipsel-linux-android") << int(Abi::MipsArchitecture) - << int(Abi::LinuxOS) << int(Abi::AndroidLinuxFlavor) - << int(Abi::ElfFormat) << 32; - - QTest::newRow("mipsel-unknown-linux-android") << int(Abi::MipsArchitecture) - << int(Abi::LinuxOS) << int(Abi::AndroidLinuxFlavor) - << int(Abi::ElfFormat) << 32; - QTest::newRow("mips-linux-gnu") << int(Abi::MipsArchitecture) << int(Abi::LinuxOS) << int(Abi::GenericFlavor) << int(Abi::ElfFormat) << 32; - QTest::newRow("mips64el-linux-android") << int(Abi::MipsArchitecture) - << int(Abi::LinuxOS) << int(Abi::AndroidLinuxFlavor) - << int(Abi::ElfFormat) << 64; - - QTest::newRow("mips64el-unknown-linux-android") << int(Abi::MipsArchitecture) - << int(Abi::LinuxOS) << int(Abi::AndroidLinuxFlavor) - << int(Abi::ElfFormat) << 64; - QTest::newRow("mips64-linux-octeon-gnu") << int(Abi::MipsArchitecture) << int(Abi::LinuxOS) << int(Abi::GenericFlavor) << int(Abi::ElfFormat) << 64; diff --git a/src/plugins/projectexplorer/abi.h b/src/plugins/projectexplorer/abi.h index 43243847c8..ddca225896 100644 --- a/src/plugins/projectexplorer/abi.h +++ b/src/plugins/projectexplorer/abi.h @@ -34,7 +34,7 @@ #include <vector> -namespace Utils { class FileName; } +namespace Utils { class FilePath; } namespace ProjectExplorer { @@ -42,6 +42,9 @@ namespace ProjectExplorer { // ABI (documentation inside) // -------------------------------------------------------------------------- +class Abi; +using Abis = QVector<Abi>; + class PROJECTEXPLORER_EXPORT Abi { public: @@ -54,6 +57,7 @@ public: ShArchitecture, AvrArchitecture, XtensaArchitecture, + Mcs51Architecture, UnknownArchitecture }; @@ -108,6 +112,8 @@ public: MachOFormat, PEFormat, RuntimeQmlFormat, + UbrofFormat, + OmfFormat, UnknownFormat }; @@ -154,7 +160,7 @@ public: static Abi fromString(const QString &abiString); static Abi hostAbi(); - static QList<Abi> abisOfBinary(const Utils::FileName &path); + static Abis abisOfBinary(const Utils::FilePath &path); private: diff --git a/src/plugins/projectexplorer/abiwidget.cpp b/src/plugins/projectexplorer/abiwidget.cpp index 80b88ac03e..5df9a3ee05 100644 --- a/src/plugins/projectexplorer/abiwidget.cpp +++ b/src/plugins/projectexplorer/abiwidget.cpp @@ -85,7 +85,7 @@ AbiWidget::AbiWidget(QWidget *parent) : QWidget(parent), d->m_abi->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); d->m_abi->setMinimumContentsLength(4); layout->addWidget(d->m_abi); - connect(d->m_abi, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(d->m_abi, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AbiWidget::mainComboBoxChanged); d->m_architectureComboBox = new QComboBox(this); @@ -93,7 +93,7 @@ AbiWidget::AbiWidget(QWidget *parent) : QWidget(parent), for (int i = 0; i <= static_cast<int>(Abi::UnknownArchitecture); ++i) d->m_architectureComboBox->addItem(Abi::toString(static_cast<Abi::Architecture>(i)), i); d->m_architectureComboBox->setCurrentIndex(static_cast<int>(Abi::UnknownArchitecture)); - connect(d->m_architectureComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(d->m_architectureComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AbiWidget::customComboBoxesChanged); QLabel *separator1 = new QLabel(this); @@ -106,7 +106,7 @@ AbiWidget::AbiWidget(QWidget *parent) : QWidget(parent), for (int i = 0; i <= static_cast<int>(Abi::UnknownOS); ++i) d->m_osComboBox->addItem(Abi::toString(static_cast<Abi::OS>(i)), i); d->m_osComboBox->setCurrentIndex(static_cast<int>(Abi::UnknownOS)); - connect(d->m_osComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(d->m_osComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AbiWidget::customOsComboBoxChanged); QLabel *separator2 = new QLabel(this); @@ -116,7 +116,7 @@ AbiWidget::AbiWidget(QWidget *parent) : QWidget(parent), d->m_osFlavorComboBox = new QComboBox(this); layout->addWidget(d->m_osFlavorComboBox); - connect(d->m_osFlavorComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(d->m_osFlavorComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AbiWidget::customComboBoxesChanged); QLabel *separator3 = new QLabel(this); @@ -129,7 +129,7 @@ AbiWidget::AbiWidget(QWidget *parent) : QWidget(parent), for (int i = 0; i <= static_cast<int>(Abi::UnknownFormat); ++i) d->m_binaryFormatComboBox->addItem(Abi::toString(static_cast<Abi::BinaryFormat>(i)), i); d->m_binaryFormatComboBox->setCurrentIndex(static_cast<int>(Abi::UnknownFormat)); - connect(d->m_binaryFormatComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(d->m_binaryFormatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AbiWidget::customComboBoxesChanged); QLabel *separator4 = new QLabel(this); @@ -146,17 +146,17 @@ AbiWidget::AbiWidget(QWidget *parent) : QWidget(parent), d->m_wordWidthComboBox->addItem(Abi::toString(0), 0); // Setup current word width of 0 by default. d->m_wordWidthComboBox->setCurrentIndex(d->m_wordWidthComboBox->count() - 1); - connect(d->m_wordWidthComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(d->m_wordWidthComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AbiWidget::customComboBoxesChanged); layout->setStretchFactor(d->m_abi, 1); - setAbis(QList<Abi>(), Abi::hostAbi()); + setAbis(Abis(), Abi::hostAbi()); } AbiWidget::~AbiWidget() = default; -static Abi selectAbi(const Abi ¤t, const QList<Abi> &abiList) +static Abi selectAbi(const Abi ¤t, const Abis &abiList) { if (!current.isNull()) return current; @@ -165,7 +165,7 @@ static Abi selectAbi(const Abi ¤t, const QList<Abi> &abiList) return Abi::hostAbi(); } -void AbiWidget::setAbis(const QList<Abi> &abiList, const Abi ¤tAbi) +void AbiWidget::setAbis(const Abis &abiList, const Abi ¤tAbi) { const Abi defaultAbi = selectAbi(currentAbi, abiList); { @@ -191,9 +191,9 @@ void AbiWidget::setAbis(const QList<Abi> &abiList, const Abi ¤tAbi) emitAbiChanged(defaultAbi); } -QList<Abi> AbiWidget::supportedAbis() const +Abis AbiWidget::supportedAbis() const { - QList<Abi> result; + Abis result; result.reserve(d->m_abi->count()); for (int i = 1; i < d->m_abi->count(); ++i) result << Abi::fromString(d->m_abi->itemData(i).toString()); diff --git a/src/plugins/projectexplorer/abiwidget.h b/src/plugins/projectexplorer/abiwidget.h index b8a64dd05c..6cd6699ce7 100644 --- a/src/plugins/projectexplorer/abiwidget.h +++ b/src/plugins/projectexplorer/abiwidget.h @@ -27,12 +27,13 @@ #include "projectexplorer_export.h" +#include "abi.h" + #include <QWidget> #include <memory> namespace ProjectExplorer { -class Abi; namespace Internal { class AbiWidgetPrivate; } @@ -48,9 +49,9 @@ public: AbiWidget(QWidget *parent = nullptr); ~AbiWidget() override; - void setAbis(const QList<Abi> &, const Abi ¤tAbi); + void setAbis(const ProjectExplorer::Abis &, const Abi ¤tAbi); - QList<Abi> supportedAbis() const; + Abis supportedAbis() const; bool isCustomAbi() const; Abi currentAbi() const; diff --git a/src/plugins/projectexplorer/abstractprocessstep.cpp b/src/plugins/projectexplorer/abstractprocessstep.cpp index e85a9648ec..1d92fa9280 100644 --- a/src/plugins/projectexplorer/abstractprocessstep.cpp +++ b/src/plugins/projectexplorer/abstractprocessstep.cpp @@ -35,22 +35,20 @@ #include <coreplugin/reaper.h> +#include <utils/fileinprojectfinder.h> #include <utils/fileutils.h> #include <utils/qtcassert.h> #include <utils/qtcprocess.h> #include <QDir> -#include <QTimer> #include <QHash> #include <QPair> +#include <QUrl> #include <algorithm> #include <memory> -namespace { -const int CACHE_SOFT_LIMIT = 500; -const int CACHE_HARD_LIMIT = 1000; -} // namespace +using namespace Utils; namespace ProjectExplorer { @@ -107,10 +105,8 @@ public: std::unique_ptr<Utils::QtcProcess> m_process; std::unique_ptr<IOutputParser> m_outputParserChain; ProcessParameters m_param; - QHash<QString, QPair<Utils::FileName, quint64>> m_filesCache; - QHash<QString, Utils::FileNameList> m_candidates; + Utils::FileInProjectFinder m_fileFinder; QByteArray deferredText; - quint64 m_cacheCounter = 0; bool m_ignoreReturnValue = false; bool m_skipFlush = false; @@ -194,11 +190,8 @@ void AbstractProcessStep::setIgnoreReturnValue(bool b) bool AbstractProcessStep::init() { - d->m_candidates.clear(); - const Utils::FileNameList fl = project()->files(Project::AllFiles); - for (const Utils::FileName &file : fl) - d->m_candidates[file.fileName()].push_back(file); - + d->m_fileFinder.setProjectDirectory(project()->projectDirectory()); + d->m_fileFinder.setProjectFiles(project()->files(Project::AllFiles)); return !d->m_process; } @@ -209,7 +202,7 @@ bool AbstractProcessStep::init() void AbstractProcessStep::doRun() { - QDir wd(d->m_param.effectiveWorkingDirectory()); + QDir wd(d->m_param.effectiveWorkingDirectory().toString()); if (!wd.exists()) { if (!wd.mkpath(wd.absolutePath())) { emit addOutput(tr("Could not create directory \"%1\"") @@ -220,8 +213,8 @@ void AbstractProcessStep::doRun() } } - QString effectiveCommand = d->m_param.effectiveCommand(); - if (!QFileInfo::exists(effectiveCommand)) { + const CommandLine effectiveCommand{d->m_param.effectiveCommand(), d->m_param.effectiveArguments()}; + if (!effectiveCommand.executable().exists()) { processStartupFailed(); finish(false); return; @@ -231,13 +224,13 @@ void AbstractProcessStep::doRun() d->m_process->setUseCtrlCStub(Utils::HostOsInfo::isWindowsHost()); d->m_process->setWorkingDirectory(wd.absolutePath()); d->m_process->setEnvironment(d->m_param.environment()); - d->m_process->setCommand(effectiveCommand, d->m_param.effectiveArguments()); + d->m_process->setCommand(effectiveCommand); connect(d->m_process.get(), &QProcess::readyReadStandardOutput, this, &AbstractProcessStep::processReadyReadStdOutput); connect(d->m_process.get(), &QProcess::readyReadStandardError, this, &AbstractProcessStep::processReadyReadStdError); - connect(d->m_process.get(), static_cast<void (QProcess::*)(int,QProcess::ExitStatus)>(&QProcess::finished), + connect(d->m_process.get(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &AbstractProcessStep::slotProcessFinished); d->m_process->start(); @@ -284,7 +277,7 @@ void AbstractProcessStep::cleanUp(QProcess *process) void AbstractProcessStep::processStarted() { emit addOutput(tr("Starting: \"%1\" %2") - .arg(QDir::toNativeSeparators(d->m_param.effectiveCommand()), + .arg(QDir::toNativeSeparators(d->m_param.effectiveCommand().toString()), d->m_param.prettyArguments()), BuildStep::OutputFormat::NormalMessage); } @@ -300,7 +293,7 @@ void AbstractProcessStep::processFinished(int exitCode, QProcess::ExitStatus sta if (d->m_outputParserChain) d->m_outputParserChain->flush(); - QString command = QDir::toNativeSeparators(d->m_param.effectiveCommand()); + QString command = QDir::toNativeSeparators(d->m_param.effectiveCommand().toString()); if (status == QProcess::NormalExit && exitCode == 0) { emit addOutput(tr("The process \"%1\" exited normally.").arg(command), BuildStep::OutputFormat::NormalMessage); @@ -322,7 +315,7 @@ void AbstractProcessStep::processFinished(int exitCode, QProcess::ExitStatus sta void AbstractProcessStep::processStartupFailed() { emit addOutput(tr("Could not start process \"%1\" %2") - .arg(QDir::toNativeSeparators(d->m_param.effectiveCommand()), + .arg(QDir::toNativeSeparators(d->m_param.effectiveCommand().toString()), d->m_param.prettyArguments()), BuildStep::OutputFormat::ErrorMessage); } @@ -437,44 +430,16 @@ void AbstractProcessStep::taskAdded(const Task &task, int linkedOutputLines, int Task editable(task); QString filePath = task.file.toString(); - - auto it = d->m_filesCache.find(filePath); - if (it != d->m_filesCache.end()) { - editable.file = it.value().first; - it.value().second = ++d->m_cacheCounter; - } else if (!filePath.isEmpty() && !filePath.startsWith('<') && !QDir::isAbsolutePath(filePath)) { - // We have no save way to decide which file in which subfolder - // is meant. Therefore we apply following heuristics: - // 1. Check if file is unique in whole project - // 2. Otherwise try again without any ../ - // 3. give up. - - QString sourceFilePath = filePath; - Utils::FileNameList possibleFiles = d->m_candidates.value(Utils::FileName::fromString(filePath).fileName()); - - if (possibleFiles.count() == 1) { - editable.file = possibleFiles.first(); - } else { - // More then one filename, so do a better compare - // Chop of any "../" - while (filePath.startsWith("../")) - filePath.remove(0, 3); - - int count = 0; - Utils::FileName possibleFilePath; - foreach (const Utils::FileName &fn, possibleFiles) { - if (fn.endsWith(filePath)) { - possibleFilePath = fn; - ++count; - } - } - if (count == 1) - editable.file = possibleFilePath; - else - qWarning() << "Could not find absolute location of file " << filePath; - } - - insertInCache(sourceFilePath, editable.file); + if (!filePath.isEmpty() && !filePath.startsWith('<') && !QDir::isAbsolutePath(filePath)) { + while (filePath.startsWith("../")) + filePath.remove(0, 3); + bool found = false; + const Utils::FilePathList candidates + = d->m_fileFinder.findFile(QUrl::fromLocalFile(filePath), &found); + if (found && candidates.size() == 1) + editable.file = candidates.first(); + else + qWarning() << "Could not find absolute location of file " << filePath; } emit addTask(editable, linkedOutputLines, skipLines); @@ -499,27 +464,7 @@ void AbstractProcessStep::slotProcessFinished(int, QProcess::ExitStatus) for (const QString &l : stdOutLine.split('\n')) stdError(l); - purgeCache(true); cleanUp(process); } -void AbstractProcessStep::purgeCache(bool useSoftLimit) -{ - const int limit = useSoftLimit ? CACHE_SOFT_LIMIT : CACHE_HARD_LIMIT; - if (d->m_filesCache.size() <= limit) - return; - - const quint64 minCounter = d->m_cacheCounter - static_cast<quint64>(limit); - std::remove_if(d->m_filesCache.begin(), d->m_filesCache.end(), - [minCounter](const QPair<Utils::FileName, quint64> &entry) { - return entry.second <= minCounter; - }); -} - -void AbstractProcessStep::insertInCache(const QString &relativePath, const Utils::FileName &absPath) -{ - purgeCache(false); - d->m_filesCache.insert(relativePath, qMakePair(absPath, ++d->m_cacheCounter)); -} - } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/abstractprocessstep.h b/src/plugins/projectexplorer/abstractprocessstep.h index 5d76bf7f4e..b21a6bdb25 100644 --- a/src/plugins/projectexplorer/abstractprocessstep.h +++ b/src/plugins/projectexplorer/abstractprocessstep.h @@ -29,7 +29,7 @@ #include <QProcess> -namespace Utils { class FileName; } +namespace Utils { class FilePath; } namespace ProjectExplorer { class IOutputParser; @@ -57,6 +57,7 @@ protected: ~AbstractProcessStep() override; bool init() override; void doRun() override; + virtual void finish(bool success); virtual void processStarted(); virtual void processFinished(int exitCode, QProcess::ExitStatus status); @@ -68,7 +69,6 @@ protected: void doCancel() override; private: - virtual void finish(bool success); void processReadyReadStdOutput(); void processReadyReadStdError(); @@ -80,9 +80,6 @@ private: void outputAdded(const QString &string, BuildStep::OutputFormat format); - void purgeCache(bool useSoftLimit); - void insertInCache(const QString &relativePath, const Utils::FileName &absPath); - class Private; Private *d; }; diff --git a/src/plugins/projectexplorer/addrunconfigdialog.cpp b/src/plugins/projectexplorer/addrunconfigdialog.cpp new file mode 100644 index 0000000000..647959eb15 --- /dev/null +++ b/src/plugins/projectexplorer/addrunconfigdialog.cpp @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "addrunconfigdialog.h" + +#include "project.h" +#include "target.h" + +#include <utils/itemviews.h> +#include <utils/qtcassert.h> +#include <utils/treemodel.h> + +#include <QDialogButtonBox> +#include <QHeaderView> +#include <QHBoxLayout> +#include <QItemSelectionModel> +#include <QLabel> +#include <QLineEdit> +#include <QPushButton> +#include <QRegExp> +#include <QSortFilterProxyModel> +#include <QVBoxLayout> + +using namespace Utils; + +namespace ProjectExplorer { +namespace Internal { + +const Qt::ItemDataRole IsCustomRole = Qt::UserRole; + +class CandidateTreeItem : public TreeItem +{ + Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::Internal::AddRunConfigDialog) +public: + CandidateTreeItem(const RunConfigurationCreationInfo &rci, const FilePath &projectRoot) + : m_creationInfo(rci), m_projectRoot(projectRoot) + { } + + RunConfigurationCreationInfo creationInfo() const { return m_creationInfo; } + +private: + QVariant data(int column, int role) const override + { + QTC_ASSERT(column < 2, return QVariant()); + if (role == IsCustomRole) + return m_creationInfo.projectFilePath.isEmpty(); + if (column == 0 && role == Qt::DisplayRole) + return m_creationInfo.displayName; + if (column == 1 && role == Qt::DisplayRole) { + FilePath displayPath = m_creationInfo.projectFilePath.relativeChildPath(m_projectRoot); + if (displayPath.isEmpty()) { + displayPath = m_creationInfo.projectFilePath; + QTC_CHECK(displayPath.isEmpty()); + } + return displayPath.isEmpty() ? tr("[none]") : displayPath.toUserOutput(); + } + return QVariant(); + } + + const RunConfigurationCreationInfo m_creationInfo; + const FilePath m_projectRoot; +}; + +class CandidatesModel : public TreeModel<TreeItem, CandidateTreeItem> +{ + Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::Internal::AddRunConfigDialog) +public: + CandidatesModel(Target *target, QObject *parent) : TreeModel(parent) + { + setHeader({tr("Name"), tr("Source")}); + for (const RunConfigurationCreationInfo &rci + : RunConfigurationFactory::creatorsForTarget(target)) { + rootItem()->appendChild(new CandidateTreeItem(rci, + target->project()->projectDirectory())); + } + } +}; + +class ProxyModel : public QSortFilterProxyModel +{ +public: + ProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { } + +private: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override + { + if (source_left.column() == 0) { + // Let's put the fallback candidates last. + const bool leftIsCustom = sourceModel()->data(source_left, IsCustomRole).toBool(); + const bool rightIsCustom = sourceModel()->data(source_right, IsCustomRole).toBool(); + if (leftIsCustom != rightIsCustom) + return rightIsCustom; + } + return QSortFilterProxyModel::lessThan(source_left, source_right); + } +}; + +class CandidatesTreeView : public TreeView +{ +public: + CandidatesTreeView(QWidget *parent) : TreeView(parent) + { + setUniformRowHeights(true); + } + +private: + QSize sizeHint() const override + { + const int width = columnWidth(0) + columnWidth(1); + const int height = qMin(model()->rowCount() + 10, 10) * rowHeight(model()->index(0, 0)) + + header()->sizeHint().height(); + return {width, height}; + } +}; + +AddRunConfigDialog::AddRunConfigDialog(Target *target, QWidget *parent) + : QDialog(parent), m_view(new CandidatesTreeView(this)) +{ + setWindowTitle(tr("Create Run Configuration")); + const auto model = new CandidatesModel(target, this); + const auto proxyModel = new ProxyModel(this); + proxyModel->setSourceModel(model); + const auto filterEdit = new QLineEdit(this); + m_view->setSelectionMode(TreeView::SingleSelection); + m_view->setSelectionBehavior(TreeView::SelectRows); + m_view->setSortingEnabled(true); + m_view->setModel(proxyModel); + m_view->resizeColumnToContents(0); + m_view->resizeColumnToContents(1); + m_view->sortByColumn(0, Qt::AscendingOrder); + const auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Create")); + + connect(filterEdit, &QLineEdit::textChanged, this, [proxyModel](const QString &text) { + proxyModel->setFilterRegExp(QRegExp(text, Qt::CaseInsensitive)); + }); + connect(m_view, &TreeView::doubleClicked, this, [this] { accept(); }); + const auto updateOkButton = [buttonBox, this] { + buttonBox->button(QDialogButtonBox::Ok) + ->setEnabled(m_view->selectionModel()->hasSelection()); + }; + connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, this, updateOkButton); + updateOkButton(); + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + const auto layout = new QVBoxLayout(this); + const auto filterLayout = new QHBoxLayout; + filterLayout->addWidget(new QLabel(tr("Filter candidates by name:"), this)); + filterLayout->addWidget(filterEdit); + layout->addLayout(filterLayout); + layout->addWidget(m_view); + layout->addWidget(buttonBox); +} + +void AddRunConfigDialog::accept() +{ + const QModelIndexList selected = m_view->selectionModel()->selectedRows(); + QTC_ASSERT(selected.count() == 1, return); + const auto * const proxyModel = static_cast<ProxyModel *>(m_view->model()); + const auto * const model = static_cast<CandidatesModel *>(proxyModel->sourceModel()); + const TreeItem * const item = model->itemForIndex(proxyModel->mapToSource(selected.first())); + QTC_ASSERT(item, return); + m_creationInfo = static_cast<const CandidateTreeItem *>(item)->creationInfo(); + QTC_ASSERT(m_creationInfo.id.isValid(), return); + QDialog::accept(); +} + +} // namespace Internal +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitconfigwidget.cpp b/src/plugins/projectexplorer/addrunconfigdialog.h index 4b9fbebd60..2214bed5a1 100644 --- a/src/plugins/projectexplorer/kitconfigwidget.cpp +++ b/src/plugins/projectexplorer/addrunconfigdialog.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -23,51 +23,33 @@ ** ****************************************************************************/ -#include "kitconfigwidget.h" +#pragma once -#include "kit.h" -#include "kitinformation.h" +#include "runconfiguration.h" -namespace ProjectExplorer { +#include <QDialog> -KitConfigWidget::KitConfigWidget(Kit *kit, const KitInformation *ki) : m_kit(kit), - m_kitInformation(ki), m_isSticky(kit->isSticky(ki->id())) -{ } +namespace Utils { class TreeView; } -Core::Id KitConfigWidget::kitInformationId() const -{ - return m_kitInformation->id(); -} +namespace ProjectExplorer { +class Target; -bool KitConfigWidget::isMutable() const -{ - return m_kit->isMutable(m_kitInformation->id()); -} +namespace Internal { -void KitConfigWidget::setMutable(bool b) +class AddRunConfigDialog : public QDialog { - m_kit->setMutable(m_kitInformation->id(), b); -} + Q_OBJECT +public: + AddRunConfigDialog(Target *target, QWidget *parent); -QString KitConfigWidget::msgManage() -{ - return tr("Manage..."); -} + RunConfigurationCreationInfo creationInfo() const { return m_creationInfo; } -void KitConfigWidget::setPalette(const QPalette &p) -{ - if (mainWidget()) - mainWidget()->setPalette(p); - if (buttonWidget()) - buttonWidget()->setPalette(p); -} +private: + void accept() override; -void KitConfigWidget::setStyle(QStyle *s) -{ - if (mainWidget()) - mainWidget()->setStyle(s); - if (buttonWidget()) - buttonWidget()->setStyle(s); -} + Utils::TreeView * const m_view; + RunConfigurationCreationInfo m_creationInfo; +}; +} // namespace Internal } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/allprojectsfilter.cpp b/src/plugins/projectexplorer/allprojectsfilter.cpp index 24371ad4e1..ab17127d73 100644 --- a/src/plugins/projectexplorer/allprojectsfilter.cpp +++ b/src/plugins/projectexplorer/allprojectsfilter.cpp @@ -59,7 +59,7 @@ void AllProjectsFilter::prepareSearch(const QString &entry) if (!fileIterator()) { QStringList paths; for (Project *project : SessionManager::projects()) - paths.append(Utils::transform(project->files(Project::AllFiles), &Utils::FileName::toString)); + paths.append(Utils::transform(project->files(Project::AllFiles), &Utils::FilePath::toString)); Utils::sort(paths); setFileIterator(new BaseFileFilter::ListIterator(paths)); } diff --git a/src/plugins/projectexplorer/allprojectsfind.cpp b/src/plugins/projectexplorer/allprojectsfind.cpp index aa0d7c6fbc..d41f21f1b7 100644 --- a/src/plugins/projectexplorer/allprojectsfind.cpp +++ b/src/plugins/projectexplorer/allprojectsfind.cpp @@ -88,7 +88,7 @@ Utils::FileIterator *AllProjectsFind::filesForProjects(const QStringList &nameFi QTextCodec *projectCodec = config->useGlobalSettings() ? Core::EditorManager::defaultTextCodec() : config->textCodec(); - const QStringList filteredFiles = filterFiles(Utils::transform(project->files(Project::AllFiles), &Utils::FileName::toString)); + const QStringList filteredFiles = filterFiles(Utils::transform(project->files(Project::AllFiles), &Utils::FilePath::toString)); for (const QString &fileName : filteredFiles) { QTextCodec *codec = openEditorEncodings.value(fileName); if (!codec) diff --git a/src/plugins/projectexplorer/ansifilterparser.cpp b/src/plugins/projectexplorer/ansifilterparser.cpp index 402dcf6f9b..a2db2c1a6f 100644 --- a/src/plugins/projectexplorer/ansifilterparser.cpp +++ b/src/plugins/projectexplorer/ansifilterparser.cpp @@ -168,7 +168,7 @@ void ProjectExplorerPlugin::testAnsiFilterOutputParser() QFETCH(QString, childStdErrLines); testbench.testParsing(input, inputChannel, - QList<Task>(), childStdOutLines, childStdErrLines, + Tasks(), childStdOutLines, childStdErrLines, QString()); } diff --git a/src/plugins/projectexplorer/applicationlauncher.cpp b/src/plugins/projectexplorer/applicationlauncher.cpp index 3aef28f090..3a9cde2baf 100644 --- a/src/plugins/projectexplorer/applicationlauncher.cpp +++ b/src/plugins/projectexplorer/applicationlauncher.cpp @@ -39,6 +39,7 @@ #include "devicesupport/deviceprocess.h" #include "projectexplorer.h" #include "projectexplorersettings.h" +#include "runcontrol.h" #include <QTextCodec> #include <QTimer> @@ -63,7 +64,6 @@ using namespace Internal; namespace Internal { - class ApplicationLauncherPrivate : public QObject { public: @@ -126,7 +126,7 @@ public: ApplicationLauncherPrivate::ApplicationLauncherPrivate(ApplicationLauncher *parent) : q(parent), m_outputCodec(QTextCodec::codecForLocale()) { - if (ProjectExplorerPlugin::projectExplorerSettings().mergeStdErrAndStdOut){ + if (ProjectExplorerPlugin::appOutputSettings().mergeChannels) { m_guiProcess.setProcessChannelMode(QProcess::MergedChannels); } else { m_guiProcess.setProcessChannelMode(QProcess::SeparateChannels); @@ -137,7 +137,7 @@ ApplicationLauncherPrivate::ApplicationLauncherPrivate(ApplicationLauncher *pare this, &ApplicationLauncherPrivate::readLocalStandardOutput); connect(&m_guiProcess, &QProcess::errorOccurred, this, &ApplicationLauncherPrivate::localGuiProcessError); - connect(&m_guiProcess, static_cast<void (QProcess::*)(int,QProcess::ExitStatus)>(&QProcess::finished), + connect(&m_guiProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &ApplicationLauncherPrivate::localProcessDone); connect(&m_guiProcess, &QProcess::started, this, &ApplicationLauncherPrivate::handleProcessStarted); @@ -152,8 +152,7 @@ ApplicationLauncherPrivate::ApplicationLauncherPrivate(ApplicationLauncher *pare this, &ApplicationLauncherPrivate::localConsoleProcessError); connect(&m_consoleProcess, &ConsoleProcess::processStopped, this, &ApplicationLauncherPrivate::localProcessDone); - connect(&m_consoleProcess, - static_cast<void (ConsoleProcess::*)(QProcess::ProcessError)>(&ConsoleProcess::error), + connect(&m_consoleProcess, QOverload<QProcess::ProcessError>::of(&ConsoleProcess::error), q, &ApplicationLauncher::error); #ifdef Q_OS_WIN @@ -379,7 +378,7 @@ void ApplicationLauncherPrivate::start(const Runnable &runnable, const IDevice:: #endif if (!m_useTerminal) { - m_guiProcess.setCommand(runnable.executable, runnable.commandLineArguments); + m_guiProcess.setCommand(runnable.commandLine()); m_guiProcess.closeWriteChannel(); m_guiProcess.start(); } else { diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp index 34f71efc24..df9fd503ba 100644 --- a/src/plugins/projectexplorer/appoutputpane.cpp +++ b/src/plugins/projectexplorer/appoutputpane.cpp @@ -24,47 +24,54 @@ ****************************************************************************/ #include "appoutputpane.h" + #include "projectexplorer.h" +#include "projectexplorerconstants.h" #include "projectexplorericons.h" -#include "projectexplorersettings.h" -#include "runconfiguration.h" +#include "runcontrol.h" #include "session.h" #include "windebuginterface.h" #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> -#include <coreplugin/outputwindow.h> -#include <coreplugin/find/basetextfind.h> #include <coreplugin/coreconstants.h> +#include <coreplugin/find/basetextfind.h> #include <coreplugin/icore.h> +#include <coreplugin/outputwindow.h> +#include <texteditor/behaviorsettings.h> #include <texteditor/fontsettings.h> #include <texteditor/texteditorsettings.h> -#include <texteditor/behaviorsettings.h> -#include <extensionsystem/pluginmanager.h> -#include <extensionsystem/invoker.h> +#include <extensionsystem/invoker.h> +#include <extensionsystem/pluginmanager.h> #include <utils/algorithm.h> #include <utils/outputformatter.h> #include <utils/qtcassert.h> #include <utils/utilsicons.h> #include <QAction> -#include <QVBoxLayout> +#include <QCheckBox> +#include <QHBoxLayout> +#include <QLabel> +#include <QLoggingCategory> +#include <QMenu> +#include <QSpinBox> +#include <QTabBar> #include <QTabWidget> #include <QToolButton> -#include <QTabBar> -#include <QMenu> - -#include <QDebug> +#include <QVBoxLayout> -enum { debug = 0 }; +static Q_LOGGING_CATEGORY(appOutputLog, "qtc.projectexplorer.appoutput", QtWarningMsg); using namespace ProjectExplorer; using namespace ProjectExplorer::Internal; +const char OPTIONS_PAGE_ID[] = "B.ProjectExplorer.AppOutputOptions"; + + static QObject *debuggerPlugin() { - return ExtensionSystem::PluginManager::getObjectByName(QLatin1String("DebuggerPlugin")); + return ExtensionSystem::PluginManager::getObjectByName("DebuggerPlugin"); } static QString msgAttachDebuggerTooltip(const QString &handleDescription = QString()) @@ -79,13 +86,19 @@ static void replaceAllChildWidgets(QLayout *layout, const QList<QWidget *> &newC while (QLayoutItem *child = layout->takeAt(0)) delete child; - foreach (QWidget *widget, newChildren) + for (QWidget *widget : newChildren) layout->addWidget(widget); } namespace { const char SETTINGS_KEY[] = "ProjectExplorer/AppOutput/Zoom"; const char C_APP_OUTPUT[] = "ProjectExplorer.ApplicationOutput"; +const char POP_UP_FOR_RUN_OUTPUT_KEY[] = "ProjectExplorer/Settings/ShowRunOutput"; +const char POP_UP_FOR_DEBUG_OUTPUT_KEY[] = "ProjectExplorer/Settings/ShowDebugOutput"; +const char CLEAN_OLD_OUTPUT_KEY[] = "ProjectExplorer/Settings/CleanOldAppOutput"; +const char MERGE_CHANNELS_KEY[] = "ProjectExplorer/Settings/MergeStdErrAndStdOut"; +const char WRAP_OUTPUT_KEY[] = "ProjectExplorer/Settings/WrapAppOutput"; +const char MAX_LINES_KEY[] = "ProjectExplorer/Settings/MaxAppOutputLines"; } namespace ProjectExplorer { @@ -147,11 +160,11 @@ void TabWidget::slotContextMenuRequested(const QPoint &pos) emit contextMenuRequested(pos, tabBar()->tabAt(pos)); } -AppOutputPane::RunControlTab::RunControlTab(RunControl *rc, Core::OutputWindow *w) : - runControl(rc), window(w) +AppOutputPane::RunControlTab::RunControlTab(RunControl *runControl, Core::OutputWindow *w) : + runControl(runControl), window(w) { - if (rc && w) - w->setFormatter(rc->outputFormatter()); + if (runControl && w) + w->setFormatter(runControl->outputFormatter()); } AppOutputPane::AppOutputPane() : @@ -164,16 +177,15 @@ AppOutputPane::AppOutputPane() : m_reRunButton(new QToolButton), m_stopButton(new QToolButton), m_attachButton(new QToolButton), - m_zoomInButton(new QToolButton), - m_zoomOutButton(new QToolButton), + m_settingsButton(new QToolButton), m_formatterWidget(new QWidget) { - setObjectName(QLatin1String("AppOutputPane")); // Used in valgrind engine + setObjectName("AppOutputPane"); // Used in valgrind engine + loadSettings(); // Rerun m_reRunButton->setIcon(Utils::Icons::RUN_SMALL_TOOLBAR.icon()); m_reRunButton->setToolTip(tr("Re-run this run-configuration")); - m_reRunButton->setAutoRaise(true); m_reRunButton->setEnabled(false); connect(m_reRunButton, &QToolButton::clicked, this, &AppOutputPane::reRunRunControl); @@ -187,7 +199,6 @@ AppOutputPane::AppOutputPane() : cmd->setDescription(m_stopAction->toolTip()); m_stopButton->setDefaultAction(cmd->action()); - m_stopButton->setAutoRaise(true); connect(m_stopAction, &QAction::triggered, this, &AppOutputPane::stopRunControl); @@ -196,24 +207,19 @@ AppOutputPane::AppOutputPane() : m_attachButton->setToolTip(msgAttachDebuggerTooltip()); m_attachButton->setEnabled(false); m_attachButton->setIcon(Icons::DEBUG_START_SMALL_TOOLBAR.icon()); - m_attachButton->setAutoRaise(true); connect(m_attachButton, &QToolButton::clicked, this, &AppOutputPane::attachToRunControl); - m_zoomInButton->setToolTip(tr("Increase Font Size")); - m_zoomInButton->setIcon(Utils::Icons::PLUS_TOOLBAR.icon()); - m_zoomInButton->setAutoRaise(true); + connect(this, &Core::IOutputPane::zoomIn, this, &AppOutputPane::zoomIn); + connect(this, &Core::IOutputPane::zoomOut, this, &AppOutputPane::zoomOut); + connect(this, &IOutputPane::resetZoom, this, &AppOutputPane::resetZoom); - connect(m_zoomInButton, &QToolButton::clicked, - this, &AppOutputPane::zoomIn); - - m_zoomOutButton->setToolTip(tr("Decrease Font Size")); - m_zoomOutButton->setIcon(Utils::Icons::MINUS.icon()); - m_zoomOutButton->setAutoRaise(true); - - connect(m_zoomOutButton, &QToolButton::clicked, - this, &AppOutputPane::zoomOut); + m_settingsButton->setToolTip(tr("Open Settings Page")); + m_settingsButton->setIcon(Utils::Icons::SETTINGS_TOOLBAR.icon()); + connect(m_settingsButton, &QToolButton::clicked, this, [] { + Core::ICore::showOptionsDialog(OPTIONS_PAGE_ID); + }); auto formatterWidgetsLayout = new QHBoxLayout; formatterWidgetsLayout->setContentsMargins(QMargins()); @@ -236,42 +242,26 @@ AppOutputPane::AppOutputPane() : m_mainWidget->setLayout(layout); - connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::fontSettingsChanged, - this, &AppOutputPane::updateFontSettings); - - connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::behaviorSettingsChanged, - this, &AppOutputPane::updateBehaviorSettings); - connect(SessionManager::instance(), &SessionManager::aboutToUnloadSession, this, &AppOutputPane::aboutToUnloadSession); - connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged, - this, &AppOutputPane::updateFromSettings); - - QSettings *settings = Core::ICore::settings(); - m_zoom = settings->value(QLatin1String(SETTINGS_KEY), 0).toFloat(); - connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested, - this, &AppOutputPane::saveSettings); + setupFilterUi("AppOutputPane.Filter"); + setFilteringEnabled(false); + setZoomButtonsEnabled(false); + setupContext("Core.AppOutputPane", m_mainWidget); } AppOutputPane::~AppOutputPane() { - if (debug) - qDebug() << "OutputPane::~OutputPane: Entries left" << m_runControlTabs.size(); + qCDebug(appOutputLog) << "AppOutputPane::~AppOutputPane: Entries left" << m_runControlTabs.size(); - foreach (const RunControlTab &rt, m_runControlTabs) { + for (const RunControlTab &rt : qAsConst(m_runControlTabs)) { delete rt.window; delete rt.runControl; } delete m_mainWidget; } -void AppOutputPane::saveSettings() -{ - QSettings *settings = Core::ICore::settings(); - settings->setValue(QLatin1String(SETTINGS_KEY), m_zoom); -} - int AppOutputPane::currentIndex() const { if (const QWidget *w = m_tabWidget->currentWidget()) @@ -337,8 +327,8 @@ QWidget *AppOutputPane::outputWidget(QWidget *) QList<QWidget*> AppOutputPane::toolBarWidgets() const { - return { m_reRunButton, m_stopButton, m_attachButton, m_zoomInButton, - m_zoomOutButton, m_formatterWidget }; + return QList<QWidget *>{m_reRunButton, m_stopButton, m_attachButton, m_settingsButton, + m_formatterWidget} + IOutputPane::toolBarWidgets(); } QString AppOutputPane::displayName() const @@ -381,18 +371,13 @@ void AppOutputPane::setFocus() m_tabWidget->currentWidget()->setFocus(); } -void AppOutputPane::updateFontSettings() +void AppOutputPane::updateFilter() { - QFont f = TextEditor::TextEditorSettings::fontSettings().font(); - foreach (const RunControlTab &rcTab, m_runControlTabs) - rcTab.window->setBaseFont(f); -} - -void AppOutputPane::updateBehaviorSettings() -{ - bool zoomEnabled = TextEditor::TextEditorSettings::behaviorSettings().m_scrollWheelZooming; - foreach (const RunControlTab &rcTab, m_runControlTabs) - rcTab.window->setWheelZoomEnabled(zoomEnabled); + const int index = currentIndex(); + if (index != -1) { + m_runControlTabs.at(index).window->updateFilterProperties( + filterText(), filterCaseSensitivity(), filterUsesRegexp()); + } } void AppOutputPane::createNewOutputWindow(RunControl *rc) @@ -407,8 +392,10 @@ void AppOutputPane::createNewOutputWindow(RunControl *rc) this, &AppOutputPane::slotRunControlFinished); connect(rc, &RunControl::applicationProcessHandleChanged, this, &AppOutputPane::enableDefaultButtons); - connect(rc, &RunControl::appendMessageRequested, - this, &AppOutputPane::appendMessage); + connect(rc, &RunControl::appendMessage, + this, [this, rc](const QString &out, Utils::OutputFormat format) { + appendMessage(rc, out, format); + }); // First look if we can reuse a tab const Runnable thisRunnable = rc->runnable(); @@ -435,42 +422,55 @@ void AppOutputPane::createNewOutputWindow(RunControl *rc) m_tabWidget->setTabText(tabIndex, rc->displayName()); tab.window->scrollToBottom(); - if (debug) - qDebug() << "OutputPane::createNewOutputWindow: Reusing tab" << tabIndex << " for " << rc; + qCDebug(appOutputLog) << "AppOutputPane::createNewOutputWindow: Reusing tab" + << tabIndex << "for" << rc; return; } // Create new static int counter = 0; Core::Id contextId = Core::Id(C_APP_OUTPUT).withSuffix(counter++); Core::Context context(contextId); - Core::OutputWindow *ow = new Core::OutputWindow(context, m_tabWidget); + Core::OutputWindow *ow = new Core::OutputWindow(context, SETTINGS_KEY, m_tabWidget); ow->setWindowTitle(tr("Application Output Window")); ow->setWindowIcon(Icons::WINDOW.icon()); - ow->setWordWrapEnabled(ProjectExplorerPlugin::projectExplorerSettings().wrapAppOutput); - ow->setMaxCharCount(ProjectExplorerPlugin::projectExplorerSettings().maxAppOutputChars); - ow->setWheelZoomEnabled(TextEditor::TextEditorSettings::behaviorSettings().m_scrollWheelZooming); - ow->setBaseFont(TextEditor::TextEditorSettings::fontSettings().font()); - ow->setFontZoom(m_zoom); + ow->setWordWrapEnabled(m_settings.wrapOutput); + ow->setMaxCharCount(m_settings.maxCharCount); + + auto updateFontSettings = [ow] { + ow->setBaseFont(TextEditor::TextEditorSettings::fontSettings().font()); + }; + + auto updateBehaviorSettings = [ow] { + ow->setWheelZoomEnabled( + TextEditor::TextEditorSettings::behaviorSettings().m_scrollWheelZooming); + }; + + updateFontSettings(); + updateBehaviorSettings(); connect(ow, &Core::OutputWindow::wheelZoom, this, [this, ow]() { - m_zoom = ow->fontZoom(); - foreach (const RunControlTab &tab, m_runControlTabs) - tab.window->setFontZoom(m_zoom); + float fontZoom = ow->fontZoom(); + for (const RunControlTab &tab : qAsConst(m_runControlTabs)) + tab.window->setFontZoom(fontZoom); }); + connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::fontSettingsChanged, + this, updateFontSettings); + connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::behaviorSettingsChanged, + this, updateBehaviorSettings); auto *agg = new Aggregation::Aggregate; agg->add(ow); agg->add(new Core::BaseTextFind(ow)); m_runControlTabs.push_back(RunControlTab(rc, ow)); m_tabWidget->addTab(ow, rc->displayName()); - if (debug) - qDebug() << "OutputPane::createNewOutputWindow: Adding tab for " << rc; + qCDebug(appOutputLog) << "AppOutputPane::createNewOutputWindow: Adding tab for" << rc; updateCloseActions(); + setFilteringEnabled(m_tabWidget->count() > 0); } void AppOutputPane::handleOldOutput(Core::OutputWindow *window) const { - if (ProjectExplorerPlugin::projectExplorerSettings().cleanOldAppOutput) + if (m_settings.cleanOldOutput) window->clear(); else window->grayOutOldContent(); @@ -478,9 +478,9 @@ void AppOutputPane::handleOldOutput(Core::OutputWindow *window) const void AppOutputPane::updateFromSettings() { - foreach (const RunControlTab &tab, m_runControlTabs) { - tab.window->setWordWrapEnabled(ProjectExplorerPlugin::projectExplorerSettings().wrapAppOutput); - tab.window->setMaxCharCount(ProjectExplorerPlugin::projectExplorerSettings().maxAppOutputChars); + for (const RunControlTab &tab : qAsConst(m_runControlTabs)) { + tab.window->setWordWrapEnabled(m_settings.wrapOutput); + tab.window->setMaxCharCount(m_settings.maxCharCount); } } @@ -492,7 +492,7 @@ void AppOutputPane::appendMessage(RunControl *rc, const QString &out, Utils::Out QString stringToWrite; if (format == Utils::NormalMessageFormat || format == Utils::ErrorMessageFormat) { stringToWrite = QTime::currentTime().toString(); - stringToWrite += QLatin1String(": "); + stringToWrite += ": "; } stringToWrite += out; window->appendMessage(stringToWrite, format); @@ -505,6 +505,36 @@ void AppOutputPane::appendMessage(RunControl *rc, const QString &out, Utils::Out } } +void AppOutputPane::setSettings(const AppOutputSettings &settings) +{ + m_settings = settings; + storeSettings(); + updateFromSettings(); +} + +void AppOutputPane::storeSettings() const +{ + QSettings * const s = Core::ICore::settings(); + s->setValue(POP_UP_FOR_RUN_OUTPUT_KEY, m_settings.popUpForRunOutput); + s->setValue(POP_UP_FOR_DEBUG_OUTPUT_KEY, m_settings.popUpForDebugOutput); + s->setValue(CLEAN_OLD_OUTPUT_KEY, m_settings.cleanOldOutput); + s->setValue(MERGE_CHANNELS_KEY, m_settings.mergeChannels); + s->setValue(WRAP_OUTPUT_KEY, m_settings.wrapOutput); + s->setValue(MAX_LINES_KEY, m_settings.maxCharCount / 100); +} + +void AppOutputPane::loadSettings() +{ + QSettings * const s = Core::ICore::settings(); + m_settings.popUpForRunOutput = s->value(POP_UP_FOR_RUN_OUTPUT_KEY, true).toBool(); + m_settings.popUpForDebugOutput = s->value(POP_UP_FOR_DEBUG_OUTPUT_KEY, false).toBool(); + m_settings.cleanOldOutput = s->value(CLEAN_OLD_OUTPUT_KEY, false).toBool(); + m_settings.mergeChannels = s->value(MERGE_CHANNELS_KEY, false).toBool(); + m_settings.wrapOutput = s->value(WRAP_OUTPUT_KEY, true).toBool(); + m_settings.maxCharCount = s->value(MAX_LINES_KEY, + Core::Constants::DEFAULT_MAX_CHAR_COUNT).toInt() * 100; +} + void AppOutputPane::showTabFor(RunControl *rc) { m_tabWidget->setCurrentIndex(tabWidgetIndexOf(indexOf(rc))); @@ -552,8 +582,7 @@ void AppOutputPane::stopRunControl() rc->forceStop(); } - if (debug) - qDebug() << "OutputPane::stopRunControl " << rc; + qCDebug(appOutputLog) << "AppOutputPane::stopRunControl" << rc; } void AppOutputPane::closeTabs(CloseTabMode mode) @@ -577,8 +606,7 @@ void AppOutputPane::closeTab(int tabIndex, CloseTabMode closeTabMode) RunControl *runControl = m_runControlTabs[index].runControl; Core::OutputWindow *window = m_runControlTabs[index].window; - if (debug) - qDebug() << "OutputPane::closeTab tab " << tabIndex << runControl << window; + qCDebug(appOutputLog) << "AppOutputPane::closeTab tab" << tabIndex << runControl << window; // Prompt user to stop if (closeTabMode == CloseTabWithPrompt) { QWidget *tabWidget = m_tabWidget->widget(tabIndex); @@ -599,6 +627,7 @@ void AppOutputPane::closeTab(int tabIndex, CloseTabMode closeTabMode) runControl->initiateFinish(); // Will self-destruct. m_runControlTabs.removeAt(index); updateCloseActions(); + setFilteringEnabled(m_tabWidget->count() > 0); if (m_runControlTabs.isEmpty()) hide(); @@ -623,22 +652,22 @@ void AppOutputPane::enableDefaultButtons() enableButtons(currentRunControl()); } -void AppOutputPane::zoomIn() +void AppOutputPane::zoomIn(int range) { - foreach (const RunControlTab &tab, m_runControlTabs) - tab.window->zoomIn(1); - if (m_runControlTabs.isEmpty()) - return; - m_zoom = m_runControlTabs.first().window->fontZoom(); + for (const RunControlTab &tab : qAsConst(m_runControlTabs)) + tab.window->zoomIn(range); } -void AppOutputPane::zoomOut() +void AppOutputPane::zoomOut(int range) { - foreach (const RunControlTab &tab, m_runControlTabs) - tab.window->zoomOut(1); - if (m_runControlTabs.isEmpty()) - return; - m_zoom = m_runControlTabs.first().window->fontZoom(); + for (const RunControlTab &tab : qAsConst(m_runControlTabs)) + tab.window->zoomOut(range); +} + +void AppOutputPane::resetZoom() +{ + for (const RunControlTab &tab : qAsConst(m_runControlTabs)) + tab.window->resetZoom(); } void AppOutputPane::enableButtons(const RunControl *rc) @@ -658,8 +687,7 @@ void AppOutputPane::enableButtons(const RunControl *rc) m_attachButton->setEnabled(false); m_attachButton->setToolTip(msgAttachDebuggerTooltip()); } - m_zoomInButton->setEnabled(true); - m_zoomOutButton->setEnabled(true); + setZoomButtonsEnabled(true); replaceAllChildWidgets(m_formatterWidget->layout(), rc->outputFormatter() ? rc->outputFormatter()->toolbarWidgets() : @@ -670,8 +698,7 @@ void AppOutputPane::enableButtons(const RunControl *rc) m_attachButton->setEnabled(false); m_attachButton->setToolTip(msgAttachDebuggerTooltip()); m_stopAction->setEnabled(false); - m_zoomInButton->setEnabled(false); - m_zoomOutButton->setEnabled(false); + setZoomButtonsEnabled(false); } m_formatterWidget->setVisible(m_formatterWidget->layout()->count()); } @@ -680,7 +707,10 @@ void AppOutputPane::tabChanged(int i) { const int index = indexOf(m_tabWidget->widget(i)); if (i != -1 && index != -1) { - enableButtons(m_runControlTabs.at(index).runControl); + const RunControlTab &controlTab = m_runControlTabs[index]; + controlTab.window->updateFilterProperties(filterText(), filterCaseSensitivity(), + filterUsesRegexp()); + enableButtons(controlTab.runControl); } else { enableDefaultButtons(); } @@ -729,9 +759,8 @@ void AppOutputPane::slotRunControlFinished2(RunControl *sender) // Enable buttons for current RunControl *current = currentRunControl(); - if (debug) - qDebug() << "OutputPane::runControlFinished" << sender << senderIndex - << " current " << current << m_runControlTabs.size(); + qCDebug(appOutputLog) << "AppOutputPane::runControlFinished" << sender << senderIndex + << "current" << current << m_runControlTabs.size(); if (current && current == sender) enableButtons(current); @@ -773,5 +802,87 @@ bool AppOutputPane::canNavigate() const return false; } +class AppOutputSettingsPage::SettingsWidget : public QWidget +{ + Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::Internal::AppOutputSettingsPage) +public: + SettingsWidget() + { + const AppOutputSettings &settings = ProjectExplorerPlugin::appOutputSettings(); + m_wrapOutputCheckBox.setText(tr("Word-wrap output")); + m_wrapOutputCheckBox.setChecked(settings.wrapOutput); + m_cleanOldOutputCheckBox.setText(tr("Clear old output on a new run")); + m_cleanOldOutputCheckBox.setChecked(settings.cleanOldOutput); + m_mergeChannelsCheckBox.setText(tr("Merge stderr and stdout")); + m_mergeChannelsCheckBox.setChecked(settings.mergeChannels); + m_popUpForRunOutputCheckBox.setText(tr("Open pane on output when running")); + m_popUpForRunOutputCheckBox.setChecked(settings.popUpForRunOutput); + m_popUpForDebugOutputCheckBox.setText(tr("Open pane on output when debugging")); + m_popUpForDebugOutputCheckBox.setChecked(settings.popUpForDebugOutput); + m_maxCharsBox.setMaximum(100000000); + m_maxCharsBox.setValue(settings.maxCharCount); + const auto layout = new QVBoxLayout(this); + layout->addWidget(&m_wrapOutputCheckBox); + layout->addWidget(&m_cleanOldOutputCheckBox); + layout->addWidget(&m_mergeChannelsCheckBox); + layout->addWidget(&m_popUpForRunOutputCheckBox); + layout->addWidget(&m_popUpForDebugOutputCheckBox); + const auto maxCharsLayout = new QHBoxLayout; + const QString msg = tr("Limit output to %1 characters"); + const QStringList parts = msg.split("%1") << QString() << QString(); + maxCharsLayout->addWidget(new QLabel(parts.at(0).trimmed())); + maxCharsLayout->addWidget(&m_maxCharsBox); + maxCharsLayout->addWidget(new QLabel(parts.at(1).trimmed())); + maxCharsLayout->addStretch(1); + layout->addLayout(maxCharsLayout); + layout->addStretch(1); + } + + AppOutputSettings settings() const + { + AppOutputSettings s; + s.wrapOutput = m_wrapOutputCheckBox.isChecked(); + s.cleanOldOutput = m_cleanOldOutputCheckBox.isChecked(); + s.mergeChannels = m_mergeChannelsCheckBox.isChecked(); + s.popUpForRunOutput = m_popUpForRunOutputCheckBox.isChecked(); + s.popUpForDebugOutput = m_popUpForDebugOutputCheckBox.isChecked(); + s.maxCharCount = m_maxCharsBox.value(); + return s; + } + +private: + QCheckBox m_wrapOutputCheckBox; + QCheckBox m_cleanOldOutputCheckBox; + QCheckBox m_mergeChannelsCheckBox; + QCheckBox m_popUpForRunOutputCheckBox; + QCheckBox m_popUpForDebugOutputCheckBox; + QSpinBox m_maxCharsBox; +}; + +AppOutputSettingsPage::AppOutputSettingsPage() +{ + setId(OPTIONS_PAGE_ID); + setDisplayName(tr("Application Output")); + setCategory(Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); +} + +QWidget *AppOutputSettingsPage::widget() +{ + if (!m_widget) + m_widget = new SettingsWidget; + return m_widget; +} + +void AppOutputSettingsPage::apply() +{ + if (m_widget) + ProjectExplorerPlugin::setAppOutputSettings(m_widget->settings()); +} + +void AppOutputSettingsPage::finish() +{ + delete m_widget; +} + #include "appoutputpane.moc" diff --git a/src/plugins/projectexplorer/appoutputpane.h b/src/plugins/projectexplorer/appoutputpane.h index fc7ac6e05e..6d7936042e 100644 --- a/src/plugins/projectexplorer/appoutputpane.h +++ b/src/plugins/projectexplorer/appoutputpane.h @@ -25,13 +25,16 @@ #pragma once -#include <QPointer> -#include <QVector> +#include "projectexplorersettings.h" #include <coreplugin/ioutputpane.h> +#include <coreplugin/dialogs/ioptionspage.h> #include <utils/outputformat.h> +#include <QPointer> +#include <QVector> + QT_BEGIN_NAMESPACE class QTabWidget; class QToolButton; @@ -98,6 +101,9 @@ public: void appendMessage(ProjectExplorer::RunControl *rc, const QString &out, Utils::OutputFormat format); + const AppOutputSettings &settings() const { return m_settings; } + void setSettings(const AppOutputSettings &settings); + private: void reRunRunControl(); void stopRunControl(); @@ -112,8 +118,9 @@ private: void updateFromSettings(); void enableDefaultButtons(); - void zoomIn(); - void zoomOut(); + void zoomIn(int range); + void zoomOut(int range); + void resetZoom(); void enableButtons(const RunControl *rc); @@ -136,9 +143,10 @@ private: int tabWidgetIndexOf(int runControlIndex) const; void handleOldOutput(Core::OutputWindow *window) const; void updateCloseActions(); - void updateFontSettings(); - void saveSettings(); - void updateBehaviorSettings(); + void updateFilter() override; + + void loadSettings(); + void storeSettings() const; QWidget *m_mainWidget; TabWidget *m_tabWidget; @@ -151,10 +159,25 @@ private: QToolButton *m_reRunButton; QToolButton *m_stopButton; QToolButton *m_attachButton; - QToolButton *m_zoomInButton; - QToolButton *m_zoomOutButton; + QToolButton * const m_settingsButton; QWidget *m_formatterWidget; - float m_zoom; + AppOutputSettings m_settings; +}; + +class AppOutputSettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + AppOutputSettingsPage(); + +private: + QWidget *widget() override; + void apply() override; + void finish() override; + + class SettingsWidget; + QPointer<SettingsWidget> m_widget; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/baseprojectwizarddialog.cpp b/src/plugins/projectexplorer/baseprojectwizarddialog.cpp index cb0734f226..14551d1d44 100644 --- a/src/plugins/projectexplorer/baseprojectwizarddialog.cpp +++ b/src/plugins/projectexplorer/baseprojectwizarddialog.cpp @@ -142,7 +142,7 @@ void BaseProjectWizardDialog::slotAccepted() { if (d->introPage->useAsDefaultPath()) { // Store the path as default path for new projects if desired. - Core::DocumentManager::setProjectsDirectory(Utils::FileName::fromString(path())); + Core::DocumentManager::setProjectsDirectory(Utils::FilePath::fromString(path())); Core::DocumentManager::setUseProjectsDirectory(true); } } diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp index 7973ed4c05..e267822b97 100644 --- a/src/plugins/projectexplorer/buildconfiguration.cpp +++ b/src/plugins/projectexplorer/buildconfiguration.cpp @@ -28,10 +28,12 @@ #include "buildenvironmentwidget.h" #include "buildinfo.h" #include "buildsteplist.h" +#include "namedwidget.h" #include "kit.h" #include "kitinformation.h" #include "kitmanager.h" #include "project.h" +#include "projectconfigurationaspects.h" #include "projectexplorer.h" #include "projectexplorerconstants.h" #include "projectmacroexpander.h" @@ -40,13 +42,15 @@ #include <coreplugin/idocument.h> -#include <utils/qtcassert.h> -#include <utils/macroexpander.h> #include <utils/algorithm.h> -#include <utils/mimetypes/mimetype.h> +#include <utils/detailswidget.h> +#include <utils/macroexpander.h> #include <utils/mimetypes/mimedatabase.h> +#include <utils/mimetypes/mimetype.h> +#include <utils/qtcassert.h> #include <QDebug> +#include <QFormLayout> static const char BUILD_STEP_LIST_COUNT[] = "ProjectExplorer.BuildConfiguration.BuildStepListCount"; static const char BUILD_STEP_LIST_PREFIX[] = "ProjectExplorer.BuildConfiguration.BuildStepList."; @@ -82,27 +86,74 @@ BuildConfiguration::BuildConfiguration(Target *target, Core::Id id) // Many macroexpanders are based on the current project, so they may change the environment: connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged, this, &BuildConfiguration::updateCacheAndEmitEnvironmentChanged); + + m_buildDirectoryAspect = addAspect<BaseStringAspect>(); + m_buildDirectoryAspect->setSettingsKey(BUILDDIRECTORY_KEY); + m_buildDirectoryAspect->setLabelText(tr("Build directory:")); + m_buildDirectoryAspect->setDisplayStyle(BaseStringAspect::PathChooserDisplay); + m_buildDirectoryAspect->setExpectedKind(Utils::PathChooser::Directory); + m_buildDirectoryAspect->setBaseFileName(target->project()->projectDirectory()); + m_buildDirectoryAspect->setEnvironment(environment()); + connect(m_buildDirectoryAspect, &BaseStringAspect::changed, + this, &BuildConfiguration::buildDirectoryChanged); + + connect(this, &BuildConfiguration::environmentChanged, this, [this] { + m_buildDirectoryAspect->setEnvironment(environment()); + }); } -Utils::FileName BuildConfiguration::buildDirectory() const +Utils::FilePath BuildConfiguration::buildDirectory() const { - const QString path = QDir::cleanPath(macroExpander()->expand(environment().expandVariables(m_buildDirectory.toString()))); - return Utils::FileName::fromString(QDir::cleanPath(QDir(target()->project()->projectDirectory().toString()).absoluteFilePath(path))); + QString path = environment().expandVariables(m_buildDirectoryAspect->value().trimmed()); + path = QDir::cleanPath(macroExpander()->expand(path)); + return Utils::FilePath::fromString(QDir::cleanPath(QDir(target()->project()->projectDirectory().toString()).absoluteFilePath(path))); } -Utils::FileName BuildConfiguration::rawBuildDirectory() const +Utils::FilePath BuildConfiguration::rawBuildDirectory() const { - return m_buildDirectory; + return m_buildDirectoryAspect->fileName(); } -void BuildConfiguration::setBuildDirectory(const Utils::FileName &dir) +void BuildConfiguration::setBuildDirectory(const Utils::FilePath &dir) { - if (dir == m_buildDirectory) + if (dir == m_buildDirectoryAspect->fileName()) return; - m_buildDirectory = dir; + m_buildDirectoryAspect->setFileName(dir); emitBuildDirectoryChanged(); } +NamedWidget *BuildConfiguration::createConfigWidget() +{ + NamedWidget *named = new NamedWidget; + named->setDisplayName(m_configWidgetDisplayName); + + QWidget *widget = nullptr; + + if (m_configWidgetHasFrame) { + auto container = new Utils::DetailsWidget(named); + widget = new QWidget(container); + container->setState(Utils::DetailsWidget::NoSummary); + container->setWidget(widget); + + auto vbox = new QVBoxLayout(named); + vbox->setMargin(0); + vbox->addWidget(container); + } else { + widget = named; + } + + auto formLayout = new QFormLayout(widget); + formLayout->setMargin(0); + formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); + + for (ProjectConfigurationAspect *aspect : aspects()) { + if (aspect->isVisible()) + aspect->addToConfigurationLayout(formLayout); + } + + return named; +} + void BuildConfiguration::initialize(const BuildInfo &info) { setDisplayName(info.displayName); @@ -133,7 +184,6 @@ QVariantMap BuildConfiguration::toMap() const QVariantMap map(ProjectConfiguration::toMap()); map.insert(QLatin1String(CLEAR_SYSTEM_ENVIRONMENT_KEY), m_clearSystemEnvironment); map.insert(QLatin1String(USER_ENVIRONMENT_CHANGES_KEY), Utils::EnvironmentItem::toStringList(m_userEnvironmentChanges)); - map.insert(QLatin1String(BUILDDIRECTORY_KEY), m_buildDirectory.toString()); map.insert(QLatin1String(BUILD_STEP_LIST_COUNT), m_stepLists.count()); for (int i = 0; i < m_stepLists.count(); ++i) @@ -146,7 +196,6 @@ bool BuildConfiguration::fromMap(const QVariantMap &map) { m_clearSystemEnvironment = map.value(QLatin1String(CLEAR_SYSTEM_ENVIRONMENT_KEY)).toBool(); m_userEnvironmentChanges = Utils::EnvironmentItem::fromStringList(map.value(QLatin1String(USER_ENVIRONMENT_CHANGES_KEY)).toStringList()); - m_buildDirectory = Utils::FileName::fromString(map.value(QLatin1String(BUILDDIRECTORY_KEY)).toString()); updateCacheAndEmitEnvironmentChanged(); @@ -194,6 +243,31 @@ void BuildConfiguration::emitBuildDirectoryChanged() } } +ProjectExplorer::BaseStringAspect *BuildConfiguration::buildDirectoryAspect() const +{ + return m_buildDirectoryAspect; +} + +void BuildConfiguration::setConfigWidgetDisplayName(const QString &display) +{ + m_configWidgetDisplayName = display; +} + +void BuildConfiguration::setBuildDirectoryHistoryCompleter(const QString &history) +{ + m_buildDirectoryAspect->setHistoryCompleter(history); +} + +void BuildConfiguration::setConfigWidgetHasFrame(bool configWidgetHasFrame) +{ + m_configWidgetHasFrame = configWidgetHasFrame; +} + +void BuildConfiguration::setBuildDirectorySettingsKey(const QString &key) +{ + m_buildDirectoryAspect->setSettingsKey(key); +} + Target *BuildConfiguration::target() const { return static_cast<Target *>(parent()); @@ -299,20 +373,16 @@ bool BuildConfiguration::isActive() const * PATH. This is used to in build configurations targeting broken build systems * to provide hints about which compiler to use. */ -void BuildConfiguration::prependCompilerPathToEnvironment(Utils::Environment &env) const -{ - return prependCompilerPathToEnvironment(target()->kit(), env); -} void BuildConfiguration::prependCompilerPathToEnvironment(Kit *k, Utils::Environment &env) { const ToolChain *tc - = ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::CXX_LANGUAGE_ID); + = ToolChainKitAspect::toolChain(k, ProjectExplorer::Constants::CXX_LANGUAGE_ID); if (!tc) return; - const Utils::FileName compilerDir = tc->compilerCommand().parentDir(); + const Utils::FilePath compilerDir = tc->compilerCommand().parentDir(); if (!compilerDir.isEmpty()) env.prependOrSetPath(compilerDir.toString()); } @@ -334,7 +404,7 @@ BuildConfigurationFactory::~BuildConfigurationFactory() g_buildConfigurationFactories.removeOne(this); } -const QList<Task> BuildConfigurationFactory::reportIssues(ProjectExplorer::Kit *kit, const QString &projectPath, +const Tasks BuildConfigurationFactory::reportIssues(ProjectExplorer::Kit *kit, const QString &projectPath, const QString &buildDir) const { if (m_issueReporter) @@ -363,7 +433,7 @@ bool BuildConfigurationFactory::supportsTargetDeviceType(Core::Id id) const BuildConfigurationFactory *BuildConfigurationFactory::find(const Kit *k, const QString &projectPath) { QTC_ASSERT(k, return nullptr); - const Core::Id deviceType = DeviceTypeKitInformation::deviceTypeId(k); + const Core::Id deviceType = DeviceTypeKitAspect::deviceTypeId(k); for (BuildConfigurationFactory *factory : g_buildConfigurationFactories) { if (Utils::mimeTypeForFile(projectPath).matchesName(factory->m_supportedProjectMimeTypeName) && factory->supportsTargetDeviceType(deviceType)) @@ -405,7 +475,7 @@ bool BuildConfigurationFactory::canHandle(const Target *target) const if (containsType(target->project()->projectIssues(target->kit()), Task::TaskType::Error)) return false; - if (!supportsTargetDeviceType(DeviceTypeKitInformation::deviceTypeId(target->kit()))) + if (!supportsTargetDeviceType(DeviceTypeKitAspect::deviceTypeId(target->kit()))) return false; return true; diff --git a/src/plugins/projectexplorer/buildconfiguration.h b/src/plugins/projectexplorer/buildconfiguration.h index c40a9d82a6..c9ba394946 100644 --- a/src/plugins/projectexplorer/buildconfiguration.h +++ b/src/plugins/projectexplorer/buildconfiguration.h @@ -27,20 +27,20 @@ #include "projectexplorer_export.h" #include "projectconfiguration.h" +#include "task.h" #include <utils/environment.h> #include <utils/fileutils.h> namespace ProjectExplorer { +class BaseStringAspect; class BuildInfo; -class NamedWidget; class BuildStepList; -class Node; class Kit; +class NamedWidget; +class Node; class Target; -class Task; -class IOutputParser; class PROJECTEXPLORER_EXPORT BuildConfiguration : public ProjectConfiguration { @@ -51,11 +51,11 @@ protected: explicit BuildConfiguration(Target *target, Core::Id id); public: - Utils::FileName buildDirectory() const; - Utils::FileName rawBuildDirectory() const; - void setBuildDirectory(const Utils::FileName &dir); + Utils::FilePath buildDirectory() const; + Utils::FilePath rawBuildDirectory() const; + void setBuildDirectory(const Utils::FilePath &dir); - virtual NamedWidget *createConfigWidget() = 0; + virtual NamedWidget *createConfigWidget(); virtual QList<NamedWidget *> createSubConfigWidgets(); // Maybe the BuildConfiguration is not the best place for the environment @@ -95,10 +95,15 @@ public: bool isActive() const override; - void prependCompilerPathToEnvironment(Utils::Environment &env) const; static void prependCompilerPathToEnvironment(Kit *k, Utils::Environment &env); void updateCacheAndEmitEnvironmentChanged(); + ProjectExplorer::BaseStringAspect *buildDirectoryAspect() const; + void setConfigWidgetDisplayName(const QString &display); + void setBuildDirectoryHistoryCompleter(const QString &history); + void setConfigWidgetHasFrame(bool configWidgetHasFrame); + void setBuildDirectorySettingsKey(const QString &key); + signals: void environmentChanged(); void buildDirectoryChanged(); @@ -114,9 +119,11 @@ private: bool m_clearSystemEnvironment = false; QList<Utils::EnvironmentItem> m_userEnvironmentChanges; QList<BuildStepList *> m_stepLists; - Utils::FileName m_buildDirectory; - Utils::FileName m_lastEmmitedBuildDirectory; + ProjectExplorer::BaseStringAspect *m_buildDirectoryAspect = nullptr; + Utils::FilePath m_lastEmmitedBuildDirectory; mutable Utils::Environment m_cachedEnvironment; + QString m_configWidgetDisplayName; + bool m_configWidgetHasFrame = false; }; class PROJECTEXPLORER_EXPORT BuildConfigurationFactory : public QObject @@ -143,10 +150,10 @@ public: static BuildConfigurationFactory *find(const Kit *k, const QString &projectPath); static BuildConfigurationFactory *find(Target *parent); - using IssueReporter = std::function<QList<ProjectExplorer::Task>(Kit *, const QString &, const QString &)>; + using IssueReporter = std::function<Tasks(Kit *, const QString &, const QString &)>; void setIssueReporter(const IssueReporter &issueReporter); - const QList<Task> reportIssues(ProjectExplorer::Kit *kit, - const QString &projectPath, const QString &buildDir) const; + const Tasks reportIssues(ProjectExplorer::Kit *kit, + const QString &projectPath, const QString &buildDir) const; protected: virtual QList<BuildInfo> availableBuilds(const Target *parent) const = 0; diff --git a/src/plugins/projectexplorer/buildenvironmentwidget.cpp b/src/plugins/projectexplorer/buildenvironmentwidget.cpp index 3177f7e6b1..047412fbf3 100644 --- a/src/plugins/projectexplorer/buildenvironmentwidget.cpp +++ b/src/plugins/projectexplorer/buildenvironmentwidget.cpp @@ -43,7 +43,8 @@ BuildEnvironmentWidget::BuildEnvironmentWidget(BuildConfiguration *bc) : m_clearSystemEnvironmentCheckBox = new QCheckBox(this); m_clearSystemEnvironmentCheckBox->setText(tr("Clear system environment")); - m_buildEnvironmentWidget = new EnvironmentWidget(this, m_clearSystemEnvironmentCheckBox); + m_buildEnvironmentWidget = new EnvironmentWidget(this, EnvironmentWidget::TypeLocal, + m_clearSystemEnvironmentCheckBox); vbox->addWidget(m_buildEnvironmentWidget); connect(m_buildEnvironmentWidget, &EnvironmentWidget::userChangesChanged, diff --git a/src/plugins/projectexplorer/buildinfo.h b/src/plugins/projectexplorer/buildinfo.h index e135959352..41dfd78180 100644 --- a/src/plugins/projectexplorer/buildinfo.h +++ b/src/plugins/projectexplorer/buildinfo.h @@ -46,7 +46,7 @@ public: QString displayName; QString typeName; - Utils::FileName buildDirectory; + Utils::FilePath buildDirectory; Core::Id kitId; BuildConfiguration::BuildType buildType = BuildConfiguration::Unknown; diff --git a/src/plugins/projectexplorer/buildmanager.cpp b/src/plugins/projectexplorer/buildmanager.cpp index 544677517a..eece545e07 100644 --- a/src/plugins/projectexplorer/buildmanager.cpp +++ b/src/plugins/projectexplorer/buildmanager.cpp @@ -143,6 +143,8 @@ void BuildManager::extensionsInitialized() tr("Build System", "Category for build system issues listed under 'Issues'")); TaskHub::addCategory(Constants::TASK_CATEGORY_DEPLOYMENT, tr("Deployment", "Category for deployment issues listed under 'Issues'")); + TaskHub::addCategory(Constants::TASK_CATEGORY_AUTOTEST, + tr("Autotests", "Category for autotest issues listed under 'Issues'")); } BuildManager::~BuildManager() @@ -186,6 +188,16 @@ int BuildManager::getErrorTaskCount() return errors; } +void BuildManager::setCompileOutputSettings(const Internal::CompileOutputSettings &settings) +{ + d->m_outputWindow->setSettings(settings); +} + +const Internal::CompileOutputSettings &BuildManager::compileOutputSettings() +{ + return d->m_outputWindow->settings(); +} + void BuildManager::cancel() { if (d->m_running) { @@ -193,8 +205,6 @@ void BuildManager::cancel() return; d->m_canceling = true; d->m_currentBuildStep->cancel(); - while (d->m_canceling) - QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); // TODO: Is this really necessary? } } @@ -366,7 +376,7 @@ void BuildManager::nextBuildQueue() const QString projectName = d->m_currentBuildStep->project()->displayName(); const QString targetName = t->displayName(); addToOutputWindow(tr("Error while building/deploying project %1 (kit: %2)").arg(projectName, targetName), BuildStep::OutputFormat::Stderr); - const QList<Task> kitTasks = t->kit()->validate(); + const Tasks kitTasks = t->kit()->validate(); if (!kitTasks.isEmpty()) { addToOutputWindow(tr("The kit %1 has configuration issues which might be the root cause for this problem.") .arg(targetName), BuildStep::OutputFormat::Stderr); @@ -436,9 +446,12 @@ bool BuildManager::buildQueueAppend(const QList<BuildStep *> &steps, QStringList { if (!d->m_running) { d->m_outputWindow->clearContents(); - TaskHub::clearTasks(Constants::TASK_CATEGORY_COMPILE); - TaskHub::clearTasks(Constants::TASK_CATEGORY_BUILDSYSTEM); - TaskHub::clearTasks(Constants::TASK_CATEGORY_DEPLOYMENT); + if (ProjectExplorerPlugin::projectExplorerSettings().clearIssuesOnRebuild) { + TaskHub::clearTasks(Constants::TASK_CATEGORY_COMPILE); + TaskHub::clearTasks(Constants::TASK_CATEGORY_BUILDSYSTEM); + TaskHub::clearTasks(Constants::TASK_CATEGORY_DEPLOYMENT); + TaskHub::clearTasks(Constants::TASK_CATEGORY_AUTOTEST); + } foreach (const QString &str, preambleMessage) addToOutputWindow(str, BuildStep::OutputFormat::NormalMessage, BuildStep::DontAppendNewline); @@ -513,7 +526,7 @@ bool BuildManager::buildLists(QList<BuildStepList *> bsls, const QStringList &pr return false; } - if (ProjectExplorerPlugin::projectExplorerSettings().showCompilerOutput) + if (d->m_outputWindow->settings().popUp) d->m_outputWindow->popup(IOutputPane::NoModeSwitch); startBuildQueue(); return true; @@ -526,7 +539,7 @@ void BuildManager::appendStep(BuildStep *step, const QString &name) d->m_outputWindow->popup(IOutputPane::NoModeSwitch); return; } - if (ProjectExplorerPlugin::projectExplorerSettings().showCompilerOutput) + if (d->m_outputWindow->settings().popUp) d->m_outputWindow->popup(IOutputPane::NoModeSwitch); startBuildQueue(); } diff --git a/src/plugins/projectexplorer/buildmanager.h b/src/plugins/projectexplorer/buildmanager.h index fc23bc851a..fe452d6965 100644 --- a/src/plugins/projectexplorer/buildmanager.h +++ b/src/plugins/projectexplorer/buildmanager.h @@ -32,6 +32,7 @@ #include <QStringList> namespace ProjectExplorer { +namespace Internal { class CompileOutputSettings; } class Task; class Project; @@ -64,6 +65,9 @@ public: static int getErrorTaskCount(); + static void setCompileOutputSettings(const Internal::CompileOutputSettings &settings); + static const Internal::CompileOutputSettings &compileOutputSettings(); + public slots: static void cancel(); // Shows without focus diff --git a/src/plugins/projectexplorer/buildsettingspropertiespage.cpp b/src/plugins/projectexplorer/buildsettingspropertiespage.cpp index 772740abd2..481c37eef0 100644 --- a/src/plugins/projectexplorer/buildsettingspropertiespage.cpp +++ b/src/plugins/projectexplorer/buildsettingspropertiespage.cpp @@ -119,7 +119,7 @@ BuildSettingsWidget::BuildSettingsWidget(Target *target) : updateAddButtonMenu(); updateBuildSettings(); - connect(m_buildConfigurationComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(m_buildConfigurationComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &BuildSettingsWidget::currentIndexChanged); connect(m_removeButton, &QAbstractButton::clicked, diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp index d293b09290..35e02b8972 100644 --- a/src/plugins/projectexplorer/buildstep.cpp +++ b/src/plugins/projectexplorer/buildstep.cpp @@ -225,6 +225,12 @@ void BuildStep::setWidgetExpandedByDefault(bool widgetExpandedByDefault) m_widgetExpandedByDefault = widgetExpandedByDefault; } +QVariant BuildStep::data(Core::Id id) const +{ + Q_UNUSED(id); + return {}; +} + /*! \fn BuildStep::isImmutable() @@ -299,7 +305,7 @@ bool BuildStepFactory::canHandle(BuildStepList *bsl) const if (!m_supportedDeviceTypes.isEmpty()) { Target *target = bsl->target(); QTC_ASSERT(target, return false); - Core::Id deviceType = DeviceTypeKitInformation::deviceTypeId(target->kit()); + Core::Id deviceType = DeviceTypeKitAspect::deviceTypeId(target->kit()); if (!m_supportedDeviceTypes.contains(deviceType)) return false; } diff --git a/src/plugins/projectexplorer/buildstep.h b/src/plugins/projectexplorer/buildstep.h index 0796b87582..0abf7d5b78 100644 --- a/src/plugins/projectexplorer/buildstep.h +++ b/src/plugins/projectexplorer/buildstep.h @@ -28,6 +28,7 @@ #include "projectconfiguration.h" #include "projectexplorer_export.h" +#include <utils/optional.h> #include <utils/qtcassert.h> #include <QFutureInterface> @@ -88,9 +89,14 @@ public: bool widgetExpandedByDefault() const; void setWidgetExpandedByDefault(bool widgetExpandedByDefault); + bool hasUserExpansionState() const { return m_wasExpanded.has_value(); } + bool wasUserExpanded() const { return m_wasExpanded.value_or(false); } + void setUserExpanded(bool expanded) { m_wasExpanded = expanded; } + bool isImmutable() const { return m_immutable; } void setImmutable(bool immutable) { m_immutable = immutable; } + virtual QVariant data(Core::Id id) const; signals: /// Adds a \p task to the Issues pane. /// Do note that for linking compile output with tasks, you should first emit the task @@ -122,6 +128,7 @@ private: bool m_immutable = false; bool m_widgetExpandedByDefault = true; bool m_runInGuiThread = true; + Utils::optional<bool> m_wasExpanded; }; class PROJECTEXPLORER_EXPORT BuildStepInfo diff --git a/src/plugins/projectexplorer/buildstepspage.cpp b/src/plugins/projectexplorer/buildstepspage.cpp index a9897e610c..331ccd9f40 100644 --- a/src/plugins/projectexplorer/buildstepspage.cpp +++ b/src/plugins/projectexplorer/buildstepspage.cpp @@ -258,8 +258,11 @@ void BuildStepListWidget::init(BuildStepList *bsl) for (int i = 0; i < bsl->count(); ++i) { addBuildStep(i); // addBuilStep expands the config widget by default, which we don't want here - if (m_buildStepsData.at(i)->step->widgetExpandedByDefault()) - m_buildStepsData.at(i)->detailsWidget->setState(DetailsWidget::Collapsed); + if (m_buildStepsData.at(i)->step->widgetExpandedByDefault()) { + m_buildStepsData.at(i)->detailsWidget->setState( + m_buildStepsData.at(i)->step->wasUserExpanded() + ? DetailsWidget::Expanded : DetailsWidget::Collapsed); + } } m_noStepsLabel->setVisible(bsl->isEmpty()); @@ -323,10 +326,10 @@ void BuildStepListWidget::addBuildStep(int pos) this, &BuildStepListWidget::updateEnabledState); // Expand new build steps by default - if (newStep->widgetExpandedByDefault()) - s->detailsWidget->setState(DetailsWidget::Expanded); - else - s->detailsWidget->setState(DetailsWidget::OnlySummary); + const bool expand = newStep->hasUserExpansionState() + ? newStep->wasUserExpanded() : newStep->widgetExpandedByDefault(); + s->detailsWidget->setState(expand ? DetailsWidget::Expanded : DetailsWidget::OnlySummary); + connect(s->detailsWidget, &DetailsWidget::expanded, newStep, &BuildStep::setUserExpanded); m_noStepsLabel->setVisible(false); updateBuildStepButtonsState(); diff --git a/src/plugins/projectexplorer/buildtargetinfo.h b/src/plugins/projectexplorer/buildtargetinfo.h index 38cadea991..28af0330a4 100644 --- a/src/plugins/projectexplorer/buildtargetinfo.h +++ b/src/plugins/projectexplorer/buildtargetinfo.h @@ -32,7 +32,6 @@ #include <utils/fileutils.h> #include <QList> -#include <QSet> namespace ProjectExplorer { @@ -43,9 +42,9 @@ public: QString displayName; QString displayNameUniquifier; - Utils::FileName targetFilePath; - Utils::FileName projectFilePath; - Utils::FileName workingDirectory; + Utils::FilePath targetFilePath; + Utils::FilePath projectFilePath; + Utils::FilePath workingDirectory; bool isQtcRunnable = true; bool usesTerminal = false; @@ -76,26 +75,4 @@ inline uint qHash(const BuildTargetInfo &ti) return qHash(ti.displayName) ^ qHash(ti.buildKey); } -class PROJECTEXPLORER_EXPORT BuildTargetInfoList -{ -public: - BuildTargetInfo buildTargetInfo(const QString &buildKey) { - return Utils::findOrDefault(list, [&buildKey](const BuildTargetInfo &ti) { - return ti.buildKey == buildKey; - }); - } - - QList<BuildTargetInfo> list; -}; - -inline bool operator==(const BuildTargetInfoList &til1, const BuildTargetInfoList &til2) -{ - return til1.list.toSet() == til2.list.toSet(); -} - -inline bool operator!=(const BuildTargetInfoList &til1, const BuildTargetInfoList &til2) -{ - return !(til1 == til2); -} - } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/clangparser.cpp b/src/plugins/projectexplorer/clangparser.cpp index 98d733e69f..1859f25896 100644 --- a/src/plugins/projectexplorer/clangparser.cpp +++ b/src/plugins/projectexplorer/clangparser.cpp @@ -68,7 +68,7 @@ void ClangParser::stdError(const QString &line) m_expectSnippet = true; Task task(taskType(match.captured(3)), match.captured(4), - Utils::FileName(), /* filename */ + Utils::FilePath(), /* filename */ -1, /* line */ Constants::TASK_CATEGORY_COMPILE); newTask(task); @@ -80,7 +80,7 @@ void ClangParser::stdError(const QString &line) m_expectSnippet = true; newTask(Task(Task::Unknown, lne.trimmed(), - Utils::FileName::fromUserInput(match.captured(2)), /* filename */ + Utils::FilePath::fromUserInput(match.captured(2)), /* filename */ match.captured(3).toInt(), /* line */ Constants::TASK_CATEGORY_COMPILE)); return; @@ -95,7 +95,7 @@ void ClangParser::stdError(const QString &line) lineNo = match.captured(5).toInt(&ok); Task task(taskType(match.captured(7)), match.captured(8), - Utils::FileName::fromUserInput(match.captured(1)), /* filename */ + Utils::FilePath::fromUserInput(match.captured(1)), /* filename */ lineNo, Core::Id(Constants::TASK_CATEGORY_COMPILE)); newTask(task); @@ -107,7 +107,7 @@ void ClangParser::stdError(const QString &line) m_expectSnippet = true; Task task(Task::Error, match.captured(1), - Utils::FileName(), + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_COMPILE)); newTask(task); @@ -141,7 +141,7 @@ void ProjectExplorerPlugin::testClangOutputParser_data() QTest::addColumn<OutputParserTester::Channel>("inputChannel"); QTest::addColumn<QString>("childStdOutLines"); QTest::addColumn<QString>("childStdErrLines"); - QTest::addColumn<QList<Task> >("tasks"); + QTest::addColumn<Tasks >("tasks"); QTest::addColumn<QString>("outputLines"); const Core::Id categoryCompile = Constants::TASK_CATEGORY_COMPILE; @@ -149,32 +149,32 @@ void ProjectExplorerPlugin::testClangOutputParser_data() QTest::newRow("pass-through stdout") << QString::fromLatin1("Sometext") << OutputParserTester::STDOUT << QString::fromLatin1("Sometext\n") << QString() - << QList<Task>() + << Tasks() << QString(); QTest::newRow("pass-through stderr") << QString::fromLatin1("Sometext") << OutputParserTester::STDERR << QString() << QString::fromLatin1("Sometext\n") - << QList<Task>() + << Tasks() << QString(); QTest::newRow("clang++ warning") << QString::fromLatin1("clang++: warning: argument unused during compilation: '-mthreads'") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Warning, QLatin1String("argument unused during compilation: '-mthreads'"), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile)) << QString(); QTest::newRow("clang++ error") << QString::fromLatin1("clang++: error: no input files [err_drv_no_input_files]") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Error, QLatin1String("no input files [err_drv_no_input_files]"), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile)) << QString(); QTest::newRow("complex warning") @@ -184,16 +184,16 @@ void ProjectExplorerPlugin::testClangOutputParser_data() " ^") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Unknown, QLatin1String("In file included from ..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qnamespace.h:45:"), - Utils::FileName::fromUserInput(QLatin1String("..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qnamespace.h")), 45, + Utils::FilePath::fromUserInput(QLatin1String("..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qnamespace.h")), 45, categoryCompile) << Task(Task::Warning, QLatin1String("unknown attribute 'dllimport' ignored [-Wunknown-attributes]\n" "class Q_CORE_EXPORT QSysInfo {\n" " ^"), - Utils::FileName::fromUserInput(QLatin1String("..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qglobal.h")), 1425, + Utils::FilePath::fromUserInput(QLatin1String("..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qglobal.h")), 1425, categoryCompile)) << QString(); QTest::newRow("note") @@ -202,12 +202,12 @@ void ProjectExplorerPlugin::testClangOutputParser_data() " ^") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Unknown, QLatin1String("instantiated from:\n" "# define Q_CORE_EXPORT Q_DECL_IMPORT\n" " ^"), - Utils::FileName::fromUserInput(QLatin1String("..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qglobal.h")), 1289, + Utils::FilePath::fromUserInput(QLatin1String("..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qglobal.h")), 1289, categoryCompile)) << QString(); QTest::newRow("fatal error") @@ -216,12 +216,12 @@ void ProjectExplorerPlugin::testClangOutputParser_data() " ^") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Error, QLatin1String("'bits/c++config.h' file not found\n" "#include <bits/c++config.h>\n" " ^"), - Utils::FileName::fromUserInput(QLatin1String("/usr/include/c++/4.6/utility")), 68, + Utils::FilePath::fromUserInput(QLatin1String("/usr/include/c++/4.6/utility")), 68, categoryCompile)) << QString(); @@ -231,12 +231,12 @@ void ProjectExplorerPlugin::testClangOutputParser_data() " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Warning, QLatin1String("?: has lower precedence than +; + will be evaluated first [-Wparentheses]\n" " int x = option->rect.x() + horizontal ? 2 : 6;\n" " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^"), - Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/coreplugin/manhattanstyle.cpp")), 567, + Utils::FilePath::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/coreplugin/manhattanstyle.cpp")), 567, categoryCompile)) << QString(); QTest::newRow("code sign error") @@ -245,24 +245,24 @@ void ProjectExplorerPlugin::testClangOutputParser_data() "CodeSign error: code signing is required for product type 'Application' in SDK 'iOS 7.0'") << OutputParserTester::STDERR << QString() << QString::fromLatin1("Check dependencies\n") - << (QList<Task>() + << (Tasks() << Task(Task::Error, QLatin1String("No matching provisioning profiles found: No provisioning profiles with a valid signing identity (i.e. certificate and private key pair) were found."), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile) << Task(Task::Error, QLatin1String("code signing is required for product type 'Application' in SDK 'iOS 7.0'"), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile)) << QString(); QTest::newRow("moc note") << QString::fromLatin1("/home/qtwebkithelpviewer.h:0: Note: No relevant classes found. No output generated.") << OutputParserTester::STDERR << QString() << QString() - << (QList<ProjectExplorer::Task>() + << (Tasks() << Task(Task::Unknown, QLatin1String("Note: No relevant classes found. No output generated."), - Utils::FileName::fromUserInput(QLatin1String("/home/qtwebkithelpviewer.h")), 0, + Utils::FilePath::fromUserInput(QLatin1String("/home/qtwebkithelpviewer.h")), 0, categoryCompile) ) << QString(); @@ -274,7 +274,7 @@ void ProjectExplorerPlugin::testClangOutputParser() testbench.appendOutputParser(new ClangParser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); - QFETCH(QList<Task>, tasks); + QFETCH(Tasks, tasks); QFETCH(QString, childStdOutLines); QFETCH(QString, childStdErrLines); QFETCH(QString, outputLines); diff --git a/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp b/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp index 9410c49931..5c25cb83d8 100644 --- a/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp +++ b/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp @@ -56,7 +56,7 @@ CodeStyleSettingsWidget::CodeStyleSettingsWidget(Project *project) : QWidget(), m_ui.languageComboBox->addItem(factory->displayName()); } - connect(m_ui.languageComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(m_ui.languageComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), m_ui.stackedWidget, &QStackedWidget::setCurrentIndex); } diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp index a76ea49ea7..d92bcc4bbe 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.cpp +++ b/src/plugins/projectexplorer/compileoutputwindow.cpp @@ -45,12 +45,17 @@ #include <utils/theme/theme.h> #include <utils/utilsicons.h> +#include <QCheckBox> +#include <QHBoxLayout> #include <QIcon> -#include <QTextCharFormat> +#include <QLabel> +#include <QPlainTextEdit> +#include <QSpinBox> #include <QTextBlock> +#include <QTextCharFormat> #include <QTextCursor> -#include <QPlainTextEdit> #include <QToolButton> +#include <QVBoxLayout> using namespace ProjectExplorer; using namespace ProjectExplorer::Internal; @@ -58,6 +63,10 @@ using namespace ProjectExplorer::Internal; namespace { const char SETTINGS_KEY[] = "ProjectExplorer/CompileOutput/Zoom"; const char C_COMPILE_OUTPUT[] = "ProjectExplorer.CompileOutput"; +const char POP_UP_KEY[] = "ProjectExplorer/Settings/ShowCompilerOutput"; +const char WRAP_OUTPUT_KEY[] = "ProjectExplorer/Settings/WrapBuildOutput"; +const char MAX_LINES_KEY[] = "ProjectExplorer/Settings/MaxBuildOutputLines"; +const char OPTIONS_PAGE_ID[] = "C.ProjectExplorer.CompileOutputOptions"; } namespace ProjectExplorer { @@ -67,31 +76,11 @@ class CompileOutputTextEdit : public Core::OutputWindow { Q_OBJECT public: - CompileOutputTextEdit(const Core::Context &context) : Core::OutputWindow(context) + CompileOutputTextEdit(const Core::Context &context) : Core::OutputWindow(context, SETTINGS_KEY) { - setWheelZoomEnabled(true); - - QSettings *settings = Core::ICore::settings(); - float zoom = settings->value(QLatin1String(SETTINGS_KEY), 0).toFloat(); - setFontZoom(zoom); - - fontSettingsChanged(); - - connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::fontSettingsChanged, - this, &CompileOutputTextEdit::fontSettingsChanged); - - connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested, - this, &CompileOutputTextEdit::saveSettings); - setMouseTracking(true); } - void saveSettings() - { - QSettings *settings = Core::ICore::settings(); - settings->setValue(QLatin1String(SETTINGS_KEY), fontZoom()); - } - void addTask(const Task &task, int blocknumber) { m_taskids.insert(blocknumber, task.taskId); @@ -101,11 +90,6 @@ public: { m_taskids.clear(); } -private: - void fontSettingsChanged() - { - setBaseFont(TextEditor::TextEditorSettings::fontSettings().font()); - } protected: void mouseMoveEvent(QMouseEvent *ev) override @@ -149,8 +133,7 @@ private: CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : m_cancelBuildButton(new QToolButton), - m_zoomInButton(new QToolButton), - m_zoomOutButton(new QToolButton), + m_settingsButton(new QToolButton), m_formatter(new Utils::OutputFormatter) { Core::Context context(C_COMPILE_OUTPUT); @@ -175,21 +158,34 @@ CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : Utils::ProxyAction::proxyActionWithIcon(cancelBuildAction, Utils::Icons::STOP_SMALL_TOOLBAR.icon()); m_cancelBuildButton->setDefaultAction(cancelBuildProxyButton); - m_zoomInButton->setToolTip(tr("Increase Font Size")); - m_zoomInButton->setIcon(Utils::Icons::PLUS_TOOLBAR.icon()); - m_zoomOutButton->setToolTip(tr("Decrease Font Size")); - m_zoomOutButton->setIcon(Utils::Icons::MINUS.icon()); + m_settingsButton->setToolTip(tr("Open Settings Page")); + m_settingsButton->setIcon(Utils::Icons::SETTINGS_TOOLBAR.icon()); + + auto updateFontSettings = [this] { + m_outputWindow->setBaseFont(TextEditor::TextEditorSettings::fontSettings().font()); + }; + auto updateZoomEnabled = [this] { + m_outputWindow->setWheelZoomEnabled( + TextEditor::TextEditorSettings::behaviorSettings().m_scrollWheelZooming); + }; + + updateFontSettings(); updateZoomEnabled(); + setupFilterUi("CompileOutputPane.Filter"); + setFilteringEnabled(true); - connect(TextEditor::TextEditorSettings::instance(), - &TextEditor::TextEditorSettings::behaviorSettingsChanged, - this, &CompileOutputWindow::updateZoomEnabled); + connect(this, &IOutputPane::zoomIn, m_outputWindow, &Core::OutputWindow::zoomIn); + connect(this, &IOutputPane::zoomOut, m_outputWindow, &Core::OutputWindow::zoomOut); + connect(this, &IOutputPane::resetZoom, m_outputWindow, &Core::OutputWindow::resetZoom); + connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::fontSettingsChanged, + this, updateFontSettings); + connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::behaviorSettingsChanged, + this, updateZoomEnabled); - connect(m_zoomInButton, &QToolButton::clicked, - this, [this]() { m_outputWindow->zoomIn(1); }); - connect(m_zoomOutButton, &QToolButton::clicked, - this, [this]() { m_outputWindow->zoomOut(1); }); + connect(m_settingsButton, &QToolButton::clicked, this, [] { + Core::ICore::showOptionsDialog(OPTIONS_PAGE_ID); + }); auto agg = new Aggregation::Aggregate; agg->add(m_outputWindow); @@ -199,8 +195,8 @@ CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : m_handler = new ShowOutputTaskHandler(this); ExtensionSystem::PluginManager::addObject(m_handler); - connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged, - this, &CompileOutputWindow::updateFromSettings); + setupContext(C_COMPILE_OUTPUT, m_outputWindow); + loadSettings(); updateFromSettings(); } @@ -209,25 +205,14 @@ CompileOutputWindow::~CompileOutputWindow() ExtensionSystem::PluginManager::removeObject(m_handler); delete m_handler; delete m_cancelBuildButton; - delete m_zoomInButton; - delete m_zoomOutButton; + delete m_settingsButton; delete m_formatter; } -void CompileOutputWindow::updateZoomEnabled() -{ - const TextEditor::BehaviorSettings &settings - = TextEditor::TextEditorSettings::behaviorSettings(); - bool zoomEnabled = settings.m_scrollWheelZooming; - m_zoomInButton->setEnabled(zoomEnabled); - m_zoomOutButton->setEnabled(zoomEnabled); - m_outputWindow->setWheelZoomEnabled(zoomEnabled); -} - void CompileOutputWindow::updateFromSettings() { - m_outputWindow->setWordWrapEnabled(ProjectExplorerPlugin::projectExplorerSettings().wrapAppOutput); - m_outputWindow->setMaxCharCount(ProjectExplorerPlugin::projectExplorerSettings().maxBuildOutputChars); + m_outputWindow->setWordWrapEnabled(m_settings.wrapOutput); + m_outputWindow->setMaxCharCount(m_settings.maxCharCount); } bool CompileOutputWindow::hasFocus() const @@ -252,7 +237,7 @@ QWidget *CompileOutputWindow::outputWidget(QWidget *) QList<QWidget *> CompileOutputWindow::toolBarWidgets() const { - return {m_cancelBuildButton, m_zoomInButton, m_zoomOutButton}; + return QList<QWidget *>{m_cancelBuildButton, m_settingsButton} + IOutputPane::toolBarWidgets(); } void CompileOutputWindow::appendText(const QString &text, BuildStep::OutputFormat format) @@ -359,4 +344,101 @@ void CompileOutputWindow::flush() m_formatter->flush(); } +void CompileOutputWindow::setSettings(const CompileOutputSettings &settings) +{ + m_settings = settings; + storeSettings(); + updateFromSettings(); +} + +void CompileOutputWindow::updateFilter() +{ + m_outputWindow->updateFilterProperties(filterText(), filterCaseSensitivity(), + filterUsesRegexp()); +} + +void CompileOutputWindow::loadSettings() +{ + QSettings * const s = Core::ICore::settings(); + m_settings.popUp = s->value(POP_UP_KEY, false).toBool(); + m_settings.wrapOutput = s->value(WRAP_OUTPUT_KEY, true).toBool(); + m_settings.maxCharCount = s->value(MAX_LINES_KEY, + Core::Constants::DEFAULT_MAX_CHAR_COUNT).toInt() * 100; +} + +void CompileOutputWindow::storeSettings() const +{ + QSettings * const s = Core::ICore::settings(); + s->setValue(POP_UP_KEY, m_settings.popUp); + s->setValue(WRAP_OUTPUT_KEY, m_settings.wrapOutput); + s->setValue(MAX_LINES_KEY, m_settings.maxCharCount / 100); +} + +class CompileOutputSettingsPage::SettingsWidget : public QWidget +{ + Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::Internal::CompileOutputSettingsPage) +public: + SettingsWidget() + { + const CompileOutputSettings &settings = BuildManager::compileOutputSettings(); + m_wrapOutputCheckBox.setText(tr("Word-wrap output")); + m_wrapOutputCheckBox.setChecked(settings.wrapOutput); + m_popUpCheckBox.setText(tr("Open pane when building")); + m_popUpCheckBox.setChecked(settings.popUp); + m_maxCharsBox.setMaximum(100000000); + m_maxCharsBox.setValue(settings.maxCharCount); + const auto layout = new QVBoxLayout(this); + layout->addWidget(&m_wrapOutputCheckBox); + layout->addWidget(&m_popUpCheckBox); + const auto maxCharsLayout = new QHBoxLayout; + const QString msg = tr("Limit output to %1 characters"); + const QStringList parts = msg.split("%1") << QString() << QString(); + maxCharsLayout->addWidget(new QLabel(parts.at(0).trimmed())); + maxCharsLayout->addWidget(&m_maxCharsBox); + maxCharsLayout->addWidget(new QLabel(parts.at(1).trimmed())); + maxCharsLayout->addStretch(1); + layout->addLayout(maxCharsLayout); + layout->addStretch(1); + } + + CompileOutputSettings settings() const + { + CompileOutputSettings s; + s.wrapOutput = m_wrapOutputCheckBox.isChecked(); + s.popUp = m_popUpCheckBox.isChecked(); + s.maxCharCount = m_maxCharsBox.value(); + return s; + } + +private: + QCheckBox m_wrapOutputCheckBox; + QCheckBox m_popUpCheckBox; + QSpinBox m_maxCharsBox; +}; + +CompileOutputSettingsPage::CompileOutputSettingsPage() +{ + setId(OPTIONS_PAGE_ID); + setDisplayName(tr("Compile Output")); + setCategory(Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); +} + +QWidget *CompileOutputSettingsPage::widget() +{ + if (!m_widget) + m_widget = new SettingsWidget; + return m_widget; +} + +void CompileOutputSettingsPage::apply() +{ + if (m_widget) + BuildManager::setCompileOutputSettings(m_widget->settings()); +} + +void CompileOutputSettingsPage::finish() +{ + delete m_widget; +} + #include "compileoutputwindow.moc" diff --git a/src/plugins/projectexplorer/compileoutputwindow.h b/src/plugins/projectexplorer/compileoutputwindow.h index 9ff9c8ac92..a8a5303fc6 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.h +++ b/src/plugins/projectexplorer/compileoutputwindow.h @@ -26,10 +26,13 @@ #pragma once #include "buildstep.h" +#include "projectexplorersettings.h" +#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/ioutputpane.h> #include <QHash> #include <QPair> +#include <QPointer> QT_BEGIN_NAMESPACE class QPlainTextEdit; @@ -81,17 +84,39 @@ public: void flush(); + const CompileOutputSettings &settings() const { return m_settings; } + void setSettings(const CompileOutputSettings &settings); + private: + void updateFilter() override; + + void loadSettings(); + void storeSettings() const; void updateFromSettings(); - void updateZoomEnabled(); CompileOutputTextEdit *m_outputWindow; QHash<unsigned int, QPair<int, int>> m_taskPositions; ShowOutputTaskHandler *m_handler; QToolButton *m_cancelBuildButton; - QToolButton *m_zoomInButton; - QToolButton *m_zoomOutButton; + QToolButton * const m_settingsButton; Utils::OutputFormatter *m_formatter; + CompileOutputSettings m_settings; +}; + +class CompileOutputSettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + CompileOutputSettingsPage(); + +private: + QWidget *widget() override; + void apply() override; + void finish() override; + + class SettingsWidget; + QPointer<SettingsWidget> m_widget; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/currentprojectfilter.cpp b/src/plugins/projectexplorer/currentprojectfilter.cpp index 13c0d308db..0e924eeb2a 100644 --- a/src/plugins/projectexplorer/currentprojectfilter.cpp +++ b/src/plugins/projectexplorer/currentprojectfilter.cpp @@ -58,7 +58,7 @@ void CurrentProjectFilter::prepareSearch(const QString &entry) if (!fileIterator()) { QStringList paths; if (m_project) - paths = Utils::transform(m_project->files(Project::AllFiles), &Utils::FileName::toString); + paths = Utils::transform(m_project->files(Project::AllFiles), &Utils::FilePath::toString); setFileIterator(new BaseFileFilter::ListIterator(paths)); } BaseFileFilter::prepareSearch(entry); diff --git a/src/plugins/projectexplorer/currentprojectfind.cpp b/src/plugins/projectexplorer/currentprojectfind.cpp index 75aaf3e601..ca8093f78b 100644 --- a/src/plugins/projectexplorer/currentprojectfind.cpp +++ b/src/plugins/projectexplorer/currentprojectfind.cpp @@ -73,7 +73,7 @@ QVariant CurrentProjectFind::additionalParameters() const { Project *project = ProjectTree::currentProject(); if (project && project->document()) - return qVariantFromValue(project->projectFilePath().toString()); + return QVariant::fromValue(project->projectFilePath().toString()); return QVariant(); } diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp index aa23dc69c9..957b742474 100644 --- a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp +++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp @@ -31,7 +31,7 @@ #include "environmentaspect.h" #include "localenvironmentaspect.h" #include "project.h" -#include "runconfigurationaspects.h" +#include "runcontrol.h" #include "target.h" #include <coreplugin/icore.h> @@ -94,8 +94,7 @@ private: CustomExecutableDialog::CustomExecutableDialog(RunConfiguration *rc) : QDialog(Core::ICore::dialogParent()), - m_rc(rc), - m_workingDirectory(rc->aspect<EnvironmentAspect>()) + m_rc(rc) { auto vbox = new QVBoxLayout(this); vbox->addWidget(new QLabel(tr("Could not find the executable, please specify one."))); @@ -146,7 +145,7 @@ CustomExecutableDialog::CustomExecutableDialog(RunConfiguration *rc) void CustomExecutableDialog::accept() { - auto executable = FileName::fromString(m_executableChooser->path()); + auto executable = FilePath::fromString(m_executableChooser->path()); m_rc->aspect<ExecutableAspect>()->setExecutable(executable); copyAspect(&m_arguments, m_rc->aspect<ArgumentsAspect>()); copyAspect(&m_workingDirectory, m_rc->aspect<WorkingDirectoryAspect>()); @@ -184,8 +183,7 @@ CustomExecutableRunConfiguration::CustomExecutableRunConfiguration(Target *targe CustomExecutableRunConfiguration::CustomExecutableRunConfiguration(Target *target, Core::Id id) : RunConfiguration(target, id) { - auto envAspect = addAspect<LocalEnvironmentAspect> - (target, LocalEnvironmentAspect::BaseEnvironmentModifier()); + auto envAspect = addAspect<LocalEnvironmentAspect>(target); auto exeAspect = addAspect<ExecutableAspect>(); exeAspect->setSettingsKey("ProjectExplorer.CustomExecutableRunConfiguration.Executable"); @@ -195,7 +193,7 @@ CustomExecutableRunConfiguration::CustomExecutableRunConfiguration(Target *targe exeAspect->setEnvironment(envAspect->environment()); addAspect<ArgumentsAspect>(); - addAspect<WorkingDirectoryAspect>(envAspect); + addAspect<WorkingDirectoryAspect>(); addAspect<TerminalAspect>(); connect(envAspect, &EnvironmentAspect::environmentChanged, @@ -253,11 +251,11 @@ bool CustomExecutableRunConfiguration::isConfigured() const Runnable CustomExecutableRunConfiguration::runnable() const { - FileName workingDirectory = + FilePath workingDirectory = aspect<WorkingDirectoryAspect>()->workingDirectory(macroExpander()); Runnable r; - r.executable = aspect<ExecutableAspect>()->executable().toString(); + r.executable = executable().toString(); r.commandLineArguments = aspect<ArgumentsAspect>()->arguments(macroExpander()); r.environment = aspect<EnvironmentAspect>()->environment(); r.workingDirectory = workingDirectory.toString(); @@ -285,8 +283,6 @@ CustomExecutableRunConfigurationFactory::CustomExecutableRunConfigurationFactory FixedRunConfigurationFactory(CustomExecutableRunConfiguration::tr("Custom Executable")) { registerRunConfiguration<CustomExecutableRunConfiguration>(CUSTOM_EXECUTABLE_ID); - - addRunWorkerFactory<SimpleTargetRunner>(ProjectExplorer::Constants::NORMAL_RUN_MODE); } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/customparser.cpp b/src/plugins/projectexplorer/customparser.cpp index 8c0b287ce4..c408dfc2c6 100644 --- a/src/plugins/projectexplorer/customparser.cpp +++ b/src/plugins/projectexplorer/customparser.cpp @@ -150,12 +150,12 @@ Core::Id CustomParser::id() return Core::Id("ProjectExplorer.OutputParser.Custom"); } -FileName CustomParser::absoluteFilePath(const QString &filePath) const +FilePath CustomParser::absoluteFilePath(const QString &filePath) const { if (m_workingDirectory.isEmpty()) - return FileName::fromUserInput(filePath); + return FilePath::fromUserInput(filePath); - return FileName::fromString(FileUtils::resolvePath(m_workingDirectory, filePath)); + return FilePath::fromString(FileUtils::resolvePath(m_workingDirectory, filePath)); } bool CustomParser::hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel, @@ -171,7 +171,7 @@ bool CustomParser::hasMatch(const QString &line, CustomParserExpression::CustomP if (!match.hasMatch()) return false; - const FileName fileName = absoluteFilePath(match.captured(expression.fileNameCap())); + const FilePath fileName = absoluteFilePath(match.captured(expression.fileNameCap())); const int lineNumber = match.captured(expression.lineNumberCap()).toInt(); const QString message = match.captured(expression.messageCap()); @@ -215,12 +215,12 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() QTest::addColumn<int>("warningMessageCap"); QTest::addColumn<QString>("childStdOutLines"); QTest::addColumn<QString>("childStdErrLines"); - QTest::addColumn<QList<Task> >("tasks"); + QTest::addColumn<Tasks >("tasks"); QTest::addColumn<QString>("outputLines"); const Core::Id categoryCompile = Constants::TASK_CATEGORY_COMPILE; const QString simplePattern = "^([a-z]+\\.[a-z]+):(\\d+): error: ([^\\s].+)$"; - const FileName fileName = FileName::fromUserInput("main.c"); + const FilePath fileName = FilePath::fromUserInput("main.c"); QTest::newRow("empty patterns") << QString::fromLatin1("Sometext") @@ -230,7 +230,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << QString() << 1 << 2 << 3 << QString() << 1 << 2 << 3 << QString::fromLatin1("Sometext\n") << QString() - << QList<Task>() + << Tasks() << QString(); QTest::newRow("pass-through stdout") @@ -241,7 +241,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << simplePattern << 1 << 2 << 3 << QString() << 1 << 2 << 3 << QString::fromLatin1("Sometext\n") << QString() - << QList<Task>() + << Tasks() << QString(); QTest::newRow("pass-through stderr") @@ -252,7 +252,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << simplePattern << 1 << 2 << 3 << QString() << 1 << 2 << 3 << QString() << QString::fromLatin1("Sometext\n") - << QList<Task>() + << Tasks() << QString(); const QString simpleError = "main.c:9: error: `sfasdf' undeclared (first use this function)"; @@ -267,12 +267,12 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << simplePattern << 1 << 2 << 3 << QString() << 0 << 0 << 0 << QString() << QString() - << QList<Task>{Task(Task::Error, message, fileName, 9, categoryCompile)} + << Tasks{Task(Task::Error, message, fileName, 9, categoryCompile)} << QString(); const QString pathPattern = "^([a-z\\./]+):(\\d+): error: ([^\\s].+)$"; QString workingDir = "/home/src/project"; - FileName expandedFileName = FileName::fromString("/home/src/project/main.c"); + FilePath expandedFileName = FilePath::fromString("/home/src/project/main.c"); QTest::newRow("simple error with expanded path") << "main.c:9: error: `sfasdf' undeclared (first use this function)" @@ -282,10 +282,10 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << pathPattern << 1 << 2 << 3 << QString() << 0 << 0 << 0 << QString() << QString() - << QList<Task>{Task(Task::Error, message, expandedFileName, 9, categoryCompile)} + << Tasks{Task(Task::Error, message, expandedFileName, 9, categoryCompile)} << QString(); - expandedFileName = FileName::fromString("/home/src/project/subdir/main.c"); + expandedFileName = FilePath::fromString("/home/src/project/subdir/main.c"); QTest::newRow("simple error with subdir path") << "subdir/main.c:9: error: `sfasdf' undeclared (first use this function)" << workingDir @@ -294,7 +294,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << pathPattern << 1 << 2 << 3 << QString() << 0 << 0 << 0 << QString() << QString() - << QList<Task>{Task(Task::Error, message, expandedFileName, 9, categoryCompile)} + << Tasks{Task(Task::Error, message, expandedFileName, 9, categoryCompile)} << QString(); workingDir = "/home/src/build-project"; @@ -306,7 +306,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << pathPattern << 1 << 2 << 3 << QString() << 0 << 0 << 0 << QString() << QString() - << QList<Task>{Task(Task::Error, message, expandedFileName, 9, categoryCompile)} + << Tasks{Task(Task::Error, message, expandedFileName, 9, categoryCompile)} << QString(); QTest::newRow("simple error on wrong channel") @@ -317,7 +317,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << simplePattern << 1 << 2 << 3 << QString() << 0 << 0 << 0 << simpleErrorPassThrough << QString() - << QList<Task>() + << Tasks() << QString(); QTest::newRow("simple error on other wrong channel") @@ -328,7 +328,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << simplePattern << 1 << 2 << 3 << QString() << 0 << 0 << 0 << QString() << simpleErrorPassThrough - << QList<Task>() + << Tasks() << QString(); const QString simpleError2 = "Error: Line 19 in main.c: `sfasdf' undeclared (first use this function)"; @@ -343,7 +343,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << simplePattern2 << 2 << 1 << 3 << QString() << 1 << 2 << 3 << QString() << QString() - << QList<Task>{Task(Task::Error, message, fileName, lineNumber2, categoryCompile)} + << Tasks{Task(Task::Error, message, fileName, lineNumber2, categoryCompile)} << QString(); QTest::newRow("another simple error on stdout") @@ -354,7 +354,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << simplePattern2 << 2 << 1 << 3 << QString() << 1 << 2 << 3 << QString() << QString() - << QList<Task>{Task(Task::Error, message, fileName, lineNumber2, categoryCompile)} + << Tasks{Task(Task::Error, message, fileName, lineNumber2, categoryCompile)} << QString(); const QString simpleWarningPattern = "^([a-z]+\\.[a-z]+):(\\d+): warning: ([^\\s].+)$"; @@ -369,7 +369,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << QString() << 1 << 2 << 3 << simpleWarningPattern << 1 << 2 << 3 << QString() << QString() - << QList<Task>{Task(Task::Warning, warningMessage, fileName, 1234, categoryCompile)} + << Tasks{Task(Task::Warning, warningMessage, fileName, 1234, categoryCompile)} << QString(); const QString simpleWarning2 = "Warning: `helloWorld' declared but not used (main.c:19)"; @@ -384,7 +384,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << simplePattern2 << 1 << 2 << 3 << simpleWarningPattern2 << 2 << 3 << 1 << QString() << QString() - << QList<Task>{Task(Task::Warning, warningMessage, fileName, lineNumber2, categoryCompile)} + << Tasks{Task(Task::Warning, warningMessage, fileName, lineNumber2, categoryCompile)} << QString(); QTest::newRow("warning on wrong channel") @@ -395,7 +395,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << QString() << 1 << 2 << 3 << simpleWarningPattern2 << 2 << 3 << 1 << simpleWarningPassThrough2 << QString() - << QList<Task>() + << Tasks() << QString(); QTest::newRow("warning on other wrong channel") @@ -406,7 +406,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << QString() << 1 << 2 << 3 << simpleWarningPattern2 << 2 << 3 << 1 << QString() << simpleWarningPassThrough2 - << QList<Task>() + << Tasks() << QString(); QTest::newRow("error and *warning*") @@ -417,7 +417,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << simplePattern << 1 << 2 << 3 << simpleWarningPattern << 1 << 2 << 3 << QString() << QString() - << QList<Task>{Task(Task::Warning, warningMessage, fileName, 1234, categoryCompile)} + << Tasks{Task(Task::Warning, warningMessage, fileName, 1234, categoryCompile)} << QString(); QTest::newRow("*error* when equal pattern") @@ -428,11 +428,11 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << simplePattern << 1 << 2 << 3 << simplePattern << 1 << 2 << 3 << QString() << QString() - << QList<Task>{Task(Task::Error, message, fileName, 9, categoryCompile)} + << Tasks{Task(Task::Error, message, fileName, 9, categoryCompile)} << QString(); const QString unitTestError = "../LedDriver/LedDriverTest.c:63: FAIL: Expected 0x0080 Was 0xffff"; - const FileName unitTestFileName = FileName::fromUserInput("../LedDriver/LedDriverTest.c"); + const FilePath unitTestFileName = FilePath::fromUserInput("../LedDriver/LedDriverTest.c"); const QString unitTestMessage = "Expected 0x0080 Was 0xffff"; const QString unitTestPattern = "^([^:]+):(\\d+): FAIL: ([^\\s].+)$"; const int unitTestLineNumber = 63; @@ -445,7 +445,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data() << unitTestPattern << 1 << 2 << 3 << QString() << 1 << 2 << 3 << QString() << QString() - << QList<Task>{Task(Task::Error, unitTestMessage, unitTestFileName, unitTestLineNumber, categoryCompile)} + << Tasks{Task(Task::Error, unitTestMessage, unitTestFileName, unitTestLineNumber, categoryCompile)} << QString(); } @@ -466,7 +466,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers() QFETCH(int, warningMessageCap); QFETCH(QString, childStdOutLines); QFETCH(QString, childStdErrLines); - QFETCH(QList<Task>, tasks); + QFETCH(Tasks, tasks); QFETCH(QString, outputLines); CustomParserSettings settings; diff --git a/src/plugins/projectexplorer/customparser.h b/src/plugins/projectexplorer/customparser.h index b7c70d4759..dafb0e8da1 100644 --- a/src/plugins/projectexplorer/customparser.h +++ b/src/plugins/projectexplorer/customparser.h @@ -96,7 +96,7 @@ public: static Core::Id id(); private: - Utils::FileName absoluteFilePath(const QString &filePath) const; + Utils::FilePath absoluteFilePath(const QString &filePath) const; bool hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel, const CustomParserExpression &expression, Task::TaskType taskType); bool parseLine(const QString &rawLine, CustomParserExpression::CustomParserChannel channel); diff --git a/src/plugins/projectexplorer/customparserconfigdialog.cpp b/src/plugins/projectexplorer/customparserconfigdialog.cpp index b688e8aca1..061d1814ed 100644 --- a/src/plugins/projectexplorer/customparserconfigdialog.cpp +++ b/src/plugins/projectexplorer/customparserconfigdialog.cpp @@ -42,20 +42,20 @@ CustomParserConfigDialog::CustomParserConfigDialog(QDialog *parent) : connect(ui->errorPattern, &QLineEdit::textChanged, this, &CustomParserConfigDialog::changed); connect(ui->errorOutputMessage, &QLineEdit::textChanged, this, &CustomParserConfigDialog::changed); - connect(ui->errorFileNameCap, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + connect(ui->errorFileNameCap, QOverload<int>::of(&QSpinBox::valueChanged), this, &CustomParserConfigDialog::changed); - connect(ui->errorLineNumberCap, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + connect(ui->errorLineNumberCap, QOverload<int>::of(&QSpinBox::valueChanged), this, &CustomParserConfigDialog::changed); - connect(ui->errorMessageCap, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + connect(ui->errorMessageCap, QOverload<int>::of(&QSpinBox::valueChanged), this, &CustomParserConfigDialog::changed); connect(ui->warningPattern, &QLineEdit::textChanged, this, &CustomParserConfigDialog::changed); connect(ui->warningOutputMessage, &QLineEdit::textChanged, this, &CustomParserConfigDialog::changed); - connect(ui->warningFileNameCap, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + connect(ui->warningFileNameCap, QOverload<int>::of(&QSpinBox::valueChanged), this, &CustomParserConfigDialog::changed); - connect(ui->warningLineNumberCap, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + connect(ui->warningLineNumberCap, QOverload<int>::of(&QSpinBox::valueChanged), this, &CustomParserConfigDialog::changed); - connect(ui->warningMessageCap, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + connect(ui->warningMessageCap, QOverload<int>::of(&QSpinBox::valueChanged), this, &CustomParserConfigDialog::changed); changed(); diff --git a/src/plugins/projectexplorer/customtoolchain.cpp b/src/plugins/projectexplorer/customtoolchain.cpp index 1833875102..e1175963ea 100644 --- a/src/plugins/projectexplorer/customtoolchain.cpp +++ b/src/plugins/projectexplorer/customtoolchain.cpp @@ -81,17 +81,11 @@ static const char warningExampleKeyC[] = "ProjectExplorer.CustomToolChain.Warnin // CustomToolChain // -------------------------------------------------------------------------- -CustomToolChain::CustomToolChain(Detection d) : - ToolChain(Constants::CUSTOM_TOOLCHAIN_TYPEID, d), +CustomToolChain::CustomToolChain() : + ToolChain(Constants::CUSTOM_TOOLCHAIN_TYPEID), m_outputParserId(GccParser::id()) { } -CustomToolChain::CustomToolChain(Core::Id language, Detection d) : CustomToolChain(d) -{ - setLanguage(language); -} - - QString CustomToolChain::typeDisplayName() const { return Internal::CustomToolChainFactory::tr("Custom"); @@ -169,7 +163,7 @@ ToolChain::BuiltInHeaderPathsRunner CustomToolChain::createBuiltInHeaderPathsRun const HeaderPaths builtInHeaderPaths = m_builtInHeaderPaths; // This runner must be thread-safe! - return [builtInHeaderPaths](const QStringList &cxxFlags, const QString &) { + return [builtInHeaderPaths](const QStringList &cxxFlags, const QString &, const QString &) { HeaderPaths flagHeaderPaths; for (const QString &cxxFlag : cxxFlags) { if (cxxFlag.startsWith(QLatin1String("-I"))) { @@ -182,23 +176,23 @@ ToolChain::BuiltInHeaderPathsRunner CustomToolChain::createBuiltInHeaderPathsRun } HeaderPaths CustomToolChain::builtInHeaderPaths(const QStringList &cxxFlags, - const FileName &fileName) const + const FilePath &fileName) const { - return createBuiltInHeaderPathsRunner()(cxxFlags, fileName.toString()); + return createBuiltInHeaderPathsRunner()(cxxFlags, fileName.toString(), ""); } void CustomToolChain::addToEnvironment(Environment &env) const { if (!m_compilerCommand.isEmpty()) { - FileName path = m_compilerCommand.parentDir(); + FilePath path = m_compilerCommand.parentDir(); env.prependOrSetPath(path.toString()); - FileName makePath = m_makeCommand.parentDir(); + FilePath makePath = m_makeCommand.parentDir(); if (makePath != path) env.prependOrSetPath(makePath.toString()); } } -FileNameList CustomToolChain::suggestedMkspecList() const +QStringList CustomToolChain::suggestedMkspecList() const { return m_mkspecs; } @@ -235,7 +229,7 @@ void CustomToolChain::setHeaderPaths(const QStringList &list) toolChainUpdated(); } -void CustomToolChain::setCompilerCommand(const FileName &path) +void CustomToolChain::setCompilerCommand(const FilePath &path) { if (path == m_compilerCommand) return; @@ -243,12 +237,12 @@ void CustomToolChain::setCompilerCommand(const FileName &path) toolChainUpdated(); } -FileName CustomToolChain::compilerCommand() const +FilePath CustomToolChain::compilerCommand() const { return m_compilerCommand; } -void CustomToolChain::setMakeCommand(const FileName &path) +void CustomToolChain::setMakeCommand(const FilePath &path) { if (path == m_makeCommand) return; @@ -256,9 +250,9 @@ void CustomToolChain::setMakeCommand(const FileName &path) toolChainUpdated(); } -QString CustomToolChain::makeCommand(const Environment &) const +FilePath CustomToolChain::makeCommand(const Environment &) const { - return m_makeCommand.toString(); + return m_makeCommand; } void CustomToolChain::setCxx11Flags(const QStringList &flags) @@ -276,10 +270,7 @@ const QStringList &CustomToolChain::cxx11Flags() const void CustomToolChain::setMkspecs(const QString &specs) { - Utils::FileNameList tmp - = Utils::transform(specs.split(QLatin1Char(',')), - [](QString fn) { return FileName::fromString(fn); }); - + const QStringList tmp = specs.split(','); if (tmp == m_mkspecs) return; m_mkspecs = tmp; @@ -288,16 +279,7 @@ void CustomToolChain::setMkspecs(const QString &specs) QString CustomToolChain::mkspecs() const { - QString list; - for (const FileName &spec : m_mkspecs) - list.append(spec.toString() + QLatin1Char(',')); - list.chop(1); - return list; -} - -ToolChain *CustomToolChain::clone() const -{ - return new CustomToolChain(*this); + return m_mkspecs.join(','); } QVariantMap CustomToolChain::toMap() const @@ -333,8 +315,8 @@ bool CustomToolChain::fromMap(const QVariantMap &data) if (!ToolChain::fromMap(data)) return false; - m_compilerCommand = FileName::fromString(data.value(QLatin1String(compilerCommandKeyC)).toString()); - m_makeCommand = FileName::fromString(data.value(QLatin1String(makeCommandKeyC)).toString()); + m_compilerCommand = FilePath::fromString(data.value(QLatin1String(compilerCommandKeyC)).toString()); + m_makeCommand = FilePath::fromString(data.value(QLatin1String(makeCommandKeyC)).toString()); m_targetAbi = Abi::fromString(data.value(QLatin1String(targetAbiKeyC)).toString()); const QStringList macros = data.value(QLatin1String(predefinedMacrosKeyC)).toStringList(); m_predefinedMacros = Macro::toMacros(macros.join('\n').toUtf8()); @@ -425,37 +407,10 @@ namespace Internal { CustomToolChainFactory::CustomToolChainFactory() { setDisplayName(tr("Custom")); -} - -QSet<Core::Id> CustomToolChainFactory::supportedLanguages() const -{ - return ToolChainManager::allLanguages(); -} - -bool CustomToolChainFactory::canCreate() -{ - return true; -} - -ToolChain *CustomToolChainFactory::create(Core::Id language) -{ - return new CustomToolChain(language, ToolChain::ManualDetection); -} - -// Used by the ToolChainManager to restore user-generated tool chains -bool CustomToolChainFactory::canRestore(const QVariantMap &data) -{ - return typeIdFromMap(data) == Constants::CUSTOM_TOOLCHAIN_TYPEID; -} - -ToolChain *CustomToolChainFactory::restore(const QVariantMap &data) -{ - auto tc = new CustomToolChain(ToolChain::ManualDetection); - if (tc->fromMap(data)) - return tc; - - delete tc; - return nullptr; + setSupportedToolChainType(Constants::CUSTOM_TOOLCHAIN_TYPEID); + setSupportsAllLanguages(true); + setToolchainConstructor([] { return new CustomToolChain; }); + setUserCreatable(true); } // -------------------------------------------------------------------------- @@ -566,7 +521,7 @@ CustomToolChainConfigWidget::CustomToolChainConfigWidget(CustomToolChain *tc) : this, &CustomToolChainConfigWidget::updateSummaries); connect(m_cxx11Flags, &QLineEdit::textChanged, this, &ToolChainConfigWidget::dirty); connect(m_mkspecs, &QLineEdit::textChanged, this, &ToolChainConfigWidget::dirty); - connect(m_errorParserComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(m_errorParserComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &CustomToolChainConfigWidget::errorParserChanged); connect(m_customParserSettingsButton, &QAbstractButton::clicked, this, &CustomToolChainConfigWidget::openCustomParserSettingsDialog); @@ -635,8 +590,8 @@ void CustomToolChainConfigWidget::setFromToolchain() QSignalBlocker blocker(this); auto tc = static_cast<CustomToolChain *>(toolChain()); m_compilerCommand->setFileName(tc->compilerCommand()); - m_makeCommand->setFileName(FileName::fromString(tc->makeCommand(Environment()))); - m_abiWidget->setAbis(QList<Abi>(), tc->targetAbi()); + m_makeCommand->setFileName(tc->makeCommand(Environment())); + m_abiWidget->setAbis(Abis(), tc->targetAbi()); const QStringList macroLines = Utils::transform<QList>(tc->rawPredefinedMacros(), [](const Macro &m) { return QString::fromUtf8(m.toKeyValue(QByteArray())); }); @@ -654,7 +609,7 @@ bool CustomToolChainConfigWidget::isDirtyImpl() const auto tc = static_cast<CustomToolChain *>(toolChain()); Q_ASSERT(tc); return m_compilerCommand->fileName() != tc->compilerCommand() - || m_makeCommand->path() != tc->makeCommand(Environment()) + || m_makeCommand->path() != tc->makeCommand(Environment()).toString() || m_abiWidget->currentAbi() != tc->targetAbi() || Macro::toMacros(m_predefinedDetails->text().toUtf8()) != tc->rawPredefinedMacros() || m_headerDetails->entries() != tc->headerPathsList() diff --git a/src/plugins/projectexplorer/customtoolchain.h b/src/plugins/projectexplorer/customtoolchain.h index 37ca6abd15..415cf89abd 100644 --- a/src/plugins/projectexplorer/customtoolchain.h +++ b/src/plugins/projectexplorer/customtoolchain.h @@ -80,9 +80,9 @@ public: BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner() const override; HeaderPaths builtInHeaderPaths(const QStringList &cxxFlags, - const Utils::FileName &) const override; + const Utils::FilePath &) const override; void addToEnvironment(Utils::Environment &env) const override; - Utils::FileNameList suggestedMkspecList() const override; + QStringList suggestedMkspecList() const override; IOutputParser *outputParser() const override; QStringList headerPathsList() const; void setHeaderPaths(const QStringList &list); @@ -94,10 +94,10 @@ public: bool operator ==(const ToolChain &) const override; - void setCompilerCommand(const Utils::FileName &); - Utils::FileName compilerCommand() const override; - void setMakeCommand(const Utils::FileName &); - QString makeCommand(const Utils::Environment &environment) const override; + void setCompilerCommand(const Utils::FilePath &); + Utils::FilePath compilerCommand() const override; + void setMakeCommand(const Utils::FilePath &); + Utils::FilePath makeCommand(const Utils::Environment &environment) const override; void setCxx11Flags(const QStringList &); const QStringList &cxx11Flags() const; @@ -105,29 +105,23 @@ public: void setMkspecs(const QString &); QString mkspecs() const; - ToolChain *clone() const override; - Core::Id outputParserId() const; void setOutputParserId(Core::Id parserId); CustomParserSettings customParserSettings() const; void setCustomParserSettings(const CustomParserSettings &settings); static QList<CustomToolChain::Parser> parsers(); -protected: - CustomToolChain(const CustomToolChain &) = default; - private: - explicit CustomToolChain(Detection d); - explicit CustomToolChain(Core::Id language, Detection d); + CustomToolChain(); - Utils::FileName m_compilerCommand; - Utils::FileName m_makeCommand; + Utils::FilePath m_compilerCommand; + Utils::FilePath m_makeCommand; Abi m_targetAbi; Macros m_predefinedMacros; HeaderPaths m_builtInHeaderPaths; QStringList m_cxx11Flags; - Utils::FileNameList m_mkspecs; + QStringList m_mkspecs; Core::Id m_outputParserId; CustomParserSettings m_customParserSettings; @@ -144,14 +138,6 @@ class CustomToolChainFactory : public ToolChainFactory public: CustomToolChainFactory(); - QSet<Core::Id> supportedLanguages() const override; - - bool canCreate() override; - ToolChain *create(Core::Id language) override; - - // Used by the ToolChainManager to restore user-generated tool chains - bool canRestore(const QVariantMap &data) override; - ToolChain *restore(const QVariantMap &data) override; }; // -------------------------------------------------------------------------- diff --git a/src/plugins/projectexplorer/customwizard/customwizard.cpp b/src/plugins/projectexplorer/customwizard/customwizard.cpp index 9a3ea42a4e..1f92543431 100644 --- a/src/plugins/projectexplorer/customwizard/customwizard.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizard.cpp @@ -406,7 +406,7 @@ QList<Core::IWizardFactory *> CustomWizard::createWizards() const QDir::Filters filters = QDir::Dirs|QDir::Readable|QDir::NoDotAndDotDot; const QDir::SortFlags sortflags = QDir::Name|QDir::IgnoreCase; - QList<QFileInfo> dirs; + QFileInfoList dirs; if (userTemplateDir.exists()) { if (CustomWizardPrivate::verbose) verboseLog += QString::fromLatin1("### CustomWizard: userTemplateDir \"%1\" found, adding\n").arg(userTemplateDirName); @@ -446,7 +446,7 @@ QList<Core::IWizardFactory *> CustomWizard::createWizards() break; } } else { - QList<QFileInfo> subDirs = dir.entryInfoList(filters, sortflags); + QFileInfoList subDirs = dir.entryInfoList(filters, sortflags); if (!subDirs.isEmpty()) { // There is no QList::prepend(QList)... dirs.swap(subDirs); diff --git a/src/plugins/projectexplorer/deployablefile.cpp b/src/plugins/projectexplorer/deployablefile.cpp index 1d7b6132c1..d8ea6cd33a 100644 --- a/src/plugins/projectexplorer/deployablefile.cpp +++ b/src/plugins/projectexplorer/deployablefile.cpp @@ -34,10 +34,10 @@ using namespace Utils; namespace ProjectExplorer { DeployableFile::DeployableFile(const QString &localFilePath, const QString &remoteDir, Type type) - : m_localFilePath(FileName::fromUserInput(localFilePath)), m_remoteDir(remoteDir), m_type(type) + : m_localFilePath(FilePath::fromUserInput(localFilePath)), m_remoteDir(remoteDir), m_type(type) { } -DeployableFile::DeployableFile(const FileName &localFilePath, const QString &remoteDir, Type type) +DeployableFile::DeployableFile(const FilePath &localFilePath, const QString &remoteDir, Type type) : m_localFilePath(localFilePath), m_remoteDir(remoteDir), m_type(type) { } diff --git a/src/plugins/projectexplorer/deployablefile.h b/src/plugins/projectexplorer/deployablefile.h index 70d05c29e8..b1a6c6a1d6 100644 --- a/src/plugins/projectexplorer/deployablefile.h +++ b/src/plugins/projectexplorer/deployablefile.h @@ -45,10 +45,10 @@ public: DeployableFile() = default; DeployableFile(const QString &m_localFilePath, const QString &m_remoteDir, Type type = TypeNormal); - DeployableFile(const Utils::FileName &localFilePath, const QString &remoteDir, + DeployableFile(const Utils::FilePath &localFilePath, const QString &remoteDir, Type type = TypeNormal); - Utils::FileName localFilePath() const { return m_localFilePath; } + Utils::FilePath localFilePath() const { return m_localFilePath; } QString remoteDirectory() const { return m_remoteDir; } QString remoteFilePath() const; @@ -57,7 +57,7 @@ public: bool isExecutable() const; private: - Utils::FileName m_localFilePath; + Utils::FilePath m_localFilePath; QString m_remoteDir; Type m_type = TypeNormal; }; diff --git a/src/plugins/projectexplorer/deployconfiguration.cpp b/src/plugins/projectexplorer/deployconfiguration.cpp index 7619a06ca2..4d16599a12 100644 --- a/src/plugins/projectexplorer/deployconfiguration.cpp +++ b/src/plugins/projectexplorer/deployconfiguration.cpp @@ -162,7 +162,7 @@ bool DeployConfigurationFactory::canHandle(Target *target) const if (!m_supportedTargetDeviceTypes.isEmpty()) { if (!m_supportedTargetDeviceTypes.contains( - DeviceTypeKitInformation::deviceTypeId(target->kit()))) + DeviceTypeKitAspect::deviceTypeId(target->kit()))) return false; } diff --git a/src/plugins/projectexplorer/deploymentdata.cpp b/src/plugins/projectexplorer/deploymentdata.cpp index c34b6a4044..7cdc4a1185 100644 --- a/src/plugins/projectexplorer/deploymentdata.cpp +++ b/src/plugins/projectexplorer/deploymentdata.cpp @@ -33,7 +33,7 @@ namespace ProjectExplorer { -void DeploymentData::setLocalInstallRoot(const Utils::FileName &installRoot) +void DeploymentData::setLocalInstallRoot(const Utils::FilePath &installRoot) { m_localInstallRoot = installRoot; } diff --git a/src/plugins/projectexplorer/deploymentdata.h b/src/plugins/projectexplorer/deploymentdata.h index 7297ad84b2..e45427ebf8 100644 --- a/src/plugins/projectexplorer/deploymentdata.h +++ b/src/plugins/projectexplorer/deploymentdata.h @@ -28,18 +28,30 @@ #include "deployablefile.h" #include "projectexplorer_export.h" +#include <utils/environment.h> + #include <QList> namespace ProjectExplorer { +enum class DeploymentKnowledge { Perfect, Approximative, Bad }; + +class PROJECTEXPLORER_EXPORT MakeInstallCommand +{ +public: + Utils::FilePath command; + QStringList arguments; + Utils::Environment environment; +}; + class PROJECTEXPLORER_EXPORT DeploymentData { public: void setFileList(const QList<DeployableFile> &files) { m_files = files; } QList<DeployableFile> allFiles() const { return m_files; } - void setLocalInstallRoot(const Utils::FileName &installRoot); - Utils::FileName localInstallRoot() const { return m_localInstallRoot; } + void setLocalInstallRoot(const Utils::FilePath &installRoot); + Utils::FilePath localInstallRoot() const { return m_localInstallRoot; } void addFile(const DeployableFile &file); void addFile(const QString &localFilePath, const QString &remoteDirectory, @@ -54,7 +66,7 @@ public: private: QList<DeployableFile> m_files; - Utils::FileName m_localInstallRoot; + Utils::FilePath m_localInstallRoot; }; inline bool operator!=(const DeploymentData &d1, const DeploymentData &d2) { return !(d1 == d2); } diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp index 283e921d92..a725658b9b 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp @@ -30,8 +30,10 @@ #include "desktopdeviceconfigurationwidget.h" #include "desktopprocesssignaloperation.h" +#include <coreplugin/fileutils.h> + #include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/runconfiguration.h> +#include <projectexplorer/runcontrol.h> #include <ssh/sshconnection.h> @@ -58,10 +60,11 @@ DesktopDevice::DesktopDevice() const QString portRange = QString::fromLatin1("%1-%2").arg(DESKTOP_PORT_START).arg(DESKTOP_PORT_END); setFreePorts(Utils::PortList::fromString(portRange)); + setOpenTerminal([](const Utils::Environment &env, const QString &workingDir) { + Core::FileUtils::openTerminal(workingDir, env); + }); } -DesktopDevice::DesktopDevice(const DesktopDevice &other) = default; - IDevice::DeviceInfo DesktopDevice::deviceInformation() const { return DeviceInfo(); @@ -177,9 +180,4 @@ Utils::OsType DesktopDevice::osType() const return Utils::HostOsInfo::hostOs(); } -IDevice::Ptr DesktopDevice::clone() const -{ - return Ptr(new DesktopDevice(*this)); -} - } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.h b/src/plugins/projectexplorer/devicesupport/desktopdevice.h index da57cf95bd..dec063b5e5 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.h +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.h @@ -53,11 +53,8 @@ public: QUrl toolControlChannel(const ControlChannelHint &) const override; Utils::OsType osType() const override; - IDevice::Ptr clone() const override; - protected: DesktopDevice(); - DesktopDevice(const DesktopDevice &other); friend class ProjectExplorerPlugin; friend class Internal::DesktopDeviceFactory; diff --git a/src/plugins/projectexplorer/devicesupport/desktopdeviceprocess.cpp b/src/plugins/projectexplorer/devicesupport/desktopdeviceprocess.cpp index d57b765bb0..bb9f6c8f43 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdeviceprocess.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopdeviceprocess.cpp @@ -26,7 +26,7 @@ #include "desktopdeviceprocess.h" #include "idevice.h" -#include "../runconfiguration.h" +#include "../runcontrol.h" #include <utils/environment.h> #include <utils/qtcassert.h> diff --git a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp index 7935efb5fc..29e95751f6 100644 --- a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp @@ -44,9 +44,9 @@ DeviceCheckBuildStep::DeviceCheckBuildStep(BuildStepList *bsl) bool DeviceCheckBuildStep::init() { - IDevice::ConstPtr device = DeviceKitInformation::device(target()->kit()); + IDevice::ConstPtr device = DeviceKitAspect::device(target()->kit()); if (!device) { - Core::Id deviceTypeId = DeviceTypeKitInformation::deviceTypeId(target()->kit()); + Core::Id deviceTypeId = DeviceTypeKitAspect::deviceTypeId(target()->kit()); IDeviceFactory *factory = IDeviceFactory::find(deviceTypeId); if (!factory || !factory->canCreate()) { emit addOutput(tr("No device configured."), BuildStep::OutputFormat::ErrorMessage); @@ -71,7 +71,7 @@ bool DeviceCheckBuildStep::init() DeviceManager *dm = DeviceManager::instance(); dm->addDevice(newDevice); - DeviceKitInformation::setDevice(target()->kit(), newDevice); + DeviceKitAspect::setDevice(target()->kit(), newDevice); } return true; diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index d8c72aa354..cd6e7421aa 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -221,14 +221,14 @@ QVariantMap DeviceManager::toMap() const return map; } -Utils::FileName DeviceManager::settingsFilePath(const QString &extension) +Utils::FilePath DeviceManager::settingsFilePath(const QString &extension) { - return Utils::FileName::fromString(Core::ICore::userResourcePath() + extension); + return Utils::FilePath::fromString(Core::ICore::userResourcePath() + extension); } -Utils::FileName DeviceManager::systemSettingsFilePath(const QString &deviceFileRelativePath) +Utils::FilePath DeviceManager::systemSettingsFilePath(const QString &deviceFileRelativePath) { - return Utils::FileName::fromString(Core::ICore::installerResourcePath() + return Utils::FilePath::fromString(Core::ICore::installerResourcePath() + deviceFileRelativePath); } @@ -414,10 +414,8 @@ public: static Core::Id testTypeId() { return "TestType"; } private: - TestDevice(const TestDevice &other) = default; QString displayType() const override { return QLatin1String("blubb"); } IDeviceWidget *createWidget() override { return nullptr; } - Ptr clone() const override { return Ptr(new TestDevice(*this)); } DeviceProcessSignalOperation::Ptr signalOperation() const override { return DeviceProcessSignalOperation::Ptr(); @@ -425,8 +423,19 @@ private: Utils::OsType osType() const override { return Utils::HostOsInfo::hostOs(); } }; +class TestDeviceFactory : public IDeviceFactory +{ +public: + TestDeviceFactory() : IDeviceFactory(TestDevice::testTypeId()) + { + setConstructionFunction([] { return IDevice::Ptr(new TestDevice); }); + } +}; + void ProjectExplorerPlugin::testDeviceManager() { + TestDeviceFactory factory; + TestDevice::Ptr dev = IDevice::Ptr(new TestDevice); dev->setDisplayName(QLatin1String("blubbdiblubbfurz!")); QVERIFY(dev->isAutoDetected()); diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.h b/src/plugins/projectexplorer/devicesupport/devicemanager.h index bd9c2b606e..6f286845dd 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.h +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.h @@ -33,7 +33,7 @@ #include <memory> -namespace Utils { class FileName; } +namespace Utils { class FilePath; } namespace ProjectExplorer { class IDevice; @@ -96,8 +96,8 @@ private: static void replaceInstance(); static void removeClonedInstance(); - static Utils::FileName settingsFilePath(const QString &extension); - static Utils::FileName systemSettingsFilePath(const QString &deviceFileRelativePath); + static Utils::FilePath settingsFilePath(const QString &extension); + static Utils::FilePath systemSettingsFilePath(const QString &deviceFileRelativePath); static void copy(const DeviceManager *source, DeviceManager *target, bool deep); const std::unique_ptr<Internal::DeviceManagerPrivate> d; diff --git a/src/plugins/projectexplorer/devicesupport/deviceprocess.h b/src/plugins/projectexplorer/devicesupport/deviceprocess.h index e26151d251..4aa197666c 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceprocess.h +++ b/src/plugins/projectexplorer/devicesupport/deviceprocess.h @@ -26,7 +26,6 @@ #pragma once #include "../projectexplorer_export.h" -#include "../runconfiguration.h" #include <QObject> #include <QProcess> diff --git a/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.cpp b/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.cpp index dd6a851929..2e0cf6a660 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.cpp +++ b/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.cpp @@ -185,14 +185,10 @@ DeviceProcessesDialogPrivate::DeviceProcessesDialogPrivate(KitChooser *chooser, proxyModel.setFilterRegExp(processFilterLineEdit->text()); - connect(processFilterLineEdit, - static_cast<void (FancyLineEdit::*)(const QString &)>(&FancyLineEdit::textChanged), - &proxyModel, - static_cast<void (ProcessListFilterModel::*)(const QString &)>( - &ProcessListFilterModel::setFilterRegExp)); - connect(procView->selectionModel(), - &QItemSelectionModel::selectionChanged, - this, &DeviceProcessesDialogPrivate::updateButtons); + connect(processFilterLineEdit, QOverload<const QString &>::of(&FancyLineEdit::textChanged), + &proxyModel, QOverload<const QString &>::of(&ProcessListFilterModel::setFilterRegExp)); + connect(procView->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &DeviceProcessesDialogPrivate::updateButtons); connect(updateListButton, &QAbstractButton::clicked, this, &DeviceProcessesDialogPrivate::updateProcessList); connect(kitChooser, &KitChooser::currentIndexChanged, @@ -219,7 +215,7 @@ void DeviceProcessesDialogPrivate::setDevice(const IDevice::ConstPtr &device) processList = device->createProcessListModel(); QTC_ASSERT(processList, return); - proxyModel.setSourceModel(processList); + proxyModel.setSourceModel(processList->model()); connect(processList, &DeviceProcessList::error, this, &DeviceProcessesDialogPrivate::handleRemoteError); @@ -267,7 +263,7 @@ void DeviceProcessesDialogPrivate::killProcess() void DeviceProcessesDialogPrivate::updateDevice() { - setDevice(DeviceKitInformation::device(kitChooser->currentKit())); + setDevice(DeviceKitAspect::device(kitChooser->currentKit())); } void DeviceProcessesDialogPrivate::handleProcessKilled() diff --git a/src/plugins/projectexplorer/devicesupport/deviceprocesslist.cpp b/src/plugins/projectexplorer/devicesupport/deviceprocesslist.cpp index e6075da516..0405956100 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceprocesslist.cpp +++ b/src/plugins/projectexplorer/devicesupport/deviceprocesslist.cpp @@ -27,23 +27,38 @@ #include "localprocesslist.h" #include <utils/qtcassert.h> +#include <utils/treemodel.h> + +using namespace Utils; namespace ProjectExplorer { namespace Internal { enum State { Inactive, Listing, Killing }; +class DeviceProcessTreeItem : public TreeItem +{ +public: + DeviceProcessTreeItem(const DeviceProcessItem &p, Qt::ItemFlags f) : process(p), fl(f) {} + + QVariant data(int column, int role) const final; + Qt::ItemFlags flags(int) const final { return fl; } + + DeviceProcessItem process; + Qt::ItemFlags fl; +}; + class DeviceProcessListPrivate { public: DeviceProcessListPrivate(const IDevice::ConstPtr &device) - : device(device), - state(Inactive) + : device(device) { } + qint64 ownPid = -1; const IDevice::ConstPtr device; - QList<DeviceProcessItem> remoteProcesses; - State state; + State state = Inactive; + TreeModel<TypedTreeItem<DeviceProcessTreeItem>, DeviceProcessTreeItem> model; }; } // namespace Internal @@ -51,39 +66,19 @@ public: using namespace Internal; DeviceProcessList::DeviceProcessList(const IDevice::ConstPtr &device, QObject *parent) - : QAbstractItemModel(parent), d(std::make_unique<DeviceProcessListPrivate>(device)) -{ } - -DeviceProcessList::~DeviceProcessList() = default; - -QModelIndex DeviceProcessList::parent(const QModelIndex &) const + : QObject(parent), d(std::make_unique<DeviceProcessListPrivate>(device)) { - return QModelIndex(); -} - -bool DeviceProcessList::hasChildren(const QModelIndex &parent) const -{ - if (!parent.isValid()) - return rowCount(parent) > 0 && columnCount(parent) > 0; - return false; -} - -QModelIndex DeviceProcessList::index(int row, int column, const QModelIndex &parent) const -{ - return hasIndex(row, column, parent) ? createIndex(row, column) : QModelIndex(); + d->model.setHeader({tr("Process ID"), tr("Command Line")}); } +DeviceProcessList::~DeviceProcessList() = default; void DeviceProcessList::update() { QTC_ASSERT(d->state == Inactive, return); QTC_ASSERT(device(), return); - if (!d->remoteProcesses.isEmpty()) { - beginRemoveRows(QModelIndex(), 0, d->remoteProcesses.count() - 1); - d->remoteProcesses.clear(); - endRemoveRows(); - } + d->model.clear(); d->state = Listing; doUpdate(); } @@ -92,22 +87,29 @@ void DeviceProcessList::reportProcessListUpdated(const QList<DeviceProcessItem> { QTC_ASSERT(d->state == Listing, return); setFinished(); - if (!processes.isEmpty()) { - beginInsertRows(QModelIndex(), 0, processes.count() - 1); - d->remoteProcesses = processes; - endInsertRows(); + for (const DeviceProcessItem &process : processes) { + Qt::ItemFlags fl; + if (process.pid != d->ownPid) + fl = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + d->model.rootItem()->appendChild(new DeviceProcessTreeItem(process, fl)); } + emit processListUpdated(); } void DeviceProcessList::killProcess(int row) { - QTC_ASSERT(row >= 0 && row < d->remoteProcesses.count(), return); + QTC_ASSERT(row >= 0 && row < d->model.rootItem()->childCount(), return); QTC_ASSERT(d->state == Inactive, return); QTC_ASSERT(device(), return); d->state = Killing; - doKillProcess(d->remoteProcesses.at(row)); + doKillProcess(at(row)); +} + +void DeviceProcessList::setOwnPid(qint64 pid) +{ + d->ownPid = pid; } void DeviceProcessList::reportProcessKilled() @@ -119,40 +121,21 @@ void DeviceProcessList::reportProcessKilled() DeviceProcessItem DeviceProcessList::at(int row) const { - return d->remoteProcesses.at(row); + return d->model.rootItem()->childAt(row)->process; } -int DeviceProcessList::rowCount(const QModelIndex &parent) const +QAbstractItemModel *DeviceProcessList::model() const { - return parent.isValid() ? 0 : d->remoteProcesses.count(); + return &d->model; } -int DeviceProcessList::columnCount(const QModelIndex &) const +QVariant DeviceProcessTreeItem::data(int column, int role) const { - return 2; -} - -QVariant DeviceProcessList::headerData(int section, Qt::Orientation orientation, - int role) const -{ - if (orientation != Qt::Horizontal || role != Qt::DisplayRole || section < 0 - || section >= columnCount()) - return QVariant(); - return section == 0? tr("Process ID") : tr("Command Line"); -} - -QVariant DeviceProcessList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid() || index.row() >= rowCount(index.parent()) - || index.column() >= columnCount()) - return QVariant(); - if (role == Qt::DisplayRole || role == Qt::ToolTipRole) { - const DeviceProcessItem &proc = d->remoteProcesses.at(index.row()); - if (index.column() == 0) - return proc.pid; + if (column == 0) + return process.pid; else - return proc.cmdLine; + return process.cmdLine; } return QVariant(); } diff --git a/src/plugins/projectexplorer/devicesupport/deviceprocesslist.h b/src/plugins/projectexplorer/devicesupport/deviceprocesslist.h index 18fa97337d..c7959dc3a5 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceprocesslist.h +++ b/src/plugins/projectexplorer/devicesupport/deviceprocesslist.h @@ -46,7 +46,7 @@ public: QString exe; }; -class PROJECTEXPLORER_EXPORT DeviceProcessList : public QAbstractItemModel +class PROJECTEXPLORER_EXPORT DeviceProcessList : public QObject { Q_OBJECT @@ -56,7 +56,10 @@ public: void update(); void killProcess(int row); + void setOwnPid(qint64 pid); + DeviceProcessItem at(int row) const; + QAbstractItemModel *model() const; static QList<DeviceProcessItem> localProcesses(); @@ -73,15 +76,6 @@ protected: IDevice::ConstPtr device() const; private: - QModelIndex index(int row, int column, const QModelIndex &parent) const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - QModelIndex parent(const QModelIndex &) const override; - bool hasChildren(const QModelIndex &parent) const override; - virtual void doUpdate() = 0; virtual void doKillProcess(const DeviceProcessItem &process) = 0; diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp index 5cb6623f1c..af42589622 100644 --- a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp @@ -122,7 +122,7 @@ void DeviceSettingsWidget::initGui() lastIndex = 0; if (lastIndex < m_ui->configurationComboBox->count()) m_ui->configurationComboBox->setCurrentIndex(lastIndex); - connect(m_ui->configurationComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(m_ui->configurationComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &DeviceSettingsWidget::currentDeviceChanged); currentDeviceChanged(currentIndex()); connect(m_ui->defaultDeviceButton, &QAbstractButton::clicked, diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h index e739af3163..4344f059a5 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h +++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h @@ -27,7 +27,7 @@ #include "idevice.h" -#include <projectexplorer/runconfiguration.h> +#include <projectexplorer/runcontrol.h> #include <utils/portlist.h> diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index 54caab5f99..86d783a6d9 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -27,6 +27,7 @@ #include "devicemanager.h" #include "deviceprocesslist.h" +#include "idevicefactory.h" #include "../kit.h" #include "../kitinformation.h" @@ -152,6 +153,7 @@ public: QList<Utils::Icon> deviceIcons; QList<IDevice::DeviceAction> deviceActions; QVariantMap extraData; + IDevice::OpenTerminal openTerminal; }; } // namespace Internal @@ -161,6 +163,11 @@ IDevice::IDevice() : d(new Internal::IDevicePrivate) { } +void IDevice::setOpenTerminal(const IDevice::OpenTerminal &openTerminal) +{ + d->openTerminal = openTerminal; +} + void IDevice::setupId(Origin origin, Core::Id id) { d->origin = origin; @@ -168,11 +175,15 @@ void IDevice::setupId(Origin origin, Core::Id id) d->id = id.isValid() ? id : newId(); } -IDevice::IDevice(const IDevice &other) - : QEnableSharedFromThis<IDevice>(other) - , d(std::make_unique<Internal::IDevicePrivate>()) +bool IDevice::canOpenTerminal() const { - *d = *other.d; + return bool(d->openTerminal); +} + +void IDevice::openTerminal(const Utils::Environment &env, const QString &workingDir) const +{ + QTC_ASSERT(canOpenTerminal(), return); + d->openTerminal(env, workingDir); } IDevice::~IDevice() = default; @@ -249,7 +260,7 @@ Core::Id IDevice::id() const */ bool IDevice::isCompatibleWith(const Kit *k) const { - return DeviceTypeKitInformation::deviceTypeId(k) == type(); + return DeviceTypeKitAspect::deviceTypeId(k) == type(); } void IDevice::addDeviceAction(const DeviceAction &deviceAction) @@ -395,6 +406,19 @@ QVariantMap IDevice::toMap() const return map; } +IDevice::Ptr IDevice::clone() const +{ + IDeviceFactory *factory = IDeviceFactory::find(d->type); + QTC_ASSERT(factory, return {}); + IDevice::Ptr device = factory->construct(); + QTC_ASSERT(device, return {}); + device->d->deviceState = d->deviceState; + device->d->deviceActions = d->deviceActions; + device->d->deviceIcons = d->deviceIcons; + device->fromMap(toMap()); + return device; +} + QString IDevice::deviceStateToString() const { const char context[] = "ProjectExplorer::IDevice"; diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index 560325617c..b263544000 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -120,6 +120,7 @@ public: // See cpp file for documentation. class PROJECTEXPLORER_EXPORT IDevice : public QEnableSharedFromThis<IDevice> { + friend class Internal::IDevicePrivate; public: using Ptr = QSharedPointer<IDevice>; using ConstPtr = QSharedPointer<const IDevice>; @@ -127,9 +128,10 @@ public: enum Origin { ManuallyAdded, AutoDetected }; enum MachineType { Hardware, Emulator }; - IDevice &operator=(const IDevice &) = delete; virtual ~IDevice(); + Ptr clone() const; + QString displayName() const; void setDisplayName(const QString &name); @@ -187,7 +189,6 @@ public: virtual void fromMap(const QVariantMap &map); virtual QVariantMap toMap() const; - virtual Ptr clone() const = 0; static Core::Id typeFromMap(const QVariantMap &map); static Core::Id idFromMap(const QVariantMap &map); @@ -218,11 +219,19 @@ public: void setupId(Origin origin, Core::Id id = Core::Id()); + bool canOpenTerminal() const; + void openTerminal(const Utils::Environment &env, const QString &workingDir) const; + protected: IDevice(); - IDevice(const IDevice &other); + + using OpenTerminal = std::function<void(const Utils::Environment &, const QString &)>; + void setOpenTerminal(const OpenTerminal &openTerminal); private: + IDevice(const IDevice &) = delete; + IDevice &operator=(const IDevice &) = delete; + int version() const; const std::unique_ptr<Internal::IDevicePrivate> d; diff --git a/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp b/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp index b0d95c6474..832cbd5246 100644 --- a/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp +++ b/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp @@ -56,8 +56,8 @@ namespace Internal { LocalProcessList::LocalProcessList(const IDevice::ConstPtr &device, QObject *parent) : DeviceProcessList(device, parent) - , m_myPid(GetCurrentProcessId()) { + setOwnPid(GetCurrentProcessId()); } QList<DeviceProcessItem> LocalProcessList::getLocalProcesses() @@ -89,8 +89,9 @@ QList<DeviceProcessItem> LocalProcessList::getLocalProcesses() #ifdef Q_OS_UNIX LocalProcessList::LocalProcessList(const IDevice::ConstPtr &device, QObject *parent) : DeviceProcessList(device, parent) - , m_myPid(getpid()) -{} +{ + setOwnPid(getpid()); +} static bool isUnixProcessId(const QString &procname) { @@ -206,14 +207,6 @@ void LocalProcessList::doKillProcess(const DeviceProcessItem &process) signalOperation->killProcess(process.pid); } -Qt::ItemFlags LocalProcessList::flags(const QModelIndex &index) const -{ - Qt::ItemFlags flags = DeviceProcessList::flags(index); - if (index.isValid() && at(index.row()).pid == m_myPid) - flags &= ~(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - return flags; -} - void LocalProcessList::handleUpdate() { reportProcessListUpdated(getLocalProcesses()); diff --git a/src/plugins/projectexplorer/devicesupport/localprocesslist.h b/src/plugins/projectexplorer/devicesupport/localprocesslist.h index 599f20ed3e..8e10086849 100644 --- a/src/plugins/projectexplorer/devicesupport/localprocesslist.h +++ b/src/plugins/projectexplorer/devicesupport/localprocesslist.h @@ -36,7 +36,6 @@ class LocalProcessList : public DeviceProcessList public: explicit LocalProcessList(const IDevice::ConstPtr &device, QObject *parent = nullptr); - Qt::ItemFlags flags(const QModelIndex &index) const override; static QList<DeviceProcessItem> getLocalProcesses(); @@ -47,8 +46,6 @@ private: private: void handleUpdate(); void reportDelayedKillStatus(const QString &errorMessage); - - const qint64 m_myPid; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.cpp b/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.cpp index f094cb1473..7e8be72c65 100644 --- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.cpp +++ b/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.cpp @@ -26,7 +26,7 @@ #include "sshdeviceprocess.h" #include "idevice.h" -#include "../runconfiguration.h" +#include "../runcontrol.h" #include <ssh/sshconnection.h> #include <ssh/sshconnectionmanager.h> @@ -188,21 +188,21 @@ void SshDeviceProcess::handleConnected() d->process = runInTerminal() && d->runnable.executable.isEmpty() ? d->connection->createRemoteShell() - : d->connection->createRemoteProcess(fullCommandLine(d->runnable).toUtf8()); + : d->connection->createRemoteProcess(fullCommandLine(d->runnable)); const QString display = d->displayName(); if (!display.isEmpty()) d->process->requestX11Forwarding(display); if (runInTerminal()) { d->process->requestTerminal(); const QStringList cmdLine = d->process->fullLocalCommandLine(); - connect(&d->consoleProcess, - static_cast<void (ConsoleProcess::*)(QProcess::ProcessError)>(&ConsoleProcess::error), + connect(&d->consoleProcess, QOverload<QProcess::ProcessError>::of(&ConsoleProcess::error), this, &DeviceProcess::error); connect(&d->consoleProcess, &ConsoleProcess::processStarted, this, &SshDeviceProcess::handleProcessStarted); connect(&d->consoleProcess, &ConsoleProcess::stubStopped, this, [this] { handleProcessFinished(d->consoleProcess.errorString()); }); - d->consoleProcess.start(cmdLine.first(), cmdLine.mid(1).join(' ')); + d->consoleProcess.start(cmdLine.first(), cmdLine.mid(1).join(' '), + ConsoleProcess::MetaCharMode::Ignore); } else { connect(d->process.get(), &QSsh::SshRemoteProcess::started, this, &SshDeviceProcess::handleProcessStarted); diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp index c1c7b0a45f..d8107458fd 100644 --- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp +++ b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp @@ -54,7 +54,7 @@ void SshDeviceProcessList::doUpdate() this, &SshDeviceProcessList::handleConnectionError); connect(&d->process, &SshRemoteProcessRunner::processClosed, this, &SshDeviceProcessList::handleListProcessFinished); - d->process.run(listProcessesCommandLine().toUtf8(), device()->sshParameters()); + d->process.run(listProcessesCommandLine(), device()->sshParameters()); } void SshDeviceProcessList::doKillProcess(const DeviceProcessItem &process) diff --git a/src/plugins/projectexplorer/devicesupport/sshsettingspage.cpp b/src/plugins/projectexplorer/devicesupport/sshsettingspage.cpp index 32b6b3e83e..152467c93e 100644 --- a/src/plugins/projectexplorer/devicesupport/sshsettingspage.cpp +++ b/src/plugins/projectexplorer/devicesupport/sshsettingspage.cpp @@ -58,7 +58,7 @@ private: void setupSftpPathChooser(); void setupAskpassPathChooser(); void setupKeygenPathChooser(); - void setupPathChooser(PathChooser &chooser, const FileName &initialPath, bool &changedFlag); + void setupPathChooser(PathChooser &chooser, const FilePath &initialPath, bool &changedFlag); void updateCheckboxEnabled(); void updateSpinboxEnabled(); @@ -170,7 +170,7 @@ void SshSettingsWidget::setupKeygenPathChooser() setupPathChooser(m_keygenChooser, SshSettings::keygenFilePath(), m_keygenPathChanged); } -void SshSettingsWidget::setupPathChooser(PathChooser &chooser, const FileName &initialPath, +void SshSettingsWidget::setupPathChooser(PathChooser &chooser, const FilePath &initialPath, bool &changedFlag) { chooser.setExpectedKind(PathChooser::ExistingCommand); diff --git a/src/plugins/projectexplorer/editorconfiguration.cpp b/src/plugins/projectexplorer/editorconfiguration.cpp index 43f7b79c69..40e08d368e 100644 --- a/src/plugins/projectexplorer/editorconfiguration.cpp +++ b/src/plugins/projectexplorer/editorconfiguration.cpp @@ -395,7 +395,7 @@ TabSettings actualTabSettings(const QString &fileName, { if (baseTextdocument) return baseTextdocument->tabSettings(); - if (Project *project = SessionManager::projectForFile(Utils::FileName::fromString(fileName))) + if (Project *project = SessionManager::projectForFile(Utils::FilePath::fromString(fileName))) return project->editorConfiguration()->codeStyle()->tabSettings(); return TextEditorSettings::codeStyle()->tabSettings(); } diff --git a/src/plugins/projectexplorer/editorsettingspropertiespage.cpp b/src/plugins/projectexplorer/editorsettingspropertiespage.cpp index 83fc49dcea..ba76cb078e 100644 --- a/src/plugins/projectexplorer/editorsettingspropertiespage.cpp +++ b/src/plugins/projectexplorer/editorsettingspropertiespage.cpp @@ -43,14 +43,14 @@ EditorSettingsWidget::EditorSettingsWidget(Project *project) : QWidget(), m_proj globalSettingsActivated(config->useGlobalSettings() ? 0 : 1); - connect(m_ui.globalSelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), + connect(m_ui.globalSelector, QOverload<int>::of(&QComboBox::activated), this, &EditorSettingsWidget::globalSettingsActivated); connect(m_ui.restoreButton, &QAbstractButton::clicked, this, &EditorSettingsWidget::restoreDefaultValues); connect(m_ui.showWrapColumn, &QAbstractButton::toggled, config, &EditorConfiguration::setShowWrapColumn); - connect(m_ui.wrapColumn, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + connect(m_ui.wrapColumn, QOverload<int>::of(&QSpinBox::valueChanged), config, &EditorConfiguration::setWrapColumn); connect(m_ui.behaviorSettingsWidget, &TextEditor::BehaviorSettingsWidget::typingSettingsChanged, diff --git a/src/plugins/projectexplorer/environmentaspect.cpp b/src/plugins/projectexplorer/environmentaspect.cpp index d0b0845a3e..0b0eff1e37 100644 --- a/src/plugins/projectexplorer/environmentaspect.cpp +++ b/src/plugins/projectexplorer/environmentaspect.cpp @@ -30,6 +30,8 @@ #include <utils/qtcassert.h> +using namespace Utils; + static const char BASE_KEY[] = "PE.EnvironmentAspect.Base"; static const char CHANGES_KEY[] = "PE.EnvironmentAspect.Changes"; @@ -53,9 +55,7 @@ int EnvironmentAspect::baseEnvironmentBase() const void EnvironmentAspect::setBaseEnvironmentBase(int base) { - QTC_ASSERT(base >= 0, return); - QTC_ASSERT(possibleBaseEnvironments().contains(base), return); - + QTC_ASSERT(base >= 0 && base < m_baseEnvironments.size(), return); if (m_base != base) { m_base = base; emit baseEnvironmentChanged(); @@ -64,53 +64,81 @@ void EnvironmentAspect::setBaseEnvironmentBase(int base) void EnvironmentAspect::setUserEnvironmentChanges(const QList<Utils::EnvironmentItem> &diff) { - if (m_changes != diff) { - m_changes = diff; - emit userEnvironmentChangesChanged(m_changes); + if (m_userChanges != diff) { + m_userChanges = diff; + emit userEnvironmentChangesChanged(m_userChanges); emit environmentChanged(); } } Utils::Environment EnvironmentAspect::environment() const { - Utils::Environment env = baseEnvironment(); - env.modify(m_changes); + QTC_ASSERT(m_base >= 0 && m_base < m_baseEnvironments.size(), return Environment()); + Environment env = m_baseEnvironments.at(m_base).unmodifiedBaseEnvironment(); + for (const EnvironmentModifier &modifier : m_modifiers) + modifier(env); + env.modify(m_userChanges); return env; } -QList<int> EnvironmentAspect::possibleBaseEnvironments() const +const QStringList EnvironmentAspect::displayNames() const { - return m_displayNames.keys(); + return Utils::transform(m_baseEnvironments, &BaseEnvironment::displayName); } -QString EnvironmentAspect::baseEnvironmentDisplayName(int base) const +void EnvironmentAspect::addModifier(const EnvironmentAspect::EnvironmentModifier &modifier) { - return m_displayNames[base]; + m_modifiers.append(modifier); } -void EnvironmentAspect::addSupportedBaseEnvironment(int base, const QString &displayName) +void EnvironmentAspect::addSupportedBaseEnvironment(const QString &displayName, + const std::function<Environment()> &getter) { - m_displayNames[base] = displayName; + BaseEnvironment baseEnv; + baseEnv.displayName = displayName; + baseEnv.getter = getter; + m_baseEnvironments.append(baseEnv); if (m_base == -1) - setBaseEnvironmentBase(base); + setBaseEnvironmentBase(m_baseEnvironments.size() - 1); } -void EnvironmentAspect::addPreferredBaseEnvironment(int base, const QString &displayName) +void EnvironmentAspect::addPreferredBaseEnvironment(const QString &displayName, + const std::function<Environment()> &getter) { - m_displayNames[base] = displayName; - setBaseEnvironmentBase(base); + BaseEnvironment baseEnv; + baseEnv.displayName = displayName; + baseEnv.getter = getter; + m_baseEnvironments.append(baseEnv); + setBaseEnvironmentBase(m_baseEnvironments.size() - 1); } void EnvironmentAspect::fromMap(const QVariantMap &map) { m_base = map.value(QLatin1String(BASE_KEY), -1).toInt(); - m_changes = Utils::EnvironmentItem::fromStringList(map.value(QLatin1String(CHANGES_KEY)).toStringList()); + m_userChanges = Utils::EnvironmentItem::fromStringList(map.value(QLatin1String(CHANGES_KEY)).toStringList()); } void EnvironmentAspect::toMap(QVariantMap &data) const { data.insert(QLatin1String(BASE_KEY), m_base); - data.insert(QLatin1String(CHANGES_KEY), Utils::EnvironmentItem::toStringList(m_changes)); + data.insert(QLatin1String(CHANGES_KEY), Utils::EnvironmentItem::toStringList(m_userChanges)); +} + +Environment EnvironmentAspect::currentUnmodifiedBaseEnvironment() const +{ + QTC_ASSERT(m_base >= 0 && m_base < m_baseEnvironments.size(), return Environment()); + return m_baseEnvironments.at(m_base).unmodifiedBaseEnvironment(); +} + +QString EnvironmentAspect::currentDisplayName() const +{ + QTC_ASSERT(m_base >= 0 && m_base < m_baseEnvironments.size(), return {}); + return m_baseEnvironments[m_base].displayName; +} + +Environment EnvironmentAspect::BaseEnvironment::unmodifiedBaseEnvironment() const +{ + return getter ? getter() : Environment(); } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/environmentaspect.h b/src/plugins/projectexplorer/environmentaspect.h index 7d0ecbe406..6657d1dfe8 100644 --- a/src/plugins/projectexplorer/environmentaspect.h +++ b/src/plugins/projectexplorer/environmentaspect.h @@ -41,22 +41,32 @@ class PROJECTEXPLORER_EXPORT EnvironmentAspect : public ProjectConfigurationAspe Q_OBJECT public: - // The environment the user chose as base for his modifications. - virtual Utils::Environment baseEnvironment() const = 0; + EnvironmentAspect(); + // The environment including the user's modifications. Utils::Environment environment() const; - QList<int> possibleBaseEnvironments() const; - QString baseEnvironmentDisplayName(int base) const; - int baseEnvironmentBase() const; void setBaseEnvironmentBase(int base); - QList<Utils::EnvironmentItem> userEnvironmentChanges() const { return m_changes; } + QList<Utils::EnvironmentItem> userEnvironmentChanges() const { return m_userChanges; } void setUserEnvironmentChanges(const QList<Utils::EnvironmentItem> &diff); - void addSupportedBaseEnvironment(int base, const QString &displayName); - void addPreferredBaseEnvironment(int base, const QString &displayName); + void addSupportedBaseEnvironment(const QString &displayName, + const std::function<Utils::Environment()> &getter); + void addPreferredBaseEnvironment(const QString &displayName, + const std::function<Utils::Environment()> &getter); + + // The environment the user chose as base for his modifications. + Utils::Environment currentUnmodifiedBaseEnvironment() const; + QString currentDisplayName() const; + + const QStringList displayNames() const; + + using EnvironmentModifier = std::function<void(Utils::Environment &)>; + void addModifier(const EnvironmentModifier &); + + bool isLocal() const { return m_isLocal; } signals: void baseEnvironmentChanged(); @@ -64,14 +74,25 @@ signals: void environmentChanged(); protected: - EnvironmentAspect(); void fromMap(const QVariantMap &map) override; void toMap(QVariantMap &map) const override; + void setIsLocal(bool local) { m_isLocal = local; } + private: + // One possible choice in the Environment aspect. + struct BaseEnvironment { + Utils::Environment unmodifiedBaseEnvironment() const; + + std::function<Utils::Environment()> getter; + QString displayName; + }; + + QList<Utils::EnvironmentItem> m_userChanges; + QList<EnvironmentModifier> m_modifiers; + QList<BaseEnvironment> m_baseEnvironments; int m_base = -1; - QList<Utils::EnvironmentItem> m_changes; - QMap<int, QString> m_displayNames; + bool m_isLocal = false; }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/environmentaspectwidget.cpp b/src/plugins/projectexplorer/environmentaspectwidget.cpp index 7b5c9e5e5f..0898a3f1b6 100644 --- a/src/plugins/projectexplorer/environmentaspectwidget.cpp +++ b/src/plugins/projectexplorer/environmentaspectwidget.cpp @@ -56,22 +56,15 @@ EnvironmentAspectWidget::EnvironmentAspectWidget(EnvironmentAspect *aspect, QWid baseLayout->setMargin(0); auto label = new QLabel(tr("Base environment for this run configuration:"), this); baseLayout->addWidget(label); + m_baseEnvironmentComboBox = new QComboBox; - QList<int> bases = m_aspect->possibleBaseEnvironments(); - int currentBase = m_aspect->baseEnvironmentBase(); - QString baseDisplayName; - foreach (int i, bases) { - const QString displayName = m_aspect->baseEnvironmentDisplayName(i); - m_baseEnvironmentComboBox->addItem(displayName, i); - if (i == currentBase) { - m_baseEnvironmentComboBox->setCurrentIndex(m_baseEnvironmentComboBox->count() - 1); - baseDisplayName = displayName; - } - } + for (const QString &displayName : m_aspect->displayNames()) + m_baseEnvironmentComboBox->addItem(displayName); if (m_baseEnvironmentComboBox->count() == 1) m_baseEnvironmentComboBox->setEnabled(false); + m_baseEnvironmentComboBox->setCurrentIndex(m_aspect->baseEnvironmentBase()); - connect(m_baseEnvironmentComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(m_baseEnvironmentComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EnvironmentAspectWidget::baseEnvironmentSelected); baseLayout->addWidget(m_baseEnvironmentComboBox); @@ -79,9 +72,11 @@ EnvironmentAspectWidget::EnvironmentAspectWidget(EnvironmentAspect *aspect, QWid if (additionalWidget) baseLayout->addWidget(additionalWidget); - m_environmentWidget = new EnvironmentWidget(this, baseEnvironmentWidget); - m_environmentWidget->setBaseEnvironment(m_aspect->baseEnvironment()); - m_environmentWidget->setBaseEnvironmentText(baseDisplayName); + const EnvironmentWidget::Type widgetType = aspect->isLocal() + ? EnvironmentWidget::TypeLocal : EnvironmentWidget::TypeRemote; + m_environmentWidget = new EnvironmentWidget(this, widgetType, baseEnvironmentWidget); + m_environmentWidget->setBaseEnvironment(m_aspect->currentUnmodifiedBaseEnvironment()); + m_environmentWidget->setBaseEnvironmentText(m_aspect->currentDisplayName()); m_environmentWidget->setUserChanges(m_aspect->userEnvironmentChanges()); m_environmentWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); topLayout->addWidget(m_environmentWidget); @@ -110,10 +105,9 @@ QWidget *EnvironmentAspectWidget::additionalWidget() const void EnvironmentAspectWidget::baseEnvironmentSelected(int idx) { m_ignoreChange = true; - int base = m_baseEnvironmentComboBox->itemData(idx).toInt(); - m_aspect->setBaseEnvironmentBase(base); - m_environmentWidget->setBaseEnvironment(m_aspect->baseEnvironment()); - m_environmentWidget->setBaseEnvironmentText(m_aspect->baseEnvironmentDisplayName(base)); + m_aspect->setBaseEnvironmentBase(idx); + m_environmentWidget->setBaseEnvironment(m_aspect->currentUnmodifiedBaseEnvironment()); + m_environmentWidget->setBaseEnvironmentText(m_aspect->currentDisplayName()); m_ignoreChange = false; } @@ -127,8 +121,8 @@ void EnvironmentAspectWidget::changeBaseEnvironment() if (m_baseEnvironmentComboBox->itemData(i).toInt() == base) m_baseEnvironmentComboBox->setCurrentIndex(i); } - m_environmentWidget->setBaseEnvironmentText(m_aspect->baseEnvironmentDisplayName(base)); - m_environmentWidget->setBaseEnvironment(m_aspect->baseEnvironment()); + m_environmentWidget->setBaseEnvironmentText(m_aspect->currentDisplayName()); + m_environmentWidget->setBaseEnvironment(m_aspect->currentUnmodifiedBaseEnvironment()); } void EnvironmentAspectWidget::userChangesEdited() @@ -149,7 +143,7 @@ void EnvironmentAspectWidget::environmentChanged() { if (m_ignoreChange) return; - m_environmentWidget->setBaseEnvironment(m_aspect->baseEnvironment()); + m_environmentWidget->setBaseEnvironment(m_aspect->currentUnmodifiedBaseEnvironment()); } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/environmentaspectwidget.h b/src/plugins/projectexplorer/environmentaspectwidget.h index b5127d889e..6ca8164f05 100644 --- a/src/plugins/projectexplorer/environmentaspectwidget.h +++ b/src/plugins/projectexplorer/environmentaspectwidget.h @@ -53,6 +53,7 @@ public: explicit EnvironmentAspectWidget(EnvironmentAspect *aspect, QWidget *additionalWidget = nullptr); virtual EnvironmentAspect *aspect() const; + EnvironmentWidget *envWidget() const { return m_environmentWidget; } QWidget *additionalWidget() const; diff --git a/src/plugins/projectexplorer/environmentwidget.cpp b/src/plugins/projectexplorer/environmentwidget.cpp index ad643e78be..1a6e424960 100644 --- a/src/plugins/projectexplorer/environmentwidget.cpp +++ b/src/plugins/projectexplorer/environmentwidget.cpp @@ -33,10 +33,13 @@ #include <utils/environmentmodel.h> #include <utils/environmentdialog.h> #include <utils/headerviewstretcher.h> +#include <utils/hostosinfo.h> #include <utils/itemviews.h> #include <utils/tooltip/tooltip.h> #include <QDir> +#include <QFileDialog> +#include <QFileInfo> #include <QString> #include <QPushButton> #include <QTreeView> @@ -124,6 +127,7 @@ public: Utils::EnvironmentModel *m_model; QString m_baseEnvironmentText; + EnvironmentWidget::OpenTerminalFunc m_openTerminalFunc; Utils::DetailsWidget *m_detailsContainer; QTreeView *m_environmentView; QPushButton *m_editButton; @@ -131,10 +135,12 @@ public: QPushButton *m_resetButton; QPushButton *m_unsetButton; QPushButton *m_batchEditButton; + QPushButton *m_appendPathButton = nullptr; + QPushButton *m_prependPathButton = nullptr; QPushButton *m_terminalButton; }; -EnvironmentWidget::EnvironmentWidget(QWidget *parent, QWidget *additionalDetailsWidget) +EnvironmentWidget::EnvironmentWidget(QWidget *parent, Type type, QWidget *additionalDetailsWidget) : QWidget(parent), d(std::make_unique<EnvironmentWidgetPrivate>()) { d->m_model = new Utils::EnvironmentModel(); @@ -200,6 +206,21 @@ EnvironmentWidget::EnvironmentWidget(QWidget *parent, QWidget *additionalDetails d->m_unsetButton->setText(tr("&Unset")); buttonLayout->addWidget(d->m_unsetButton); + if (type == TypeLocal) { + d->m_appendPathButton = new QPushButton(this); + d->m_appendPathButton->setEnabled(false); + d->m_appendPathButton->setText(tr("Append Path...")); + buttonLayout->addWidget(d->m_appendPathButton); + d->m_prependPathButton = new QPushButton(this); + d->m_prependPathButton->setEnabled(false); + d->m_prependPathButton->setText(tr("Prepend Path...")); + buttonLayout->addWidget(d->m_prependPathButton); + connect(d->m_appendPathButton, &QAbstractButton::clicked, + this, &EnvironmentWidget::appendPathButtonClicked); + connect(d->m_prependPathButton, &QAbstractButton::clicked, + this, &EnvironmentWidget::prependPathButtonClicked); + } + d->m_batchEditButton = new QPushButton(this); d->m_batchEditButton->setText(tr("&Batch Edit...")); buttonLayout->addWidget(d->m_batchEditButton); @@ -207,10 +228,8 @@ EnvironmentWidget::EnvironmentWidget(QWidget *parent, QWidget *additionalDetails d->m_terminalButton = new QPushButton(this); d->m_terminalButton->setText(tr("Open &Terminal")); d->m_terminalButton->setToolTip(tr("Open a terminal with this environment set up.")); + d->m_terminalButton->setEnabled(type == TypeLocal); buttonLayout->addWidget(d->m_terminalButton); -#if defined(Q_OS_UNIX) && QT_VERSION < QT_VERSION_CHECK(5, 10, 0) - d->m_terminalButton->setVisible(false); -#endif buttonLayout->addStretch(); horizontalLayout->addLayout(buttonLayout); @@ -234,7 +253,14 @@ EnvironmentWidget::EnvironmentWidget(QWidget *parent, QWidget *additionalDetails connect(d->m_environmentView->selectionModel(), &QItemSelectionModel::currentChanged, this, &EnvironmentWidget::environmentCurrentIndexChanged); connect(d->m_terminalButton, &QAbstractButton::clicked, - this, &EnvironmentWidget::openTerminal); + this, [this] { + Utils::Environment env = d->m_model->baseEnvironment(); + env.modify(d->m_model->userChanges()); + if (d->m_openTerminalFunc) + d->m_openTerminalFunc(env); + else + Core::FileUtils::openTerminal(QDir::currentPath(), env); + }); connect(d->m_detailsContainer, &Utils::DetailsWidget::linkActivated, this, &EnvironmentWidget::linkActivated); @@ -286,6 +312,12 @@ void EnvironmentWidget::setUserChanges(const QList<Utils::EnvironmentItem> &list updateSummaryText(); } +void EnvironmentWidget::setOpenTerminalFunc(const EnvironmentWidget::OpenTerminalFunc &func) +{ + d->m_openTerminalFunc = func; + d->m_terminalButton->setEnabled(bool(func)); +} + void EnvironmentWidget::updateSummaryText() { QList<Utils::EnvironmentItem> list = d->m_model->userChanges(); @@ -321,6 +353,36 @@ void EnvironmentWidget::linkActivated(const QString &link) focusIndex(idx); } +bool EnvironmentWidget::currentEntryIsPathList(const QModelIndex ¤t) const +{ + if (!current.isValid()) + return false; + + // Look at the name first and check it against some well-known path variables. Extend as needed. + const QString varName = d->m_model->indexToVariable(current); + if (varName.compare("PATH", Utils::HostOsInfo::fileNameCaseSensitivity()) == 0) + return true; + if (Utils::HostOsInfo::isMacHost() && varName == "DYLD_LIBRARY_PATH") + return true; + if (Utils::HostOsInfo::isAnyUnixHost() && varName == "LD_LIBRARY_PATH") + return true; + + // Now check the value: If it's a list of strings separated by the platform's path separator + // and at least one of the strings is an existing directory, then that's enough proof for us. + QModelIndex valueIndex = current; + if (valueIndex.column() == 0) + valueIndex = valueIndex.siblingAtColumn(1); + const QStringList entries = d->m_model->data(valueIndex).toString() + .split(Utils::HostOsInfo::pathListSeparator(), QString::SkipEmptyParts); + if (entries.length() < 2) + return false; + for (const QString &potentialDir : entries) { + if (QFileInfo(potentialDir).isDir()) + return true; + } + return false; +} + void EnvironmentWidget::updateButtons() { environmentCurrentIndexChanged(d->m_environmentView->currentIndex()); @@ -355,6 +417,42 @@ void EnvironmentWidget::unsetEnvironmentButtonClicked() d->m_model->unsetVariable(name); } +void EnvironmentWidget::amendPathList(const PathListModifier &modifier) +{ + const QString varName = d->m_model->indexToVariable(d->m_environmentView->currentIndex()); + const QString dir = QDir::toNativeSeparators( + QFileDialog::getExistingDirectory(this, tr("Choose Directory"))); + if (dir.isEmpty()) + return; + QModelIndex index = d->m_model->variableToIndex(varName); + if (!index.isValid()) + return; + if (index.column() == 0) + index = index.siblingAtColumn(1); + const QString value = d->m_model->data(index).toString(); + d->m_model->setData(index, modifier(value, dir)); +} + +void EnvironmentWidget::appendPathButtonClicked() +{ + amendPathList([](const QString &pathList, const QString &dir) { + QString newPathList = dir; + if (!pathList.isEmpty()) + newPathList.prepend(Utils::HostOsInfo::pathListSeparator()).prepend(pathList); + return newPathList; + }); +} + +void EnvironmentWidget::prependPathButtonClicked() +{ + amendPathList([](const QString &pathList, const QString &dir) { + QString newPathList = dir; + if (!pathList.isEmpty()) + newPathList.append(Utils::HostOsInfo::pathListSeparator()).append(pathList); + return newPathList; + }); +} + void EnvironmentWidget::batchEditEnvironmentButtonClicked() { const QList<Utils::EnvironmentItem> changes = d->m_model->userChanges(); @@ -367,13 +465,6 @@ void EnvironmentWidget::batchEditEnvironmentButtonClicked() d->m_model->setUserChanges(newChanges); } -void EnvironmentWidget::openTerminal() -{ - Utils::Environment env = d->m_model->baseEnvironment(); - env.modify(d->m_model->userChanges()); - Core::FileUtils::openTerminal(QDir::currentPath(), env); -} - void EnvironmentWidget::environmentCurrentIndexChanged(const QModelIndex ¤t) { if (current.isValid()) { @@ -388,6 +479,10 @@ void EnvironmentWidget::environmentCurrentIndexChanged(const QModelIndex ¤ d->m_resetButton->setEnabled(false); d->m_unsetButton->setEnabled(false); } + if (d->m_appendPathButton) { + d->m_appendPathButton->setEnabled(currentEntryIsPathList(current)); + d->m_prependPathButton->setEnabled(currentEntryIsPathList(current)); + } } void EnvironmentWidget::invalidateCurrentIndex() diff --git a/src/plugins/projectexplorer/environmentwidget.h b/src/plugins/projectexplorer/environmentwidget.h index 3eb85bcc8d..927dc9b0c1 100644 --- a/src/plugins/projectexplorer/environmentwidget.h +++ b/src/plugins/projectexplorer/environmentwidget.h @@ -29,6 +29,7 @@ #include <QWidget> +#include <functional> #include <memory> QT_FORWARD_DECLARE_CLASS(QModelIndex) @@ -47,7 +48,9 @@ class PROJECTEXPLORER_EXPORT EnvironmentWidget : public QWidget Q_OBJECT public: - explicit EnvironmentWidget(QWidget *parent, QWidget *additionalDetailsWidget = nullptr); + enum Type { TypeLocal, TypeRemote }; + explicit EnvironmentWidget(QWidget *parent, Type type, + QWidget *additionalDetailsWidget = nullptr); ~EnvironmentWidget() override; void setBaseEnvironmentText(const QString &text); @@ -56,6 +59,9 @@ public: QList<Utils::EnvironmentItem> userChanges() const; void setUserChanges(const QList<Utils::EnvironmentItem> &list); + using OpenTerminalFunc = std::function<void(const Utils::Environment &env)>; + void setOpenTerminalFunc(const OpenTerminalFunc &func); + signals: void userChangesChanged(); void detailsVisibleChanged(bool visible); @@ -65,14 +71,19 @@ private: void addEnvironmentButtonClicked(); void removeEnvironmentButtonClicked(); void unsetEnvironmentButtonClicked(); + void appendPathButtonClicked(); + void prependPathButtonClicked(); void batchEditEnvironmentButtonClicked(); - void openTerminal(); void environmentCurrentIndexChanged(const QModelIndex ¤t); void invalidateCurrentIndex(); void updateSummaryText(); void focusIndex(const QModelIndex &index); void updateButtons(); void linkActivated(const QString &link); + bool currentEntryIsPathList(const QModelIndex ¤t) const; + + using PathListModifier = std::function<QString(const QString &oldList, const QString &newDir)>; + void amendPathList(const PathListModifier &modifier); const std::unique_ptr<EnvironmentWidgetPrivate> d; }; diff --git a/src/plugins/projectexplorer/extraabi.cpp b/src/plugins/projectexplorer/extraabi.cpp index a1da2193fd..434994125c 100644 --- a/src/plugins/projectexplorer/extraabi.cpp +++ b/src/plugins/projectexplorer/extraabi.cpp @@ -65,7 +65,7 @@ AbiFlavorAccessor::AbiFlavorAccessor() : QCoreApplication::translate("ProjectExplorer::ToolChainManager", "ABI"), Core::Constants::IDE_DISPLAY_NAME) { - setBaseFilePath(FileName::fromString(Core::ICore::installerResourcePath() + "/abi.xml")); + setBaseFilePath(FilePath::fromString(Core::ICore::installerResourcePath() + "/abi.xml")); addVersionUpgrader(std::make_unique<AbiFlavorUpgraderV0>()); } diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp index df7297c33d..adcc8f180e 100644 --- a/src/plugins/projectexplorer/extracompiler.cpp +++ b/src/plugins/projectexplorer/extracompiler.cpp @@ -57,9 +57,9 @@ class ExtraCompilerPrivate { public: const Project *project; - Utils::FileName source; + Utils::FilePath source; FileNameToContentsHash contents; - QList<Task> issues; + Tasks issues; QDateTime compileTime; Core::IEditor *lastEditor = nullptr; QMetaObject::Connection activeBuildConfigConnection; @@ -70,13 +70,13 @@ public: void updateIssues(); }; -ExtraCompiler::ExtraCompiler(const Project *project, const Utils::FileName &source, - const Utils::FileNameList &targets, QObject *parent) : +ExtraCompiler::ExtraCompiler(const Project *project, const Utils::FilePath &source, + const Utils::FilePathList &targets, QObject *parent) : QObject(parent), d(std::make_unique<ExtraCompilerPrivate>()) { d->project = project; d->source = source; - foreach (const Utils::FileName &target, targets) + foreach (const Utils::FilePath &target, targets) d->contents.insert(target, QByteArray()); d->timer.setSingleShot(true); @@ -104,7 +104,7 @@ ExtraCompiler::ExtraCompiler(const Project *project, const Utils::FileName &sour // Use existing target files, where possible. Otherwise run the compiler. QDateTime sourceTime = d->source.toFileInfo().lastModified(); - foreach (const Utils::FileName &target, targets) { + foreach (const Utils::FilePath &target, targets) { QFileInfo targetFileInfo(target.toFileInfo()); if (!targetFileInfo.exists()) { d->dirty = true; @@ -136,22 +136,22 @@ const Project *ExtraCompiler::project() const return d->project; } -Utils::FileName ExtraCompiler::source() const +Utils::FilePath ExtraCompiler::source() const { return d->source; } -QByteArray ExtraCompiler::content(const Utils::FileName &file) const +QByteArray ExtraCompiler::content(const Utils::FilePath &file) const { return d->contents.value(file); } -Utils::FileNameList ExtraCompiler::targets() const +Utils::FilePathList ExtraCompiler::targets() const { return d->contents.keys(); } -void ExtraCompiler::forEachTarget(std::function<void (const Utils::FileName &)> func) +void ExtraCompiler::forEachTarget(std::function<void (const Utils::FilePath &)> func) { for (auto it = d->contents.constBegin(), end = d->contents.constEnd(); it != end; ++it) func(it.key()); @@ -183,7 +183,7 @@ void ExtraCompiler::onTargetsBuilt(Project *project) if (d->compileTime.isValid() && d->compileTime >= sourceTime) return; - forEachTarget([&](const Utils::FileName &target) { + forEachTarget([&](const Utils::FilePath &target) { QFileInfo fi(target.toFileInfo()); QDateTime generateTime = fi.exists() ? fi.lastModified() : QDateTime(); if (generateTime.isValid() && (generateTime > sourceTime)) { @@ -255,7 +255,7 @@ Utils::Environment ExtraCompiler::buildEnvironment() const return bc->environment(); } else { QList<Utils::EnvironmentItem> changes = - EnvironmentKitInformation::environmentChanges(target->kit()); + EnvironmentKitAspect::environmentChanges(target->kit()); Utils::Environment env = Utils::Environment::systemEnvironment(); env.modify(changes); return env; @@ -265,7 +265,7 @@ Utils::Environment ExtraCompiler::buildEnvironment() const return Utils::Environment::systemEnvironment(); } -void ExtraCompiler::setCompileIssues(const QList<Task> &issues) +void ExtraCompiler::setCompileIssues(const Tasks &issues) { d->issues = issues; d->updateIssues(); @@ -299,7 +299,7 @@ void ExtraCompilerPrivate::updateIssues() widget->setExtraSelections(TextEditor::TextEditorWidget::CodeWarningsSelection, selections); } -void ExtraCompiler::setContent(const Utils::FileName &file, const QByteArray &contents) +void ExtraCompiler::setContent(const Utils::FilePath &file, const QByteArray &contents) { auto it = d->contents.find(file); if (it != d->contents.end()) { @@ -322,8 +322,8 @@ ExtraCompilerFactory::~ExtraCompilerFactory() } void ExtraCompilerFactory::annouceCreation(const Project *project, - const Utils::FileName &source, - const Utils::FileNameList &targets) + const Utils::FilePath &source, + const Utils::FilePathList &targets) { for (ExtraCompilerFactoryObserver *observer : *observers) observer->newExtraCompiler(project, source, targets); @@ -334,8 +334,8 @@ QList<ExtraCompilerFactory *> ExtraCompilerFactory::extraCompilerFactories() return *factories(); } -ProcessExtraCompiler::ProcessExtraCompiler(const Project *project, const Utils::FileName &source, - const Utils::FileNameList &targets, QObject *parent) : +ProcessExtraCompiler::ProcessExtraCompiler(const Project *project, const Utils::FilePath &source, + const Utils::FilePathList &targets, QObject *parent) : ExtraCompiler(project, source, targets, parent) { } @@ -353,7 +353,7 @@ void ProcessExtraCompiler::run(const QByteArray &sourceContents) runImpl(contents); } -void ProcessExtraCompiler::run(const Utils::FileName &fileName) +void ProcessExtraCompiler::run(const Utils::FilePath &fileName) { ContentProvider contents = [fileName]() { QFile file(fileName.toString()); @@ -364,9 +364,9 @@ void ProcessExtraCompiler::run(const Utils::FileName &fileName) runImpl(contents); } -Utils::FileName ProcessExtraCompiler::workingDirectory() const +Utils::FilePath ProcessExtraCompiler::workingDirectory() const { - return Utils::FileName(); + return Utils::FilePath(); } QStringList ProcessExtraCompiler::arguments() const @@ -380,10 +380,10 @@ bool ProcessExtraCompiler::prepareToRun(const QByteArray &sourceContents) return true; } -QList<Task> ProcessExtraCompiler::parseIssues(const QByteArray &stdErr) +Tasks ProcessExtraCompiler::parseIssues(const QByteArray &stdErr) { Q_UNUSED(stdErr); - return QList<Task>(); + return {}; } void ProcessExtraCompiler::runImpl(const ContentProvider &provider) @@ -403,7 +403,7 @@ void ProcessExtraCompiler::runImpl(const ContentProvider &provider) void ProcessExtraCompiler::runInThread( QFutureInterface<FileNameToContentsHash> &futureInterface, - const Utils::FileName &cmd, const Utils::FileName &workDir, + const Utils::FilePath &cmd, const Utils::FilePath &workDir, const QStringList &args, const ContentProvider &provider, const Utils::Environment &env) { diff --git a/src/plugins/projectexplorer/extracompiler.h b/src/plugins/projectexplorer/extracompiler.h index 006555ffc6..ba9c6774f7 100644 --- a/src/plugins/projectexplorer/extracompiler.h +++ b/src/plugins/projectexplorer/extracompiler.h @@ -47,27 +47,27 @@ QT_FORWARD_DECLARE_CLASS(QThreadPool); namespace ProjectExplorer { class ExtraCompilerPrivate; -using FileNameToContentsHash = QHash<Utils::FileName, QByteArray>; +using FileNameToContentsHash = QHash<Utils::FilePath, QByteArray>; class PROJECTEXPLORER_EXPORT ExtraCompiler : public QObject { Q_OBJECT public: - ExtraCompiler(const Project *project, const Utils::FileName &source, - const Utils::FileNameList &targets, QObject *parent = nullptr); + ExtraCompiler(const Project *project, const Utils::FilePath &source, + const Utils::FilePathList &targets, QObject *parent = nullptr); ~ExtraCompiler() override; const Project *project() const; - Utils::FileName source() const; + Utils::FilePath source() const; // You can set the contents from the outside. This is done if the file has been (re)created by // the regular build process. - void setContent(const Utils::FileName &file, const QByteArray &content); - QByteArray content(const Utils::FileName &file) const; + void setContent(const Utils::FilePath &file, const QByteArray &content); + QByteArray content(const Utils::FilePath &file) const; - Utils::FileNameList targets() const; - void forEachTarget(std::function<void(const Utils::FileName &)> func); + Utils::FilePathList targets() const; + void forEachTarget(std::function<void(const Utils::FilePath &)> func); void setCompileTime(const QDateTime &time); QDateTime compileTime() const; @@ -75,11 +75,11 @@ public: static QThreadPool *extraCompilerThreadPool(); signals: - void contentsChanged(const Utils::FileName &file); + void contentsChanged(const Utils::FilePath &file); protected: Utils::Environment buildEnvironment() const; - void setCompileIssues(const QList<Task> &issues); + void setCompileIssues(const Tasks &issues); private: void onTargetsBuilt(Project *project); @@ -88,7 +88,7 @@ private: void setDirty(); // This method may not block! virtual void run(const QByteArray &sourceContent) = 0; - virtual void run(const Utils::FileName &file) = 0; + virtual void run(const Utils::FilePath &file) = 0; const std::unique_ptr<ExtraCompilerPrivate> d; }; @@ -98,8 +98,8 @@ class PROJECTEXPLORER_EXPORT ProcessExtraCompiler : public ExtraCompiler Q_OBJECT public: - ProcessExtraCompiler(const Project *project, const Utils::FileName &source, - const Utils::FileNameList &targets, QObject *parent = nullptr); + ProcessExtraCompiler(const Project *project, const Utils::FilePath &source, + const Utils::FilePathList &targets, QObject *parent = nullptr); ~ProcessExtraCompiler() override; protected: @@ -109,11 +109,11 @@ protected: // * prepareToRun returns true // * The process is not yet running void run(const QByteArray &sourceContents) override; - void run(const Utils::FileName &fileName) override; + void run(const Utils::FilePath &fileName) override; // Information about the process to run: - virtual Utils::FileName workingDirectory() const; - virtual Utils::FileName command() const = 0; + virtual Utils::FilePath workingDirectory() const; + virtual Utils::FilePath command() const = 0; virtual QStringList arguments() const; virtual bool prepareToRun(const QByteArray &sourceContents); @@ -123,13 +123,13 @@ protected: { Q_UNUSED(process); Q_UNUSED(sourceContents); } virtual FileNameToContentsHash handleProcessFinished(QProcess *process) = 0; - virtual QList<Task> parseIssues(const QByteArray &stdErr); + virtual Tasks parseIssues(const QByteArray &stdErr); private: using ContentProvider = std::function<QByteArray()>; void runImpl(const ContentProvider &sourceContents); void runInThread(QFutureInterface<FileNameToContentsHash> &futureInterface, - const Utils::FileName &cmd, const Utils::FileName &workDir, + const Utils::FilePath &cmd, const Utils::FilePath &workDir, const QStringList &args, const ContentProvider &provider, const Utils::Environment &env); void cleanUp(); @@ -146,8 +146,8 @@ protected: ~ExtraCompilerFactoryObserver(); virtual void newExtraCompiler(const Project *project, - const Utils::FileName &source, - const Utils::FileNameList &targets) + const Utils::FilePath &source, + const Utils::FilePathList &targets) = 0; }; @@ -162,13 +162,13 @@ public: virtual QString sourceTag() const = 0; virtual ExtraCompiler *create(const Project *project, - const Utils::FileName &source, - const Utils::FileNameList &targets) + const Utils::FilePath &source, + const Utils::FilePathList &targets) = 0; void annouceCreation(const Project *project, - const Utils::FileName &source, - const Utils::FileNameList &targets); + const Utils::FilePath &source, + const Utils::FilePathList &targets); static QList<ExtraCompilerFactory *> extraCompilerFactories(); }; diff --git a/src/plugins/projectexplorer/fileinsessionfinder.cpp b/src/plugins/projectexplorer/fileinsessionfinder.cpp new file mode 100644 index 0000000000..e4c62e405f --- /dev/null +++ b/src/plugins/projectexplorer/fileinsessionfinder.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "fileinsessionfinder.h" + +#include "project.h" +#include "session.h" + +#include <utils/fileinprojectfinder.h> + +#include <QUrl> + +using namespace Utils; + +namespace ProjectExplorer { +namespace Internal { + +class FileInSessionFinder : public QObject +{ +public: + FileInSessionFinder(); + + FilePathList doFindFile(const FilePath &filePath); + void invalidateFinder() { m_finderIsUpToDate = false; } + +private: + FileInProjectFinder m_finder; + bool m_finderIsUpToDate = false; +}; + +FileInSessionFinder::FileInSessionFinder() +{ + connect(SessionManager::instance(), &SessionManager::projectAdded, + this, [this](const Project *p) { + invalidateFinder(); + connect(p, &Project::fileListChanged, this, &FileInSessionFinder::invalidateFinder); + }); + connect(SessionManager::instance(), &SessionManager::projectRemoved, + this, [this](const Project *p) { + invalidateFinder(); + p->disconnect(this); + }); +} + +FilePathList FileInSessionFinder::doFindFile(const FilePath &filePath) +{ + if (!m_finderIsUpToDate) { + m_finder.setProjectDirectory(SessionManager::startupProject() + ? SessionManager::startupProject()->projectDirectory() + : FilePath()); + FilePathList allFiles; + for (const Project * const p : SessionManager::projects()) + allFiles << p->files(Project::AllFiles); + m_finder.setProjectFiles(allFiles); + m_finderIsUpToDate = true; + } + return m_finder.findFile(QUrl::fromLocalFile(filePath.toString())); +} + +FilePathList findFileInSession(const FilePath &filePath) +{ + static FileInSessionFinder finder; + return finder.doFindFile(filePath); +} + +} // namespace Internal +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/fileinsessionfinder.h b/src/plugins/projectexplorer/fileinsessionfinder.h new file mode 100644 index 0000000000..f14b3708f3 --- /dev/null +++ b/src/plugins/projectexplorer/fileinsessionfinder.h @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <utils/fileutils.h> + +namespace ProjectExplorer { +namespace Internal { + +Utils::FilePathList findFileInSession(const Utils::FilePath &filePath); + +} // namespace Internal +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/filterkitaspectsdialog.cpp b/src/plugins/projectexplorer/filterkitaspectsdialog.cpp new file mode 100644 index 0000000000..418ae03564 --- /dev/null +++ b/src/plugins/projectexplorer/filterkitaspectsdialog.cpp @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "filterkitaspectsdialog.h" + +#include "kitmanager.h" + +#include <utils/itemviews.h> +#include <utils/qtcassert.h> +#include <utils/treemodel.h> + +#include <QDialogButtonBox> +#include <QHeaderView> +#include <QString> +#include <QVBoxLayout> + +using namespace Utils; + +namespace ProjectExplorer { +namespace Internal { + +class FilterTreeItem : public TreeItem +{ +public: + FilterTreeItem(const KitAspect *aspect, bool enabled) : m_aspect(aspect), m_enabled(enabled) + { } + + QString displayName() const { return m_aspect->displayName(); } + Core::Id id() const { return m_aspect->id(); } + bool enabled() const { return m_enabled; } + +private: + QVariant data(int column, int role) const override + { + QTC_ASSERT(column < 2, return QVariant()); + if (column == 0 && role == Qt::DisplayRole) + return displayName(); + if (column == 1 && role == Qt::CheckStateRole) + return m_enabled ? Qt::Checked : Qt::Unchecked; + return QVariant(); + } + + bool setData(int column, const QVariant &data, int role) override + { + QTC_ASSERT(column == 1 && !m_aspect->isEssential(), return false); + if (role == Qt::CheckStateRole) { + m_enabled = data.toInt() == Qt::Checked; + return true; + } + return false; + } + + Qt::ItemFlags flags(int column) const override + { + QTC_ASSERT(column < 2, return Qt::ItemFlags()); + Qt::ItemFlags flags = Qt::ItemIsSelectable; + if (column == 0 || !m_aspect->isEssential()) + flags |= Qt::ItemIsEnabled; + if (column == 1 && !m_aspect->isEssential()) + flags |= Qt::ItemIsUserCheckable; + return flags; + } + + const KitAspect * const m_aspect; + bool m_enabled; +}; + +class FilterKitAspectsModel : public TreeModel<TreeItem, FilterTreeItem> +{ +public: + FilterKitAspectsModel(const Kit *kit, QObject *parent) : TreeModel(parent) + { + setHeader({tr("Setting"), tr("Visible")}); + for (const KitAspect * const aspect : KitManager::kitAspects()) { + if (kit && !aspect->isApplicableToKit(kit)) + continue; + const QSet<Core::Id> irrelevantAspects = kit ? kit->irrelevantAspects() + : KitManager::irrelevantAspects(); + auto * const item = new FilterTreeItem(aspect, + !irrelevantAspects.contains(aspect->id())); + rootItem()->appendChild(item); + } + static const auto cmp = [](const TreeItem *item1, const TreeItem *item2) { + return static_cast<const FilterTreeItem *>(item1)->displayName() + < static_cast<const FilterTreeItem *>(item2)->displayName(); + }; + rootItem()->sortChildren(cmp); + } + + QSet<Core::Id> disabledItems() const + { + QSet<Core::Id> ids; + for (int i = 0; i < rootItem()->childCount(); ++i) { + const FilterTreeItem * const item + = static_cast<FilterTreeItem *>(rootItem()->childAt(i)); + if (!item->enabled()) + ids << item->id(); + } + return ids; + } +}; + +class FilterTreeView : public TreeView +{ +public: + FilterTreeView(QWidget *parent) : TreeView(parent) + { + setUniformRowHeights(true); + } + +private: + QSize sizeHint() const override + { + const int width = columnWidth(0) + columnWidth(1); + const int height = model()->rowCount() * rowHeight(model()->index(0, 0)) + + header()->sizeHint().height(); + return {width, height}; + } +}; + +FilterKitAspectsDialog::FilterKitAspectsDialog(const Kit *kit, QWidget *parent) + : QDialog(parent), m_model(new FilterKitAspectsModel(kit, this)) +{ + auto * const layout = new QVBoxLayout(this); + auto * const view = new FilterTreeView(this); + view->setModel(m_model); + view->resizeColumnToContents(0); + layout->addWidget(view); + auto * const buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + layout->addWidget(buttonBox); + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +QSet<Core::Id> FilterKitAspectsDialog::irrelevantAspects() const +{ + return static_cast<FilterKitAspectsModel *>(m_model)->disabledItems(); +} + +} // namespace Internal +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitconfigwidget.h b/src/plugins/projectexplorer/filterkitaspectsdialog.h index 05c7ea058b..441abd658d 100644 --- a/src/plugins/projectexplorer/kitconfigwidget.h +++ b/src/plugins/projectexplorer/filterkitaspectsdialog.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -25,57 +25,25 @@ #pragma once -#include "projectexplorer_export.h" - #include <coreplugin/id.h> -#include <QWidget> +#include <QDialog> -namespace ProjectExplorer { +namespace Utils { class BaseTreeModel; } +namespace ProjectExplorer { class Kit; -class KitInformation; - -// -------------------------------------------------------------------------- -// KitConfigWidget -// -------------------------------------------------------------------------- +namespace Internal { -class PROJECTEXPLORER_EXPORT KitConfigWidget : public QObject +class FilterKitAspectsDialog : public QDialog { - Q_OBJECT - public: - KitConfigWidget(Kit *kit, const KitInformation *ki); - - Core::Id kitInformationId() const; - - virtual QString displayName() const = 0; - virtual QString toolTip() const { return QString(); } - virtual void makeReadOnly() = 0; - virtual void refresh() = 0; - virtual bool visibleInKit() { return true; } - - virtual QWidget *mainWidget() const = 0; - virtual QWidget *buttonWidget() const { return nullptr; } - - bool isSticky() const { return m_isSticky; } - bool isMutable() const; - void setMutable(bool b); - - static QString msgManage(); - - Kit *kit() const { return m_kit; } - - virtual void setPalette(const QPalette &p); - virtual void setStyle(QStyle *s); - -signals: - void dirty(); + FilterKitAspectsDialog(const Kit *kit, QWidget *parent); + QSet<Core::Id> irrelevantAspects() const; -protected: - Kit *m_kit; - const KitInformation *m_kitInformation; - bool m_isSticky; +private: + Utils::BaseTreeModel * const m_model; }; +} // namespace Internal } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/foldernavigationwidget.cpp b/src/plugins/projectexplorer/foldernavigationwidget.cpp index 04948cb219..6ff9c9719a 100644 --- a/src/plugins/projectexplorer/foldernavigationwidget.cpp +++ b/src/plugins/projectexplorer/foldernavigationwidget.cpp @@ -168,7 +168,7 @@ bool FolderSortProxyModel::lessThan(const QModelIndex &source_left, const QModel } const QString leftName = src->data(source_left, QFileSystemModel::FileNameRole).toString(); const QString rightName = src->data(source_right, QFileSystemModel::FileNameRole).toString(); - return Utils::FileName::fromString(leftName) < Utils::FileName::fromString(rightName); + return Utils::FilePath::fromString(leftName) < Utils::FilePath::fromString(rightName); } FolderNavigationModel::FolderNavigationModel(QObject *parent) : QFileSystemModel(parent) @@ -196,12 +196,13 @@ Qt::ItemFlags FolderNavigationModel::flags(const QModelIndex &index) const return QFileSystemModel::flags(index); } -static QVector<FolderNode *> renamableFolderNodes(const Utils::FileName &before, - const Utils::FileName &after) +static QVector<FolderNode *> renamableFolderNodes(const Utils::FilePath &before, + const Utils::FilePath &after) { QVector<FolderNode *> folderNodes; ProjectTree::forEachNode([&](Node *node) { - if (node->nodeType() == NodeType::File && node->filePath() == before + if (node->asFileNode() + && node->filePath() == before && node->parentFolderNode() && node->parentFolderNode()->canRenameFile(before.toString(), after.toString())) { folderNodes.append(node->parentFolderNode()); @@ -235,8 +236,8 @@ bool FolderNavigationModel::setData(const QModelIndex &index, const QVariant &va if (success && fileInfo(index).isFile()) { Core::DocumentManager::renamedFile(beforeFilePath, afterFilePath); const QVector<FolderNode *> folderNodes - = renamableFolderNodes(Utils::FileName::fromString(beforeFilePath), - Utils::FileName::fromString(afterFilePath)); + = renamableFolderNodes(Utils::FilePath::fromString(beforeFilePath), + Utils::FilePath::fromString(afterFilePath)); QVector<FolderNode *> failedNodes; for (FolderNode *folder : folderNodes) { if (!folder->renameFile(beforeFilePath, afterFilePath)) @@ -382,7 +383,7 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent this, [this](const QModelIndex &index) { const QModelIndex sourceIndex = m_sortProxyModel->mapToSource(index); - const auto filePath = Utils::FileName::fromString( + const auto filePath = Utils::FilePath::fromString( m_fileSystemModel->filePath(sourceIndex)); // QTimer::singleShot only posts directly onto the event loop if you use the SLOT("...") // notation, so using a singleShot with a lambda would flicker @@ -390,9 +391,9 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent QMetaObject::invokeMethod(this, "setCrumblePath", Qt::QueuedConnection, - Q_ARG(Utils::FileName, filePath)); + Q_ARG(Utils::FilePath, filePath)); }); - connect(m_crumbLabel, &Utils::FileCrumbLabel::pathClicked, [this](const Utils::FileName &path) { + connect(m_crumbLabel, &Utils::FileCrumbLabel::pathClicked, [this](const Utils::FilePath &path) { const QModelIndex rootIndex = m_sortProxyModel->mapToSource(m_listView->rootIndex()); const QModelIndex fileIndex = m_fileSystemModel->index(path.toString()); if (!isChildOf(fileIndex, rootIndex)) @@ -418,10 +419,10 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent connect(m_toggleRootSync, &QAbstractButton::clicked, this, [this]() { setRootAutoSynchronization(!m_rootAutoSync); }); connect(m_rootSelector, - static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) { - const auto directory = m_rootSelector->itemData(index).value<Utils::FileName>(); + const auto directory = m_rootSelector->itemData(index).value<Utils::FilePath>(); m_rootSelector->setToolTip(directory.toString()); setRootDirectory(directory); const QModelIndex rootIndex = m_sortProxyModel->mapToSource(m_listView->rootIndex()); @@ -481,7 +482,7 @@ void FolderNavigationWidget::insertRootDirectory( m_rootSelector->insertItem(index, directory.displayName); if (index <= previousIndex) // item was inserted, update previousIndex ++previousIndex; - m_rootSelector->setItemData(index, qVariantFromValue(directory.path), PATH_ROLE); + m_rootSelector->setItemData(index, QVariant::fromValue(directory.path), PATH_ROLE); m_rootSelector->setItemData(index, directory.id, ID_ROLE); m_rootSelector->setItemData(index, directory.sortValue, SORT_ROLE); m_rootSelector->setItemData(index, directory.path.toUserOutput(), Qt::ToolTipRole); @@ -511,8 +512,8 @@ void FolderNavigationWidget::addNewItem() const QModelIndex current = m_sortProxyModel->mapToSource(m_listView->currentIndex()); if (!current.isValid()) return; - const auto filePath = Utils::FileName::fromString(m_fileSystemModel->filePath(current)); - const Utils::FileName path = filePath.toFileInfo().isDir() ? filePath : filePath.parentDir(); + const auto filePath = Utils::FilePath::fromString(m_fileSystemModel->filePath(current)); + const Utils::FilePath path = filePath.toFileInfo().isDir() ? filePath : filePath.parentDir(); Core::ICore::showNewItemDialog(ProjectExplorerPlugin::tr("New File", "Title of dialog"), Utils::filtered(Core::IWizardFactory::allWizardFactories(), Utils::equal(&Core::IWizardFactory::kind, @@ -527,11 +528,12 @@ void FolderNavigationWidget::editCurrentItem() m_listView->edit(current); } -static QVector<FolderNode *> removableFolderNodes(const Utils::FileName &filePath) +static QVector<FolderNode *> removableFolderNodes(const Utils::FilePath &filePath) { QVector<FolderNode *> folderNodes; ProjectTree::forEachNode([&](Node *node) { - if (node->nodeType() == NodeType::File && node->filePath() == filePath + if (node->asFileNode() + && node->filePath() == filePath && node->parentFolderNode() && node->parentFolderNode()->supportsAction(RemoveFile, node)) { folderNodes.append(node->parentFolderNode()); @@ -550,7 +552,7 @@ void FolderNavigationWidget::removeCurrentItem() dialog.setDeleteFileVisible(false); if (dialog.exec() == QDialog::Accepted) { const QVector<FolderNode *> folderNodes = removableFolderNodes( - Utils::FileName::fromString(filePath)); + Utils::FilePath::fromString(filePath)); const QVector<FolderNode *> failedNodes = Utils::filtered(folderNodes, [filePath](FolderNode *folder) { return !folder->removeFiles( @@ -604,19 +606,19 @@ void FolderNavigationWidget::handleCurrentEditorChanged(Core::IEditor *editor) if (!m_autoSync || !editor || editor->document()->filePath().isEmpty() || editor->document()->isTemporary()) return; - const Utils::FileName filePath = editor->document()->filePath(); + const Utils::FilePath filePath = editor->document()->filePath(); if (m_rootAutoSync) selectBestRootForFile(filePath); selectFile(filePath); } -void FolderNavigationWidget::selectBestRootForFile(const Utils::FileName &filePath) +void FolderNavigationWidget::selectBestRootForFile(const Utils::FilePath &filePath) { const int bestRootIndex = bestRootForFile(filePath); m_rootSelector->setCurrentIndex(bestRootIndex); } -void FolderNavigationWidget::selectFile(const Utils::FileName &filePath) +void FolderNavigationWidget::selectFile(const Utils::FilePath &filePath) { const QModelIndex fileIndex = m_sortProxyModel->mapFromSource( m_fileSystemModel->index(filePath.toString())); @@ -640,22 +642,22 @@ void FolderNavigationWidget::selectFile(const Utils::FileName &filePath) } } -void FolderNavigationWidget::setRootDirectory(const Utils::FileName &directory) +void FolderNavigationWidget::setRootDirectory(const Utils::FilePath &directory) { const QModelIndex index = m_sortProxyModel->mapFromSource( m_fileSystemModel->setRootPath(directory.toString())); m_listView->setRootIndex(index); } -int FolderNavigationWidget::bestRootForFile(const Utils::FileName &filePath) +int FolderNavigationWidget::bestRootForFile(const Utils::FilePath &filePath) { int index = 0; // Computer is default int commonLength = 0; for (int i = 1; i < m_rootSelector->count(); ++i) { - const auto root = m_rootSelector->itemData(i).value<Utils::FileName>(); - if (filePath.isChildOf(root) && root.length() > commonLength) { + const auto root = m_rootSelector->itemData(i).value<Utils::FilePath>(); + if (filePath.isChildOf(root) && root.toString().size() > commonLength) { index = i; - commonLength = root.length(); + commonLength = root.toString().size(); } } return index; @@ -695,12 +697,12 @@ void FolderNavigationWidget::createNewFolder(const QModelIndex &parent) static const QString baseName = tr("New Folder"); // find non-existing name const QDir dir(m_fileSystemModel->filePath(parent)); - const QSet<Utils::FileName> existingItems + const QSet<Utils::FilePath> existingItems = Utils::transform<QSet>(dir.entryList({baseName + '*'}, QDir::AllEntries), [](const QString &entry) { - return Utils::FileName::fromString(entry); + return Utils::FilePath::fromString(entry); }); - const Utils::FileName name = Utils::makeUniquelyNumbered(Utils::FileName::fromString(baseName), + const Utils::FilePath name = Utils::makeUniquelyNumbered(Utils::FilePath::fromString(baseName), existingItems); // create directory and edit const QModelIndex index = m_sortProxyModel->mapFromSource( @@ -711,7 +713,7 @@ void FolderNavigationWidget::createNewFolder(const QModelIndex &parent) m_listView->edit(index); } -void FolderNavigationWidget::setCrumblePath(const Utils::FileName &filePath) +void FolderNavigationWidget::setCrumblePath(const Utils::FilePath &filePath) { const QModelIndex index = m_fileSystemModel->index(filePath.toString()); const int width = m_crumbLabel->width(); @@ -750,9 +752,9 @@ void FolderNavigationWidget::contextMenuEvent(QContextMenuEvent *ev) QAction *actionOpenAsProject = nullptr; QAction *newFolder = nullptr; const bool isDir = m_fileSystemModel->isDir(current); - const Utils::FileName filePath = hasCurrentItem ? Utils::FileName::fromString( + const Utils::FilePath filePath = hasCurrentItem ? Utils::FilePath::fromString( m_fileSystemModel->filePath(current)) - : Utils::FileName(); + : Utils::FilePath(); if (hasCurrentItem) { const QString fileName = m_fileSystemModel->fileName(current); if (isDir) { @@ -761,7 +763,7 @@ void FolderNavigationWidget::contextMenuEvent(QContextMenuEvent *ev) actionOpenProjects->setEnabled(false); } else { actionOpenFile = menu.addAction(tr("Open \"%1\"").arg(fileName)); - if (ProjectExplorerPlugin::isProjectFile(Utils::FileName::fromString(fileName))) + if (ProjectExplorerPlugin::isProjectFile(Utils::FilePath::fromString(fileName))) actionOpenAsProject = menu.addAction(tr("Open Project \"%1\"").arg(fileName)); } } @@ -864,12 +866,12 @@ FolderNavigationWidgetFactory::FolderNavigationWidgetFactory() insertRootDirectory({QLatin1String("A.Computer"), 0 /*sortValue*/, FolderNavigationWidget::tr("Computer"), - Utils::FileName(), + Utils::FilePath(), Icons::DESKTOP_DEVICE_SMALL.icon()}); insertRootDirectory({QLatin1String("A.Home"), 10 /*sortValue*/, FolderNavigationWidget::tr("Home"), - Utils::FileName::fromString(QDir::homePath()), + Utils::FilePath::fromString(QDir::homePath()), Utils::Icons::HOME.icon()}); updateProjectsDirectoryRoot(); connect(Core::DocumentManager::instance(), diff --git a/src/plugins/projectexplorer/foldernavigationwidget.h b/src/plugins/projectexplorer/foldernavigationwidget.h index b741de7cfe..b6268b66d6 100644 --- a/src/plugins/projectexplorer/foldernavigationwidget.h +++ b/src/plugins/projectexplorer/foldernavigationwidget.h @@ -63,7 +63,7 @@ public: QString id; int sortValue; QString displayName; - Utils::FileName path; + Utils::FilePath path; QIcon icon; }; @@ -119,17 +119,17 @@ protected: void contextMenuEvent(QContextMenuEvent *ev) override; private slots: - void setCrumblePath(const Utils::FileName &filePath); + void setCrumblePath(const Utils::FilePath &filePath); private: bool rootAutoSynchronization() const; void setRootAutoSynchronization(bool sync); void setHiddenFilesFilter(bool filter); - void selectBestRootForFile(const Utils::FileName &filePath); + void selectBestRootForFile(const Utils::FilePath &filePath); void handleCurrentEditorChanged(Core::IEditor *editor); - void selectFile(const Utils::FileName &filePath); - void setRootDirectory(const Utils::FileName &directory); - int bestRootForFile(const Utils::FileName &filePath); + void selectFile(const Utils::FilePath &filePath); + void setRootDirectory(const Utils::FilePath &directory); + int bestRootForFile(const Utils::FilePath &filePath); void openItem(const QModelIndex &index); QStringList projectsInDirectory(const QModelIndex &index) const; void openProjectsInDirectory(const QModelIndex &index); diff --git a/src/plugins/projectexplorer/gccparser.cpp b/src/plugins/projectexplorer/gccparser.cpp index 9b1fe34256..fff0606889 100644 --- a/src/plugins/projectexplorer/gccparser.cpp +++ b/src/plugins/projectexplorer/gccparser.cpp @@ -77,7 +77,7 @@ void GccParser::stdError(const QString &line) lne == QLatin1String("* cpp failed")) { newTask(Task(Task::Error, lne /* description */, - Utils::FileName() /* filename */, + Utils::FilePath() /* filename */, -1 /* linenumber */, Constants::TASK_CATEGORY_COMPILE)); return; @@ -93,7 +93,7 @@ void GccParser::stdError(const QString &line) } else if (description.startsWith(QLatin1String("fatal: "))) { description = description.mid(7); } - Task task(type, description, Utils::FileName(), /* filename */ + Task task(type, description, Utils::FilePath(), /* filename */ -1, /* line */ Constants::TASK_CATEGORY_COMPILE); newTask(task); return; @@ -101,7 +101,7 @@ void GccParser::stdError(const QString &line) match = m_regExp.match(lne); if (match.hasMatch()) { - Utils::FileName filename = Utils::FileName::fromUserInput(match.captured(1)); + Utils::FilePath filename = Utils::FilePath::fromUserInput(match.captured(1)); int lineno = match.captured(3).toInt(); Task::TaskType type = Task::Unknown; QString description = match.captured(8); @@ -125,7 +125,7 @@ void GccParser::stdError(const QString &line) if (match.hasMatch()) { newTask(Task(Task::Unknown, lne.trimmed() /* description */, - Utils::FileName::fromUserInput(match.captured(1)) /* filename */, + Utils::FilePath::fromUserInput(match.captured(1)) /* filename */, match.captured(3).toInt() /* linenumber */, Constants::TASK_CATEGORY_COMPILE)); return; @@ -199,7 +199,7 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() QTest::addColumn<OutputParserTester::Channel>("inputChannel"); QTest::addColumn<QString>("childStdOutLines"); QTest::addColumn<QString>("childStdErrLines"); - QTest::addColumn<QList<Task> >("tasks"); + QTest::addColumn<Tasks >("tasks"); QTest::addColumn<QString>("outputLines"); const Core::Id categoryCompile = Constants::TASK_CATEGORY_COMPILE; @@ -207,18 +207,18 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() QTest::newRow("pass-through stdout") << QString::fromLatin1("Sometext") << OutputParserTester::STDOUT << QString::fromLatin1("Sometext\n") << QString() - << QList<Task>() + << Tasks() << QString(); QTest::newRow("pass-through stderr") << QString::fromLatin1("Sometext") << OutputParserTester::STDERR << QString() << QString::fromLatin1("Sometext\n") - << QList<Task>() + << Tasks() << QString(); QTest::newRow("ar output") << QString::fromLatin1("../../../../x86/i686-unknown-linux-gnu/bin/i686-unknown-linux-gnu-ar: creating lib/libSkyView.a") << OutputParserTester::STDERR << QString() << QString::fromLatin1("../../../../x86/i686-unknown-linux-gnu/bin/i686-unknown-linux-gnu-ar: creating lib/libSkyView.a\n") - << QList<Task>() + << Tasks() << QString(); QTest::newRow("GCCE error") @@ -227,18 +227,18 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "/temp/test/untitled8/main.cpp:9: error: (Each undeclared identifier is reported only once for each function it appears in.)") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Unknown, QLatin1String("In function `int main(int, char**)':"), - Utils::FileName::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), -1, + Utils::FilePath::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), -1, categoryCompile) << Task(Task::Error, QLatin1String("`sfasdf' undeclared (first use this function)"), - Utils::FileName::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), 9, + Utils::FilePath::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), 9, categoryCompile) << Task(Task::Error, QLatin1String("(Each undeclared identifier is reported only once for each function it appears in.)"), - Utils::FileName::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), 9, + Utils::FilePath::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), 9, categoryCompile) ) << QString(); @@ -246,29 +246,29 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() << QString::fromLatin1("/src/corelib/global/qglobal.h:1635: warning: inline function `QDebug qDebug()' used but never defined") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Warning, QLatin1String("inline function `QDebug qDebug()' used but never defined"), - Utils::FileName::fromUserInput(QLatin1String("/src/corelib/global/qglobal.h")), 1635, + Utils::FilePath::fromUserInput(QLatin1String("/src/corelib/global/qglobal.h")), 1635, categoryCompile)) << QString(); QTest::newRow("warning") << QString::fromLatin1("main.cpp:7:2: warning: Some warning") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() << Task(Task::Warning, + << (Tasks() << Task(Task::Warning, QLatin1String("Some warning"), - Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 7, + Utils::FilePath::fromUserInput(QLatin1String("main.cpp")), 7, categoryCompile)) << QString(); QTest::newRow("GCCE #error") << QString::fromLatin1("C:\\temp\\test\\untitled8\\main.cpp:7: #error Symbian error") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Error, QLatin1String("#error Symbian error"), - Utils::FileName::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8\\main.cpp")), 7, + Utils::FilePath::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8\\main.cpp")), 7, categoryCompile)) << QString(); // Symbian reports #warning(s) twice (using different syntax). @@ -276,20 +276,20 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() << QString::fromLatin1("C:\\temp\\test\\untitled8\\main.cpp:8: warning: #warning Symbian warning") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Warning, QLatin1String("#warning Symbian warning"), - Utils::FileName::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8\\main.cpp")), 8, + Utils::FilePath::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8\\main.cpp")), 8, categoryCompile)) << QString(); QTest::newRow("GCCE #warning2") << QString::fromLatin1("/temp/test/untitled8/main.cpp:8:2: warning: #warning Symbian warning") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Warning, QLatin1String("#warning Symbian warning"), - Utils::FileName::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), 8, + Utils::FilePath::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), 8, categoryCompile)) << QString(); QTest::newRow("Undefined reference (debug)") @@ -298,18 +298,18 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "collect2: ld returned 1 exit status") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Unknown, QLatin1String("In function `main':"), - Utils::FileName::fromUserInput(QLatin1String("main.o")), -1, + Utils::FilePath::fromUserInput(QLatin1String("main.o")), -1, categoryCompile) << Task(Task::Error, QLatin1String("undefined reference to `MainWindow::doSomething()'"), - Utils::FileName::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8/main.cpp")), 8, + Utils::FilePath::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8/main.cpp")), 8, categoryCompile) << Task(Task::Error, QLatin1String("collect2: ld returned 1 exit status"), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile) ) << QString(); @@ -319,18 +319,18 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "collect2: ld returned 1 exit status") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Unknown, QLatin1String("In function `main':"), - Utils::FileName::fromUserInput(QLatin1String("main.o")), -1, + Utils::FilePath::fromUserInput(QLatin1String("main.o")), -1, categoryCompile) << Task(Task::Error, QLatin1String("undefined reference to `MainWindow::doSomething()'"), - Utils::FileName::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8/main.cpp")), -1, + Utils::FilePath::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8/main.cpp")), -1, categoryCompile) << Task(Task::Error, QLatin1String("collect2: ld returned 1 exit status"), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile) ) << QString(); @@ -338,20 +338,20 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() << QString::fromLatin1("c:\\Qt\\4.6\\lib/QtGuid4.dll: file not recognized: File format not recognized") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Error, QLatin1String("file not recognized: File format not recognized"), - Utils::FileName::fromUserInput(QLatin1String("c:\\Qt\\4.6\\lib/QtGuid4.dll")), -1, + Utils::FilePath::fromUserInput(QLatin1String("c:\\Qt\\4.6\\lib/QtGuid4.dll")), -1, categoryCompile)) << QString(); QTest::newRow("Invalid rpath") << QString::fromLatin1("g++: /usr/local/lib: No such file or directory") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Error, QLatin1String("/usr/local/lib: No such file or directory"), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile)) << QString(); @@ -361,18 +361,18 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "../../../../master/src/plugins/debugger/gdb/gdbengine.cpp:2115: warning: unused variable 'handler'") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Unknown, QLatin1String("In member function 'void Debugger::Internal::GdbEngine::handleBreakInsert2(const Debugger::Internal::GdbResponse&)':"), - Utils::FileName::fromUserInput(QLatin1String("../../../../master/src/plugins/debugger/gdb/gdbengine.cpp")), -1, + Utils::FilePath::fromUserInput(QLatin1String("../../../../master/src/plugins/debugger/gdb/gdbengine.cpp")), -1, categoryCompile) << Task(Task::Warning, QLatin1String("unused variable 'index'"), - Utils::FileName::fromUserInput(QLatin1String("../../../../master/src/plugins/debugger/gdb/gdbengine.cpp")), 2114, + Utils::FilePath::fromUserInput(QLatin1String("../../../../master/src/plugins/debugger/gdb/gdbengine.cpp")), 2114, categoryCompile) << Task(Task::Warning, QLatin1String("unused variable 'handler'"), - Utils::FileName::fromUserInput(QLatin1String("../../../../master/src/plugins/debugger/gdb/gdbengine.cpp")), 2115, + Utils::FilePath::fromUserInput(QLatin1String("../../../../master/src/plugins/debugger/gdb/gdbengine.cpp")), 2115, categoryCompile)) << QString(); QTest::newRow("gnumakeparser.cpp errors") @@ -381,18 +381,18 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp:264: error: expected ';' before ':' token") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Unknown, QLatin1String("In member function 'void ProjectExplorer::ProjectExplorerPlugin::testGnuMakeParserTaskMangling_data()':"), - Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp")), -1, + Utils::FilePath::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp")), -1, categoryCompile) << Task(Task::Error, QLatin1String("expected primary-expression before ':' token"), - Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp")), 264, + Utils::FilePath::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp")), 264, categoryCompile) << Task(Task::Error, QLatin1String("expected ';' before ':' token"), - Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp")), 264, + Utils::FilePath::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp")), 264, categoryCompile)) << QString(); QTest::newRow("distcc error(QTCREATORBUG-904)") @@ -401,42 +401,42 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() << OutputParserTester::STDERR << QString() << QString::fromLatin1("distcc[73168] (dcc_get_hostlist) Warning: no hostlist is set; can't distribute work\n" "distcc[73168] (dcc_build_somewhere) Warning: failed to distribute, running locally instead\n") - << QList<Task>() + << Tasks() << QString(); QTest::newRow("ld warning (QTCREATORBUG-905)") << QString::fromLatin1("ld: warning: Core::IEditor* QVariant::value<Core::IEditor*>() const has different visibility (hidden) in .obj/debug-shared/openeditorsview.o and (default) in .obj/debug-shared/editormanager.o") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Warning, QLatin1String("Core::IEditor* QVariant::value<Core::IEditor*>() const has different visibility (hidden) in .obj/debug-shared/openeditorsview.o and (default) in .obj/debug-shared/editormanager.o"), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile)) << QString(); QTest::newRow("ld fatal") << QString::fromLatin1("ld: fatal: Symbol referencing errors. No output written to testproject") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Error, QLatin1String("Symbol referencing errors. No output written to testproject"), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile)) << QString(); QTest::newRow("Teambuilder issues") << QString::fromLatin1("TeamBuilder Client:: error: could not find Scheduler, running Job locally...") << OutputParserTester::STDERR << QString() << QString::fromLatin1("TeamBuilder Client:: error: could not find Scheduler, running Job locally...\n") - << QList<Task>() + << Tasks() << QString(); QTest::newRow("note") << QString::fromLatin1("/home/dev/creator/share/qtcreator/debugger/dumper.cpp:1079: note: initialized from here") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("initialized from here"), - Utils::FileName::fromUserInput(QLatin1String("/home/dev/creator/share/qtcreator/debugger/dumper.cpp")), 1079, + Utils::FilePath::fromUserInput(QLatin1String("/home/dev/creator/share/qtcreator/debugger/dumper.cpp")), 1079, categoryCompile)) << QString(); QTest::newRow("static member function") @@ -444,30 +444,30 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "/Qt/4.6.2-Symbian/s60sdk/epoc32/include/stdapis/stlport/stl/_tree.c:194: warning: suggest explicit braces to avoid ambiguous 'else'") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("In static member function 'static std::_Rb_tree_node_base* std::_Rb_global<_Dummy>::_Rebalance_for_erase(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*&, std::_Rb_tree_node_base*&, std::_Rb_tree_node_base*&)':"), - Utils::FileName::fromUserInput(QLatin1String("/Qt/4.6.2-Symbian/s60sdk/epoc32/include/stdapis/stlport/stl/_tree.c")), -1, + Utils::FilePath::fromUserInput(QLatin1String("/Qt/4.6.2-Symbian/s60sdk/epoc32/include/stdapis/stlport/stl/_tree.c")), -1, categoryCompile) << Task(Task::Warning, QLatin1String("suggest explicit braces to avoid ambiguous 'else'"), - Utils::FileName::fromUserInput(QLatin1String("/Qt/4.6.2-Symbian/s60sdk/epoc32/include/stdapis/stlport/stl/_tree.c")), 194, + Utils::FilePath::fromUserInput(QLatin1String("/Qt/4.6.2-Symbian/s60sdk/epoc32/include/stdapis/stlport/stl/_tree.c")), 194, categoryCompile)) << QString(); QTest::newRow("rm false positive") << QString::fromLatin1("rm: cannot remove `release/moc_mainwindow.cpp': No such file or directory") << OutputParserTester::STDERR << QString() << QString::fromLatin1("rm: cannot remove `release/moc_mainwindow.cpp': No such file or directory\n") - << QList<Task>() + << Tasks() << QString(); QTest::newRow("ld: missing library") << QString::fromLatin1("/usr/bin/ld: cannot find -ldoesnotexist") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Error, QLatin1String("cannot find -ldoesnotexist"), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile)) << QString(); QTest::newRow("In function") @@ -476,38 +476,38 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "../../scriptbug/main.cpp:8: warning: unused variable c") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("In function void foo(i) [with i = double]:"), - Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), -1, + Utils::FilePath::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), -1, categoryCompile) << Task(Task::Unknown, QLatin1String("instantiated from here"), - Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 22, + Utils::FilePath::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 22, categoryCompile) << Task(Task::Warning, QLatin1String("unused variable c"), - Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 8, + Utils::FilePath::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 8, categoryCompile)) << QString(); QTest::newRow("instanciated from here") << QString::fromLatin1("main.cpp:10: instantiated from here ") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("instantiated from here"), - Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 10, + Utils::FilePath::fromUserInput(QLatin1String("main.cpp")), 10, categoryCompile)) << QString(); QTest::newRow("In constructor") << QString::fromLatin1("/dev/creator/src/plugins/find/basetextfind.h: In constructor 'Find::BaseTextFind::BaseTextFind(QTextEdit*)':") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("In constructor 'Find::BaseTextFind::BaseTextFind(QTextEdit*)':"), - Utils::FileName::fromUserInput(QLatin1String("/dev/creator/src/plugins/find/basetextfind.h")), -1, + Utils::FilePath::fromUserInput(QLatin1String("/dev/creator/src/plugins/find/basetextfind.h")), -1, categoryCompile)) << QString(); @@ -519,26 +519,26 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "../../scriptbug/main.cpp:5: warning: unused parameter v") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("At global scope:"), - Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), -1, + Utils::FilePath::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), -1, categoryCompile) << Task(Task::Unknown, QLatin1String("In instantiation of void bar(i) [with i = double]:"), - Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), -1, + Utils::FilePath::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), -1, categoryCompile) << Task(Task::Unknown, QLatin1String("instantiated from void foo(i) [with i = double]"), - Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 8, + Utils::FilePath::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 8, categoryCompile) << Task(Task::Unknown, QLatin1String("instantiated from here"), - Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 22, + Utils::FilePath::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 22, categoryCompile) << Task(Task::Warning, QLatin1String("unused parameter v"), - Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 5, + Utils::FilePath::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 5, categoryCompile)) << QString(); @@ -546,10 +546,10 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() << QString::fromLatin1("/home/code/test.cpp:54:38: fatal error: test.moc: No such file or directory") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Error, QLatin1String("test.moc: No such file or directory"), - Utils::FileName::fromUserInput(QLatin1String("/home/code/test.cpp")), 54, + Utils::FilePath::fromUserInput(QLatin1String("/home/code/test.cpp")), 54, categoryCompile)) << QString(); @@ -560,22 +560,22 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "collect2: ld returned 1 exit status") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("In function `QPlotAxis':"), - Utils::FileName::fromUserInput(QLatin1String("debug/qplotaxis.o")), -1, + Utils::FilePath::fromUserInput(QLatin1String("debug/qplotaxis.o")), -1, categoryCompile) << Task(Task::Error, QLatin1String("undefined reference to `vtable for QPlotAxis'"), - Utils::FileName::fromUserInput(QLatin1String("M:\\Development\\x64\\QtPlot/qplotaxis.cpp")), 26, + Utils::FilePath::fromUserInput(QLatin1String("M:\\Development\\x64\\QtPlot/qplotaxis.cpp")), 26, categoryCompile) << Task(Task::Error, QLatin1String("undefined reference to `vtable for QPlotAxis'"), - Utils::FileName::fromUserInput(QLatin1String("M:\\Development\\x64\\QtPlot/qplotaxis.cpp")), 26, + Utils::FilePath::fromUserInput(QLatin1String("M:\\Development\\x64\\QtPlot/qplotaxis.cpp")), 26, categoryCompile) << Task(Task::Error, QLatin1String("collect2: ld returned 1 exit status"), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile)) << QString(); @@ -587,26 +587,26 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "../stl/main.cpp:31: warning: unused parameter index") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("In member function typename _Vector_base<_Tp, _Alloc>::_Tp_alloc_type::const_reference Vector<_Tp, _Alloc>::at(int) [with _Tp = Point, _Alloc = Allocator<Point>]:"), - Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), -1, + Utils::FilePath::fromUserInput(QLatin1String("../stl/main.cpp")), -1, categoryCompile) << Task(Task::Unknown, QLatin1String("instantiated from here"), - Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), 38, + Utils::FilePath::fromUserInput(QLatin1String("../stl/main.cpp")), 38, categoryCompile) << Task(Task::Warning, QLatin1String("returning reference to temporary"), - Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), 31, + Utils::FilePath::fromUserInput(QLatin1String("../stl/main.cpp")), 31, categoryCompile) << Task(Task::Unknown, QLatin1String("At global scope:"), - Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), -1, + Utils::FilePath::fromUserInput(QLatin1String("../stl/main.cpp")), -1, categoryCompile) << Task(Task::Warning, QLatin1String("unused parameter index"), - Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), 31, + Utils::FilePath::fromUserInput(QLatin1String("../stl/main.cpp")), 31, categoryCompile)) << QString(); @@ -617,22 +617,22 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "C:/Symbian_SDK/epoc32/include/e32cmn.inl:7094: warning: returning reference to temporary") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("In file included from C:/Symbian_SDK/epoc32/include/e32cmn.h:6792,"), - Utils::FileName::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32cmn.h")), 6792, + Utils::FilePath::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32cmn.h")), 6792, categoryCompile) << Task(Task::Unknown, QLatin1String("from C:/Symbian_SDK/epoc32/include/e32std.h:25,"), - Utils::FileName::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32std.h")), 25, + Utils::FilePath::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32std.h")), 25, categoryCompile) << Task(Task::Unknown, QLatin1String("In member function 'SSecureId::operator const TSecureId&() const':"), - Utils::FileName::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32cmn.inl")), -1, + Utils::FilePath::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32cmn.inl")), -1, categoryCompile) << Task(Task::Warning, QLatin1String("returning reference to temporary"), - Utils::FileName::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32cmn.inl")), 7094, + Utils::FilePath::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32cmn.inl")), 7094, categoryCompile)) << QString(); @@ -640,10 +640,10 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() << QString::fromLatin1("../../../src/XmlUg/targetdelete.c: At top level:") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("At top level:"), - Utils::FileName::fromUserInput(QLatin1String("../../../src/XmlUg/targetdelete.c")), -1, + Utils::FilePath::fromUserInput(QLatin1String("../../../src/XmlUg/targetdelete.c")), -1, categoryCompile)) << QString(); @@ -653,18 +653,18 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "/Symbian/SDK/epoc32/include/variant/Symbian_OS.hrh:1134:26: warning: no newline at end of file") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("In file included from /Symbian/SDK/EPOC32/INCLUDE/GCCE/GCCE.h:15,"), - Utils::FileName::fromUserInput(QLatin1String("/Symbian/SDK/EPOC32/INCLUDE/GCCE/GCCE.h")), 15, + Utils::FilePath::fromUserInput(QLatin1String("/Symbian/SDK/EPOC32/INCLUDE/GCCE/GCCE.h")), 15, categoryCompile) << Task(Task::Unknown, QLatin1String("from <command line>:26:"), - Utils::FileName::fromUserInput(QLatin1String("<command line>")), 26, + Utils::FilePath::fromUserInput(QLatin1String("<command line>")), 26, categoryCompile) << Task(Task::Warning, QLatin1String("no newline at end of file"), - Utils::FileName::fromUserInput(QLatin1String("/Symbian/SDK/epoc32/include/variant/Symbian_OS.hrh")), 1134, + Utils::FilePath::fromUserInput(QLatin1String("/Symbian/SDK/epoc32/include/variant/Symbian_OS.hrh")), 1134, categoryCompile)) << QString(); @@ -672,10 +672,10 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() << QString::fromLatin1("release/main.o:main.cpp:(.text+0x42): undefined reference to `MainWindow::doSomething()'") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Error, QLatin1String("undefined reference to `MainWindow::doSomething()'"), - Utils::FileName::fromUserInput(QLatin1String("main.cpp")), -1, + Utils::FilePath::fromUserInput(QLatin1String("main.cpp")), -1, categoryCompile)) << QString(); @@ -684,14 +684,14 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "../../../src/shared/proparser/profileevaluator.cpp:2817:9: warning: case value '0' not in enumerated type 'ProFileEvaluator::Private::TestFunc'") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("In member function 'ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(const ProString&, const ProStringList&)':"), - Utils::FileName::fromUserInput(QLatin1String("../../../src/shared/proparser/profileevaluator.cpp")), -1, + Utils::FilePath::fromUserInput(QLatin1String("../../../src/shared/proparser/profileevaluator.cpp")), -1, categoryCompile) << Task(Task::Warning, QLatin1String("case value '0' not in enumerated type 'ProFileEvaluator::Private::TestFunc'"), - Utils::FileName::fromUserInput(QLatin1String("../../../src/shared/proparser/profileevaluator.cpp")), 2817, + Utils::FilePath::fromUserInput(QLatin1String("../../../src/shared/proparser/profileevaluator.cpp")), 2817, categoryCompile)) << QString(); @@ -700,14 +700,14 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "./mw.h:4:0: warning: \"STUPID_DEFINE\" redefined") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("In file included from <command-line>:0:0:"), - Utils::FileName::fromUserInput(QLatin1String("<command-line>")), 0, + Utils::FilePath::fromUserInput(QLatin1String("<command-line>")), 0, categoryCompile) << Task(Task::Warning, QLatin1String("\"STUPID_DEFINE\" redefined"), - Utils::FileName::fromUserInput(QLatin1String("./mw.h")), 4, + Utils::FilePath::fromUserInput(QLatin1String("./mw.h")), 4, categoryCompile)) << QString(); QTest::newRow("instanciation with line:column info") @@ -716,28 +716,28 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "file.h:21:5: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("In function 'void UnitTest::CheckEqual(UnitTest::TestResults&, const Expected&, const Actual&, const UnitTest::TestDetails&) [with Expected = unsigned int, Actual = int]':"), - Utils::FileName::fromUserInput(QLatin1String("file.h")), -1, + Utils::FilePath::fromUserInput(QLatin1String("file.h")), -1, categoryCompile) << Task(Task::Unknown, QLatin1String("instantiated from here"), - Utils::FileName::fromUserInput(QLatin1String("file.cpp")), 87, + Utils::FilePath::fromUserInput(QLatin1String("file.cpp")), 87, categoryCompile) << Task(Task::Warning, QLatin1String("comparison between signed and unsigned integer expressions [-Wsign-compare]"), - Utils::FileName::fromUserInput(QLatin1String("file.h")), 21, + Utils::FilePath::fromUserInput(QLatin1String("file.h")), 21, categoryCompile)) << QString(); QTest::newRow("linker error") // QTCREATORBUG-3107 << QString::fromLatin1("cns5k_ins_parser_tests.cpp:(.text._ZN20CNS5kINSParserEngine21DropBytesUntilStartedEP14CircularBufferIhE[CNS5kINSParserEngine::DropBytesUntilStarted(CircularBuffer<unsigned char>*)]+0x6d): undefined reference to `CNS5kINSPacket::SOH_BYTE'") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Error, QLatin1String("undefined reference to `CNS5kINSPacket::SOH_BYTE'"), - Utils::FileName::fromUserInput(QLatin1String("cns5k_ins_parser_tests.cpp")), -1, + Utils::FilePath::fromUserInput(QLatin1String("cns5k_ins_parser_tests.cpp")), -1, categoryCompile)) << QString(); @@ -745,10 +745,10 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() << QString::fromLatin1("mainwindow.ui: Warning: The name 'pushButton' (QPushButton) is already in use, defaulting to 'pushButton1'.") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Warning, QLatin1String("The name 'pushButton' (QPushButton) is already in use, defaulting to 'pushButton1'."), - Utils::FileName::fromUserInput(QLatin1String("mainwindow.ui")), -1, + Utils::FilePath::fromUserInput(QLatin1String("mainwindow.ui")), -1, Constants::TASK_CATEGORY_COMPILE)) << QString(); @@ -756,10 +756,10 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() << QString::fromLatin1("libimf.so: warning: warning: feupdateenv is not implemented and will always fail") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Warning, QLatin1String("warning: feupdateenv is not implemented and will always fail"), - Utils::FileName::fromUserInput(QLatin1String("libimf.so")), -1, + Utils::FilePath::fromUserInput(QLatin1String("libimf.so")), -1, Constants::TASK_CATEGORY_COMPILE)) << QString(); @@ -770,16 +770,16 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() " ^") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("In file included from /home/code/src/creator/src/libs/extensionsystem/pluginerrorview.cpp:31:0:"), - Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/libs/extensionsystem/pluginerrorview.cpp")), 31, + Utils::FilePath::fromUserInput(QLatin1String("/home/code/src/creator/src/libs/extensionsystem/pluginerrorview.cpp")), 31, categoryCompile) << Task(Task::Error, QLatin1String("QtGui/QAction: No such file or directory\n" " #include <QtGui/QAction>\n" " ^"), - Utils::FileName::fromUserInput(QLatin1String(".uic/ui_pluginerrorview.h")), 14, + Utils::FilePath::fromUserInput(QLatin1String(".uic/ui_pluginerrorview.h")), 14, categoryCompile)) << QString(); @@ -791,26 +791,26 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "main.cpp:7:22: error: within this context") << OutputParserTester::STDERR << QString() << QString() - << ( QList<Task>() + << ( Tasks() << Task(Task::Unknown, QLatin1String("In file included from /usr/include/qt4/QtCore/QString:1:0,"), - Utils::FileName::fromUserInput(QLatin1String("/usr/include/qt4/QtCore/QString")), 1, + Utils::FilePath::fromUserInput(QLatin1String("/usr/include/qt4/QtCore/QString")), 1, categoryCompile) << Task(Task::Unknown, QLatin1String("from main.cpp:3:"), - Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 3, + Utils::FilePath::fromUserInput(QLatin1String("main.cpp")), 3, categoryCompile) << Task(Task::Unknown, QLatin1String("In function 'void foo()':"), - Utils::FileName::fromUserInput(QLatin1String("/usr/include/qt4/QtCore/qstring.h")), -1, + Utils::FilePath::fromUserInput(QLatin1String("/usr/include/qt4/QtCore/qstring.h")), -1, categoryCompile) << Task(Task::Error, QLatin1String("'QString::QString(const char*)' is private"), - Utils::FileName::fromUserInput(QLatin1String("/usr/include/qt4/QtCore/qstring.h")), 597, + Utils::FilePath::fromUserInput(QLatin1String("/usr/include/qt4/QtCore/qstring.h")), 597, categoryCompile) << Task(Task::Error, QLatin1String("within this context"), - Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 7, + Utils::FilePath::fromUserInput(QLatin1String("main.cpp")), 7, categoryCompile)) << QString(); @@ -821,22 +821,22 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "collect2: error: ld returned 1 exit status") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Unknown, QLatin1String("In function `foo()':"), - Utils::FileName::fromUserInput(QLatin1String("foo.o")), -1, + Utils::FilePath::fromUserInput(QLatin1String("foo.o")), -1, categoryCompile) << Task(Task::Error, QLatin1String("multiple definition of `foo()'"), - Utils::FileName::fromUserInput(QLatin1String("/home/user/test/foo.cpp")), 2, + Utils::FilePath::fromUserInput(QLatin1String("/home/user/test/foo.cpp")), 2, categoryCompile) << Task(Task::Unknown, QLatin1String("first defined here"), - Utils::FileName::fromUserInput(QLatin1String("/home/user/test/bar.cpp")), 4, + Utils::FilePath::fromUserInput(QLatin1String("/home/user/test/bar.cpp")), 4, categoryCompile) << Task(Task::Error, QLatin1String("collect2: error: ld returned 1 exit status"), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile) ) << QString(); @@ -847,18 +847,18 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() "collect2: error: ld returned 1 exit status") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Error, QLatin1String("multiple definition of `foo'"), - Utils::FileName::fromUserInput(QLatin1String("foo.o")), -1, + Utils::FilePath::fromUserInput(QLatin1String("foo.o")), -1, categoryCompile) << Task(Task::Unknown, QLatin1String("first defined here"), - Utils::FileName::fromUserInput(QLatin1String("bar.o")), -1, + Utils::FilePath::fromUserInput(QLatin1String("bar.o")), -1, categoryCompile) << Task(Task::Error, QLatin1String("collect2: error: ld returned 1 exit status"), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile) ) << QString(); @@ -867,10 +867,10 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() << "obj/gtest-clang-printing.o:gtest-clang-printing.cpp:llvm::VerifyDisableABIBreakingChecks: error: undefined reference to 'llvm::DisableABIBreakingChecks'" << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Error, QLatin1String("error: undefined reference to 'llvm::DisableABIBreakingChecks'"), - Utils::FileName::fromString("gtest-clang-printing.cpp"), -1, + Utils::FilePath::fromString("gtest-clang-printing.cpp"), -1, categoryCompile) ) << QString(); @@ -879,10 +879,10 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() << QString::fromLatin1("ranlib: file: lib/libtest.a(Test0.cpp.o) has no symbols") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Warning, QLatin1String("file: lib/libtest.a(Test0.cpp.o) has no symbols"), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile) ) << QString(); @@ -890,10 +890,10 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() << QString::fromLatin1("/path/to/XCode/and/ranlib: file: lib/libtest.a(Test0.cpp.o) has no symbols") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Warning, QLatin1String("file: lib/libtest.a(Test0.cpp.o) has no symbols"), - Utils::FileName(), -1, + Utils::FilePath(), -1, categoryCompile) ) << QString(); @@ -901,10 +901,10 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() << QString::fromLatin1("/home/qtwebkithelpviewer.h:0: Note: No relevant classes found. No output generated.") << OutputParserTester::STDERR << QString() << QString() - << (QList<ProjectExplorer::Task>() + << (Tasks() << Task(Task::Unknown, QLatin1String("Note: No relevant classes found. No output generated."), - Utils::FileName::fromUserInput(QLatin1String("/home/qtwebkithelpviewer.h")), 0, + Utils::FilePath::fromUserInput(QLatin1String("/home/qtwebkithelpviewer.h")), 0, categoryCompile) ) << QString(); @@ -916,7 +916,7 @@ void ProjectExplorerPlugin::testGccOutputParsers() testbench.appendOutputParser(new GccParser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); - QFETCH(QList<Task>, tasks); + QFETCH(Tasks, tasks); QFETCH(QString, childStdOutLines); QFETCH(QString, childStdErrLines); QFETCH(QString, outputLines); diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index c9c28846f6..66db003212 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -77,7 +77,7 @@ static const char supportedAbisKeyC[] = "ProjectExplorer.GccToolChain.SupportedA static const char parentToolChainIdKeyC[] = "ProjectExplorer.ClangToolChain.ParentToolChainId"; static const char binaryRegexp[] = "(?:^|-|\\b)(?:gcc|g\\+\\+|clang(?:\\+\\+)?)(?:-([\\d.]+))?$"; -static QByteArray runGcc(const FileName &gcc, const QStringList &arguments, const QStringList &env) +static QByteArray runGcc(const FilePath &gcc, const QStringList &arguments, const QStringList &env) { if (gcc.isEmpty() || !gcc.toFileInfo().isExecutable()) return QByteArray(); @@ -98,7 +98,7 @@ static QByteArray runGcc(const FileName &gcc, const QStringList &arguments, cons return response.allOutput().toUtf8(); } -static ProjectExplorer::Macros gccPredefinedMacros(const FileName &gcc, +static ProjectExplorer::Macros gccPredefinedMacros(const FilePath &gcc, const QStringList &args, const QStringList &env) { @@ -124,7 +124,7 @@ static ProjectExplorer::Macros gccPredefinedMacros(const FileName &gcc, return predefinedMacros; } -HeaderPaths GccToolChain::gccHeaderPaths(const FileName &gcc, const QStringList &arguments, +HeaderPaths GccToolChain::gccHeaderPaths(const FilePath &gcc, const QStringList &arguments, const QStringList &env) { HeaderPaths builtInHeaderPaths; @@ -167,16 +167,9 @@ HeaderPaths GccToolChain::gccHeaderPaths(const FileName &gcc, const QStringList return builtInHeaderPaths; } -void GccToolChain::toolChainUpdated() +static Abis guessGccAbi(const QString &m, const ProjectExplorer::Macros ¯os) { - m_predefinedMacrosCache->invalidate(); - m_headerPathsCache->invalidate(); - ToolChain::toolChainUpdated(); -} - -static QList<Abi> guessGccAbi(const QString &m, const ProjectExplorer::Macros ¯os) -{ - QList<Abi> abiList; + Abis abiList; Abi guessed = Abi::abiFromTargetTriplet(m); if (guessed.isNull()) @@ -203,7 +196,10 @@ static QList<Abi> guessGccAbi(const QString &m, const ProjectExplorer::Macros &m abiList << Abi(arch, os, flavor, format, width == 64 ? 32 : 64); } else if (arch == Abi::X86Architecture && (width == 0 || width == 64)) { abiList << Abi(arch, os, flavor, format, 64); - abiList << Abi(arch, os, flavor, format, 32); + if (width != 64 || (!m.contains("mingw") + && ToolChainManager::detectionSettings().detectX64AsX32)) { + abiList << Abi(arch, os, flavor, format, 32); + } } else { abiList << Abi(arch, os, flavor, format, width); } @@ -211,7 +207,7 @@ static QList<Abi> guessGccAbi(const QString &m, const ProjectExplorer::Macros &m } -static GccToolChain::DetectedAbisResult guessGccAbi(const FileName &path, const QStringList &env, +static GccToolChain::DetectedAbisResult guessGccAbi(const FilePath &path, const QStringList &env, const ProjectExplorer::Macros ¯os, const QStringList &extraArgs = QStringList()) { @@ -226,7 +222,7 @@ static GccToolChain::DetectedAbisResult guessGccAbi(const FileName &path, const return GccToolChain::DetectedAbisResult(guessGccAbi(machine, macros), machine); } -static QString gccVersion(const FileName &path, const QStringList &env) +static QString gccVersion(const FilePath &path, const QStringList &env) { QStringList arguments("-dumpversion"); return QString::fromLocal8Bit(runGcc(path, arguments, env)).trimmed(); @@ -236,17 +232,15 @@ static QString gccVersion(const FileName &path, const QStringList &env) // GccToolChain // -------------------------------------------------------------------------- -GccToolChain::GccToolChain(Detection d) : - GccToolChain(Constants::GCC_TOOLCHAIN_TYPEID, d) +GccToolChain::GccToolChain() : + GccToolChain(Constants::GCC_TOOLCHAIN_TYPEID) { } -GccToolChain::GccToolChain(Core::Id typeId, Detection d) : - ToolChain(typeId, d), - m_predefinedMacrosCache(std::make_shared<Cache<MacroInspectionReport, 64>>()), - m_headerPathsCache(std::make_shared<Cache<HeaderPaths>>()) +GccToolChain::GccToolChain(Core::Id typeId) : + ToolChain(typeId) { } -void GccToolChain::setCompilerCommand(const FileName &path) +void GccToolChain::setCompilerCommand(const FilePath &path) { if (path == m_compilerCommand) return; @@ -255,12 +249,12 @@ void GccToolChain::setCompilerCommand(const FileName &path) toolChainUpdated(); } -void GccToolChain::setSupportedAbis(const QList<Abi> &m_abis) +void GccToolChain::setSupportedAbis(const Abis &abis) { - if (m_supportedAbis == m_abis) + if (m_supportedAbis == abis) return; - m_supportedAbis = m_abis; + m_supportedAbis = abis; toolChainUpdated(); } @@ -328,14 +322,14 @@ void GccToolChain::setTargetAbi(const Abi &abi) toolChainUpdated(); } -QList<Abi> GccToolChain::supportedAbis() const +Abis GccToolChain::supportedAbis() const { return m_supportedAbis; } bool GccToolChain::isValid() const { - if (m_compilerCommand.isNull()) + if (m_compilerCommand.isEmpty()) return false; QFileInfo fi = compilerCommand().toFileInfo(); @@ -347,7 +341,7 @@ static bool isNetworkCompiler(const QString &dirPath) return dirPath.contains("icecc") || dirPath.contains("distcc"); } -static Utils::FileName findLocalCompiler(const Utils::FileName &compilerPath, +static Utils::FilePath findLocalCompiler(const Utils::FilePath &compilerPath, const Environment &env) { // Find the "real" compiler if icecc, distcc or similar are in use. Ignore ccache, since that @@ -359,13 +353,13 @@ static Utils::FileName findLocalCompiler(const Utils::FileName &compilerPath, return compilerPath; // Filter out network compilers - const FileNameList pathComponents = Utils::filtered(env.path(), [] (const FileName &dirPath) { + const FilePathList pathComponents = Utils::filtered(env.path(), [] (const FilePath &dirPath) { return !isNetworkCompiler(dirPath.toString()); }); // This effectively searches the PATH twice, once via pathComponents and once via PATH itself: // searchInPath filters duplicates, so that will not hurt. - const Utils::FileName path = env.searchInPath(compilerPath.fileName(), pathComponents); + const Utils::FilePath path = env.searchInPath(compilerPath.fileName(), pathComponents); return path.isEmpty() ? compilerPath : path; } @@ -375,11 +369,11 @@ ToolChain::MacroInspectionRunner GccToolChain::createMacroInspectionRunner() con // Using a clean environment breaks ccache/distcc/etc. Environment env = Environment::systemEnvironment(); addToEnvironment(env); - const Utils::FileName compilerCommand = m_compilerCommand; + const Utils::FilePath compilerCommand = m_compilerCommand; const QStringList platformCodeGenFlags = m_platformCodeGenFlags; OptionsReinterpreter reinterpretOptions = m_optionsReinterpreter; QTC_CHECK(reinterpretOptions); - std::shared_ptr<Cache<MacroInspectionReport, 64>> macroCache = m_predefinedMacrosCache; + MacrosCache macroCache = predefinedMacrosCache(); Core::Id lang = language(); // This runner must be thread-safe! @@ -566,63 +560,92 @@ void GccToolChain::initExtraHeaderPathsFunction(ExtraHeaderPathsFunction &&extra m_extraHeaderPathsFunction = std::move(extraHeaderPathsFunction); } +HeaderPaths GccToolChain::builtInHeaderPaths(const Utils::Environment &env, + const Utils::FilePath &compilerCommand, + const QStringList &platformCodeGenFlags, + OptionsReinterpreter reinterpretOptions, + HeaderPathsCache headerCache, + Core::Id languageId, + ExtraHeaderPathsFunction extraHeaderPathsFunction, + const QStringList &flags, + const QString &sysRoot, + const QString &originalTargetTriple) +{ + QStringList arguments = gccPrepareArguments(flags, + sysRoot, + platformCodeGenFlags, + languageId, + reinterpretOptions); + + // Must be clang case only. + if (!originalTargetTriple.isEmpty()) + arguments << "-target" << originalTargetTriple; + + const Utils::optional<HeaderPaths> cachedPaths = headerCache->check(arguments); + if (cachedPaths) + return cachedPaths.value(); + + HeaderPaths paths = gccHeaderPaths(findLocalCompiler(compilerCommand, env), + arguments, + env.toStringList()); + extraHeaderPathsFunction(paths); + headerCache->insert(arguments, paths); + + qCDebug(gccLog) << "Reporting header paths to code model:"; + for (const HeaderPath &hp : paths) { + qCDebug(gccLog) << compilerCommand.toUserOutput() + << (languageId == Constants::CXX_LANGUAGE_ID ? ": C++ [" : ": C [") + << arguments.join(", ") << "]" << hp.path; + } + + return paths; +} + ToolChain::BuiltInHeaderPathsRunner GccToolChain::createBuiltInHeaderPathsRunner() const { // Using a clean environment breaks ccache/distcc/etc. Environment env = Environment::systemEnvironment(); addToEnvironment(env); - const Utils::FileName compilerCommand = m_compilerCommand; - const QStringList platformCodeGenFlags = m_platformCodeGenFlags; - OptionsReinterpreter reinterpretOptions = m_optionsReinterpreter; - QTC_CHECK(reinterpretOptions); - std::shared_ptr<Cache<HeaderPaths>> headerCache = m_headerPathsCache; - Core::Id languageId = language(); - // This runner must be thread-safe! - return [env, compilerCommand, platformCodeGenFlags, reinterpretOptions, headerCache, languageId, - extraHeaderPathsFunction = m_extraHeaderPathsFunction] - (const QStringList &flags, const QString &sysRoot) { - - QStringList arguments = gccPrepareArguments(flags, sysRoot, platformCodeGenFlags, - languageId, reinterpretOptions); - - const Utils::optional<HeaderPaths> cachedPaths = headerCache->check(arguments); - if (cachedPaths) - return cachedPaths.value(); - - HeaderPaths paths = gccHeaderPaths(findLocalCompiler(compilerCommand, env), - arguments, env.toStringList()); - extraHeaderPathsFunction(paths); - headerCache->insert(arguments, paths); - - qCDebug(gccLog) << "Reporting header paths to code model:"; - for (const HeaderPath &hp : paths) { - qCDebug(gccLog) << compilerCommand.toUserOutput() - << (languageId == Constants::CXX_LANGUAGE_ID ? ": C++ [" : ": C [") - << arguments.join(", ") << "]" - << hp.path; - } - - return paths; + return [env, + compilerCommand = m_compilerCommand, + platformCodeGenFlags = m_platformCodeGenFlags, + reinterpretOptions = m_optionsReinterpreter, + headerCache = headerPathsCache(), + languageId = language(), + extraHeaderPathsFunction = m_extraHeaderPathsFunction](const QStringList &flags, + const QString &sysRoot, + const QString &) { + return builtInHeaderPaths(env, + compilerCommand, + platformCodeGenFlags, + reinterpretOptions, + headerCache, + languageId, + extraHeaderPathsFunction, + flags, + sysRoot, + /*target=*/""); // Target must be empty for gcc. }; } HeaderPaths GccToolChain::builtInHeaderPaths(const QStringList &flags, - const FileName &sysRoot) const + const FilePath &sysRootPath) const { - return createBuiltInHeaderPathsRunner()(flags, sysRoot.toString()); + return createBuiltInHeaderPathsRunner()(flags, + sysRootPath.isEmpty() ? sysRoot() + : sysRootPath.toString(), + originalTargetTriple()); } -void GccToolChain::addCommandPathToEnvironment(const FileName &command, Environment &env) +void GccToolChain::addCommandPathToEnvironment(const FilePath &command, Environment &env) { - const Utils::FileName compilerDir = command.parentDir(); + const Utils::FilePath compilerDir = command.parentDir(); if (!compilerDir.isEmpty()) env.prependOrSetPath(compilerDir.toString()); } -GccToolChain::GccToolChain(const GccToolChain &) = default; - void GccToolChain::addToEnvironment(Environment &env) const { // On Windows gcc invokes cc1plus which is in libexec directory. @@ -631,50 +654,48 @@ void GccToolChain::addToEnvironment(Environment &env) const addCommandPathToEnvironment(m_compilerCommand, env); } -FileNameList GccToolChain::suggestedMkspecList() const +QStringList GccToolChain::suggestedMkspecList() const { - Abi abi = targetAbi(); - Abi host = Abi::hostAbi(); + const Abi abi = targetAbi(); + const Abi host = Abi::hostAbi(); // Cross compile: Leave the mkspec alone! if (abi.architecture() != host.architecture() || abi.os() != host.os() || abi.osFlavor() != host.osFlavor()) // Note: This can fail:-( - return FileNameList(); + return {}; if (abi.os() == Abi::DarwinOS) { QString v = version(); // prefer versioned g++ on macOS. This is required to enable building for older macOS versions if (v.startsWith("4.0") && m_compilerCommand.endsWith("-4.0")) - return FileNameList() << FileName::fromLatin1("macx-g++40"); + return {"macx-g++40"}; if (v.startsWith("4.2") && m_compilerCommand.endsWith("-4.2")) - return FileNameList() << FileName::fromLatin1("macx-g++42"); - return FileNameList() << FileName::fromLatin1("macx-g++"); + return {"macx-g++42"}; + return {"macx-g++"}; } if (abi.os() == Abi::LinuxOS) { if (abi.osFlavor() != Abi::GenericFlavor) - return FileNameList(); // most likely not a desktop, so leave the mkspec alone. + return {}; // most likely not a desktop, so leave the mkspec alone. if (abi.wordWidth() == host.wordWidth()) { // no need to explicitly set the word width, but provide that mkspec anyway to make sure // that the correct compiler is picked if a mkspec with a wordwidth is given. - return FileNameList() << FileName::fromLatin1("linux-g++") - << FileName::fromString(QString::fromLatin1("linux-g++-") + QString::number(m_targetAbi.wordWidth())); + return {"linux-g++", "linux-g++-" + QString::number(m_targetAbi.wordWidth())}; } - return FileNameList() << FileName::fromString(QString::fromLatin1("linux-g++-") + QString::number(m_targetAbi.wordWidth())); + return {"linux-g++-" + QString::number(m_targetAbi.wordWidth())}; } if (abi.os() == Abi::BsdOS && abi.osFlavor() == Abi::FreeBsdFlavor) - return FileNameList() << FileName::fromLatin1("freebsd-g++"); + return {"freebsd-g++"}; - return FileNameList(); + return {}; } -QString GccToolChain::makeCommand(const Environment &environment) const +FilePath GccToolChain::makeCommand(const Environment &environment) const { - QString make = "make"; - FileName tmp = environment.searchInPath(make); - return tmp.isEmpty() ? make : tmp.toString(); + const FilePath tmp = environment.searchInPath("make"); + return tmp.isEmpty() ? FilePath::fromString("make") : tmp; } IOutputParser *GccToolChain::outputParser() const @@ -682,7 +703,7 @@ IOutputParser *GccToolChain::outputParser() const return new GccParser; } -void GccToolChain::resetToolChain(const FileName &path) +void GccToolChain::resetToolChain(const FilePath &path) { bool resetDisplayName = (displayName() == defaultDisplayName()); @@ -707,7 +728,7 @@ void GccToolChain::resetToolChain(const FileName &path) toolChainUpdated(); } -FileName GccToolChain::compilerCommand() const +FilePath GccToolChain::compilerCommand() const { return m_compilerCommand; } @@ -751,11 +772,6 @@ QStringList GccToolChain::platformLinkerFlags() const return m_platformLinkerFlags; } -ToolChain *GccToolChain::clone() const -{ - return new GccToolChain(*this); -} - QVariantMap GccToolChain::toMap() const { QVariantMap data = ToolChain::toMap(); @@ -764,8 +780,7 @@ QVariantMap GccToolChain::toMap() const data.insert(compilerPlatformLinkerFlagsKeyC, m_platformLinkerFlags); data.insert(targetAbiKeyC, m_targetAbi.toString()); data.insert(originalTargetTripleKeyC, m_originalTargetTriple); - QStringList abiList = Utils::transform(m_supportedAbis, &Abi::toString); - data.insert(supportedAbisKeyC, abiList); + data.insert(supportedAbisKeyC, Utils::transform<QStringList>(m_supportedAbis, &Abi::toString)); return data; } @@ -774,7 +789,7 @@ bool GccToolChain::fromMap(const QVariantMap &data) if (!ToolChain::fromMap(data)) return false; - m_compilerCommand = FileName::fromString(data.value(compilerCommandKeyC).toString()); + m_compilerCommand = FilePath::fromString(data.value(compilerCommandKeyC).toString()); m_platformCodeGenFlags = data.value(compilerPlatformCodeGenFlagsKeyC).toStringList(); m_platformLinkerFlags = data.value(compilerPlatformLinkerFlagsKeyC).toStringList(); const QString targetAbiString = data.value(targetAbiKeyC).toString(); @@ -850,168 +865,154 @@ QString GccToolChain::detectVersion() const GccToolChainFactory::GccToolChainFactory() { setDisplayName(tr("GCC")); -} - -QSet<Core::Id> GccToolChainFactory::supportedLanguages() const -{ - return {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}; -} - -bool GccToolChainFactory::canCreate() -{ - return true; -} - -ToolChain *GccToolChainFactory::create(Core::Id language) -{ - ToolChain *tc = createToolChain(false); - tc->setLanguage(language); - return tc; -} - -void GccToolChainFactory::versionProbe(const QString &name, Core::Id language, Core::Id type, - QList<ToolChain *> &tcs, QList<ToolChain *> &known, - const QSet<QString> &filteredNames) -{ - if (!HostOsInfo::isLinuxHost()) - return; - const QRegularExpression regexp(binaryRegexp); - for (const QString &dir : QStringList({ "/usr/bin", "/usr/local/bin" })) { - QDir binDir(dir); - for (const QString &entry : binDir.entryList( - {"*-" + name, name + "-*", "*-" + name + "-*"}, - QDir::Files | QDir::Executable)) { - const QString fileName = FileName::fromString(entry).fileName(); - if (filteredNames.contains(fileName)) - continue; - const QRegularExpressionMatch match = regexp.match(fileName); - if (!match.hasMatch()) - continue; - const bool isNative = fileName.startsWith(name); - const Abi abi = isNative ? Abi::hostAbi() : Abi(); - tcs.append(autoDetectToolchains(compilerPathFromEnvironment(entry), abi, language, type, - known)); - known.append(tcs); - } - } + setSupportedToolChainType(Constants::GCC_TOOLCHAIN_TYPEID); + setSupportedLanguages({Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}); + setToolchainConstructor([] { return new GccToolChain; }); + setUserCreatable(true); } QList<ToolChain *> GccToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown) { QList<ToolChain *> tcs; QList<ToolChain *> known = alreadyKnown; - tcs.append(autoDetectToolchains(compilerPathFromEnvironment("g++"), Abi::hostAbi(), - Constants::CXX_LANGUAGE_ID, Constants::GCC_TOOLCHAIN_TYPEID, - alreadyKnown)); - tcs.append(autoDetectToolchains(compilerPathFromEnvironment("gcc"), Abi::hostAbi(), - Constants::C_LANGUAGE_ID, Constants::GCC_TOOLCHAIN_TYPEID, - alreadyKnown)); - known.append(tcs); - versionProbe("g++", Constants::CXX_LANGUAGE_ID, Constants::GCC_TOOLCHAIN_TYPEID, tcs, known); - versionProbe("gcc", Constants::C_LANGUAGE_ID, Constants::GCC_TOOLCHAIN_TYPEID, tcs, known, - {"c89-gcc", "c99-gcc"}); - + static const auto tcChecker = [](const ToolChain *tc) { + return tc->targetAbi().osFlavor() != Abi::WindowsMSysFlavor + && tc->compilerCommand().fileName() != "c89-gcc" + && tc->compilerCommand().fileName() != "c99-gcc"; + }; + tcs.append(autoDetectToolchains("g++", DetectVariants::Yes, Constants::CXX_LANGUAGE_ID, + Constants::GCC_TOOLCHAIN_TYPEID, alreadyKnown, tcChecker)); + tcs.append(autoDetectToolchains("gcc", DetectVariants::Yes, Constants::C_LANGUAGE_ID, + Constants::GCC_TOOLCHAIN_TYPEID, alreadyKnown, tcChecker)); return tcs; } -QList<ToolChain *> GccToolChainFactory::autoDetect(const FileName &compilerPath, const Core::Id &language) +QList<ToolChain *> GccToolChainFactory::autoDetect(const FilePath &compilerPath, const Core::Id &language) { const QString fileName = compilerPath.fileName(); if ((language == Constants::C_LANGUAGE_ID && (fileName.startsWith("gcc") || fileName.endsWith("gcc"))) || (language == Constants::CXX_LANGUAGE_ID && (fileName.startsWith("g++") || fileName.endsWith("g++")))) - return autoDetectToolChain(compilerPath, language); + return autoDetectToolChain(compilerPath, language, [](const ToolChain *tc) { + return tc->targetAbi().osFlavor() != Abi::WindowsMSysFlavor; + }); return QList<ToolChain *>(); } -// Used by the ToolChainManager to restore user-generated tool chains -bool GccToolChainFactory::canRestore(const QVariantMap &data) -{ - return typeIdFromMap(data) == Constants::GCC_TOOLCHAIN_TYPEID; -} - -ToolChain *GccToolChainFactory::restore(const QVariantMap &data) -{ - GccToolChain *tc = createToolChain(false); - if (tc->fromMap(data)) - return tc; - - delete tc; - return nullptr; -} - -GccToolChain *GccToolChainFactory::createToolChain(bool autoDetect) -{ - return new GccToolChain(autoDetect ? ToolChain::AutoDetection : ToolChain::ManualDetection); -} - -Utils::FileName GccToolChainFactory::compilerPathFromEnvironment(const QString &compilerName) -{ - Environment systemEnvironment = Environment::systemEnvironment(); - return systemEnvironment.searchInPath(compilerName); -} - -QList<ToolChain *> GccToolChainFactory::autoDetectToolchains(const FileName &compilerPath, - const Abi &requiredAbi, - Core::Id language, - const Core::Id requiredTypeId, - const QList<ToolChain *> &alreadyKnown) +QList<ToolChain *> GccToolChainFactory::autoDetectToolchains( + const QString &compilerName, DetectVariants detectVariants, Core::Id language, + const Core::Id requiredTypeId, const QList<ToolChain *> &alreadyKnown, + const ToolchainChecker &checker) { - QList<ToolChain *> result; - - if (compilerPath.isEmpty()) - return result; + FilePathList compilerPaths; + QFileInfo fi(compilerName); + if (fi.isAbsolute()) { + if (fi.isFile()) + compilerPaths << FilePath::fromString(compilerName); + } else { + const FilePathList searchPaths = Environment::systemEnvironment().path(); + for (const FilePath &dir : searchPaths) { + static const QRegularExpression regexp(binaryRegexp); + QDir binDir(dir.toString()); + QStringList nameFilters(compilerName); + if (detectVariants == DetectVariants::Yes) { + nameFilters + << compilerName + "-[1-9]*" // "clang-8", "gcc-5" + << ("*-*-*-" + compilerName) // "arm-none-eabi-gcc" + << ("*-*-*-" + compilerName + "-[1-9]*") // "arm-none-eabi-gcc-9.1.0" + << ("*-*-*-*-" + compilerName) // "x86_64-pc-linux-gnu-gcc" + << ("*-*-*-*-" + compilerName + + "-[1-9]*"); // "x86_64-pc-linux-gnu-gcc-7.4.1" + } + nameFilters = transform(nameFilters, [](const QString &baseName) { + return HostOsInfo::withExecutableSuffix(baseName); + }); + const QStringList fileNames = binDir.entryList(nameFilters, + QDir::Files | QDir::Executable); + for (const QString &fileName : fileNames) { + if (fileName != compilerName && + !regexp.match(QFileInfo(fileName).completeBaseName()).hasMatch()) { + continue; + } + compilerPaths << FilePath::fromString(binDir.filePath(fileName)); + } + } + } - result = Utils::filtered(alreadyKnown, [=](ToolChain *tc) { - return tc->typeId() == requiredTypeId && tc->compilerCommand() == compilerPath - && tc->language() == language; + QList<ToolChain *> existingCandidates + = filtered(alreadyKnown, [requiredTypeId, language, &checker](const ToolChain *tc) { + if (tc->typeId() != requiredTypeId) + return false; + if (tc->language() != language) + return false; + if (checker && !checker(tc)) + return false; + return true; }); - if (!result.isEmpty()) { - for (ToolChain *tc : result) { - if (tc->isAutoDetected()) - tc->setLanguage(language); + QList<ToolChain *> result; + for (const FilePath &compilerPath : compilerPaths) { + bool alreadyExists = false; + for (ToolChain * const existingTc : existingCandidates) { + // We have a match if the existing toolchain ultimately refers to the same file + // as the candidate path, either directly or via a hard or soft link. + // Exceptions: + // - clang++ is often a soft link to clang, but behaves differently. + // - ccache and icecc also create soft links that must not be followed here. + bool existingTcMatches = false; + const FilePath existingCommand = existingTc->compilerCommand(); + if ((requiredTypeId == Constants::CLANG_TOOLCHAIN_TYPEID + && language == Constants::CXX_LANGUAGE_ID + && !existingCommand.fileName().contains("clang++")) + || compilerPath.toString().contains("icecc") + || compilerPath.toString().contains("ccache")) { + existingTcMatches = existingCommand == compilerPath; + } else { + existingTcMatches = Environment::systemEnvironment().isSameExecutable( + existingCommand.toString(), compilerPath.toString()) + || (HostOsInfo::isWindowsHost() && existingCommand.toFileInfo().size() + == compilerPath.toFileInfo().size()); + } + if (existingTcMatches) { + if (!result.contains(existingTc)) + result << existingTc; + alreadyExists = true; + } + } + if (!alreadyExists) { + const QList<ToolChain *> newToolchains = autoDetectToolChain(compilerPath, language, + checker); + result << newToolchains; + existingCandidates << newToolchains; } - return result; } - result = autoDetectToolChain(compilerPath, language, requiredAbi); - return result; } -QList<ToolChain *> GccToolChainFactory::autoDetectToolChain(const FileName &compilerPath, +QList<ToolChain *> GccToolChainFactory::autoDetectToolChain(const FilePath &compilerPath, const Core::Id language, - const Abi &requiredAbi) + const ToolchainChecker &checker) { QList<ToolChain *> result; Environment systemEnvironment = Environment::systemEnvironment(); GccToolChain::addCommandPathToEnvironment(compilerPath, systemEnvironment); - const FileName localCompilerPath = findLocalCompiler(compilerPath, systemEnvironment); + const FilePath localCompilerPath = findLocalCompiler(compilerPath, systemEnvironment); Macros macros = gccPredefinedMacros(localCompilerPath, gccPredefinedMacrosOptions(language), systemEnvironment.toStringList()); const GccToolChain::DetectedAbisResult detectedAbis = guessGccAbi(localCompilerPath, systemEnvironment.toStringList(), macros); - - const QList<Abi> abiList = detectedAbis.supportedAbis; - if (!requiredAbi.isNull() && !abiList.contains(requiredAbi)) { - if (requiredAbi.wordWidth() != 64 - || !abiList.contains(Abi(requiredAbi.architecture(), requiredAbi.os(), requiredAbi.osFlavor(), - requiredAbi.binaryFormat(), 32))) { - return result; - } - } - - for (const Abi &abi : abiList) { - std::unique_ptr<GccToolChain> tc(createToolChain(true)); + for (const Abi &abi : detectedAbis.supportedAbis) { + std::unique_ptr<GccToolChain> tc(dynamic_cast<GccToolChain *>(create())); if (!tc) return result; tc->setLanguage(language); - tc->m_predefinedMacrosCache + tc->setDetection(ToolChain::AutoDetection); + tc->predefinedMacrosCache() ->insert(QStringList(), ToolChain::MacroInspectionReport{macros, ToolChain::languageVersion(language, macros)}); @@ -1020,8 +1021,8 @@ QList<ToolChain *> GccToolChainFactory::autoDetectToolChain(const FileName &comp tc->setTargetAbi(abi); tc->setOriginalTargetTriple(detectedAbis.originalTargetTriple); tc->setDisplayName(tc->defaultDisplayName()); // reset displayname - - result.append(tc.release()); + if (!checker || checker(tc.get())) + result.append(tc.release()); } return result; } @@ -1085,7 +1086,7 @@ void GccToolChainConfigWidget::applyImpl() if (m_macros.isEmpty()) return; - tc->m_predefinedMacrosCache + tc->predefinedMacrosCache() ->insert(tc->platformCodeGenFlags(), ToolChain::MacroInspectionReport{m_macros, ToolChain::languageVersion(tc->language(), @@ -1153,8 +1154,8 @@ void GccToolChainConfigWidget::handleCompilerCommandChange() bool haveCompiler = false; Abi currentAbi = m_abiWidget->currentAbi(); bool customAbi = m_abiWidget->isCustomAbi() && m_abiWidget->isEnabled(); - FileName path = m_compilerCommand->fileName(); - QList<Abi> abiList; + FilePath path = m_compilerCommand->fileName(); + Abis abiList; if (!path.isEmpty()) { QFileInfo fi(path.toFileInfo()); @@ -1165,7 +1166,7 @@ void GccToolChainConfigWidget::handleCompilerCommandChange() GccToolChain::addCommandPathToEnvironment(path, env); QStringList args = gccPredefinedMacrosOptions(Constants::CXX_LANGUAGE_ID) + splitString(m_platformCodeGenFlagsLineEdit->text()); - const FileName localCompilerPath = findLocalCompiler(path, env); + const FilePath localCompilerPath = findLocalCompiler(path, env); m_macros = gccPredefinedMacros(localCompilerPath, args, env.toStringList()); abiList = guessGccAbi(localCompilerPath, env.toStringList(), m_macros, splitString(m_platformCodeGenFlagsLineEdit->text())).supportedAbis; @@ -1263,40 +1264,41 @@ void ClangToolChain::syncAutodetectedWithParentToolchains() }); } -ClangToolChain::ClangToolChain(Detection d) : - GccToolChain(Constants::CLANG_TOOLCHAIN_TYPEID, d) +ClangToolChain::ClangToolChain() : + GccToolChain(Constants::CLANG_TOOLCHAIN_TYPEID) { syncAutodetectedWithParentToolchains(); } -ClangToolChain::ClangToolChain(Core::Id typeId, ToolChain::Detection d) : - GccToolChain(typeId, d) +ClangToolChain::ClangToolChain(Core::Id typeId) : + GccToolChain(typeId) { syncAutodetectedWithParentToolchains(); } -ClangToolChain::ClangToolChain(const ClangToolChain &other) - : GccToolChain(other) - , m_parentToolChainId(other.m_parentToolChainId) -{} +ClangToolChain::~ClangToolChain() +{ + QObject::disconnect(m_thisToolchainRemovedConnection); + QObject::disconnect(m_mingwToolchainAddedConnection); +} QString ClangToolChain::typeDisplayName() const { return ClangToolChainFactory::tr("Clang"); } -QString ClangToolChain::makeCommand(const Environment &environment) const +FilePath ClangToolChain::makeCommand(const Environment &environment) const { const QStringList makes = HostOsInfo::isWindowsHost() ? QStringList({"mingw32-make.exe", "make.exe"}) : QStringList({"make"}); - FileName tmp; + FilePath tmp; for (const QString &make : makes) { tmp = environment.searchInPath(make); if (!tmp.isEmpty()) - return tmp.toString(); + return tmp; } - return makes.first(); + return FilePath::fromString(makes.first()); } /** @@ -1323,23 +1325,16 @@ WarningFlags ClangToolChain::warningFlags(const QStringList &cflags) const return flags; } -FileNameList ClangToolChain::suggestedMkspecList() const +QStringList ClangToolChain::suggestedMkspecList() const { - Abi abi = targetAbi(); - if (abi.os() == Abi::DarwinOS) { - return FileNameList() - << FileName::fromLatin1("macx-clang") - << FileName::fromLatin1("macx-clang-32") - << FileName::fromLatin1("unsupported/macx-clang") - << FileName::fromLatin1("macx-ios-clang"); - } else if (abi.os() == Abi::LinuxOS) { - return FileNameList() - << FileName::fromLatin1("linux-clang") - << FileName::fromLatin1("unsupported/linux-clang"); - } else if (abi.os() == Abi::WindowsOS) { - return FileNameList() << FileName::fromLatin1("win32-clang-g++"); - } - return FileNameList(); // Note: Not supported by Qt yet, so default to the mkspec the Qt was build with + const Abi abi = targetAbi(); + if (abi.os() == Abi::DarwinOS) + return {"macx-clang", "macx-clang-32", "unsupported/macx-clang", "macx-ios-clang"}; + if (abi.os() == Abi::LinuxOS) + return {"linux-clang", "unsupported/linux-clang"}; + if (abi.os() == Abi::WindowsOS) + return {"win32-clang-g++"}; + return {}; // Note: Not supported by Qt yet, so default to the mkspec the Qt was build with } void ClangToolChain::addToEnvironment(Environment &env) const @@ -1372,10 +1367,39 @@ QString ClangToolChain::sysRoot() const if (!parentTC) return QString(); - const FileName mingwCompiler = parentTC->compilerCommand(); + const FilePath mingwCompiler = parentTC->compilerCommand(); return mingwCompiler.parentDir().parentDir().toString(); } +ToolChain::BuiltInHeaderPathsRunner ClangToolChain::createBuiltInHeaderPathsRunner() const +{ + // Using a clean environment breaks ccache/distcc/etc. + Environment env = Environment::systemEnvironment(); + addToEnvironment(env); + + // This runner must be thread-safe! + return [env, + compilerCommand = m_compilerCommand, + platformCodeGenFlags = m_platformCodeGenFlags, + reinterpretOptions = m_optionsReinterpreter, + headerCache = headerPathsCache(), + languageId = language(), + extraHeaderPathsFunction = m_extraHeaderPathsFunction](const QStringList &flags, + const QString &sysRoot, + const QString &target) { + return builtInHeaderPaths(env, + compilerCommand, + platformCodeGenFlags, + reinterpretOptions, + headerCache, + languageId, + extraHeaderPathsFunction, + flags, + sysRoot, + target); + }; +} + std::unique_ptr<ToolChainConfigWidget> ClangToolChain::createConfigurationWidget() { return std::make_unique<ClangToolChainConfigWidget>(this); @@ -1408,11 +1432,6 @@ IOutputParser *ClangToolChain::outputParser() const return new ClangParser; } -ToolChain *ClangToolChain::clone() const -{ - return new ClangToolChain(*this); -} - // -------------------------------------------------------------------------- // ClangToolChainFactory // -------------------------------------------------------------------------- @@ -1420,12 +1439,9 @@ ToolChain *ClangToolChain::clone() const ClangToolChainFactory::ClangToolChainFactory() { setDisplayName(tr("Clang")); -} - -QSet<Core::Id> ClangToolChainFactory::supportedLanguages() const -{ - return {Constants::CXX_LANGUAGE_ID, - Constants::C_LANGUAGE_ID}; + setSupportedToolChainType(Constants::CLANG_TOOLCHAIN_TYPEID); + setSupportedLanguages({Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}); + setToolchainConstructor([] { return new ClangToolChain; }); } QList<ToolChain *> ClangToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown) @@ -1433,33 +1449,25 @@ QList<ToolChain *> ClangToolChainFactory::autoDetect(const QList<ToolChain *> &a QList<ToolChain *> tcs; QList<ToolChain *> known = alreadyKnown; - const Abi hostAbi = Abi::hostAbi(); - tcs.append(autoDetectToolchains(compilerPathFromEnvironment("clang++"), hostAbi, - Constants::CXX_LANGUAGE_ID, Constants::CLANG_TOOLCHAIN_TYPEID, - alreadyKnown)); - tcs.append(autoDetectToolchains(compilerPathFromEnvironment("clang"), hostAbi, - Constants::C_LANGUAGE_ID, Constants::CLANG_TOOLCHAIN_TYPEID, - alreadyKnown)); + tcs.append(autoDetectToolchains("clang++", DetectVariants::Yes, Constants::CXX_LANGUAGE_ID, + Constants::CLANG_TOOLCHAIN_TYPEID, alreadyKnown)); + tcs.append(autoDetectToolchains("clang", DetectVariants::Yes, Constants::C_LANGUAGE_ID, + Constants::CLANG_TOOLCHAIN_TYPEID, alreadyKnown)); known.append(tcs); - versionProbe("clang++", Constants::CXX_LANGUAGE_ID, Constants::CLANG_TOOLCHAIN_TYPEID, tcs, known); - versionProbe("clang", Constants::C_LANGUAGE_ID, Constants::CLANG_TOOLCHAIN_TYPEID, tcs, known); - const FileName compilerPath = FileName::fromString(Core::ICore::clangExecutable(CLANG_BINDIR)); + const FilePath compilerPath = FilePath::fromString(Core::ICore::clangExecutable(CLANG_BINDIR)); if (!compilerPath.isEmpty()) { - const FileName clang = compilerPath.parentDir().appendPath( + const FilePath clang = compilerPath.parentDir().pathAppended( HostOsInfo::withExecutableSuffix("clang")); - tcs.append(autoDetectToolchains(clang, - hostAbi, Constants::CXX_LANGUAGE_ID, - Constants::CLANG_TOOLCHAIN_TYPEID, alreadyKnown)); - tcs.append(autoDetectToolchains(clang, - hostAbi, Constants::C_LANGUAGE_ID, - Constants::CLANG_TOOLCHAIN_TYPEID, alreadyKnown)); + tcs.append(autoDetectToolchains(clang.toString(), DetectVariants::No, + Constants::C_LANGUAGE_ID, Constants::CLANG_TOOLCHAIN_TYPEID, + tcs)); } return tcs; } -QList<ToolChain *> ClangToolChainFactory::autoDetect(const FileName &compilerPath, const Core::Id &language) +QList<ToolChain *> ClangToolChainFactory::autoDetect(const FilePath &compilerPath, const Core::Id &language) { const QString fileName = compilerPath.fileName(); if ((language == Constants::C_LANGUAGE_ID && fileName.startsWith("clang") && !fileName.startsWith("clang++")) @@ -1468,16 +1476,6 @@ QList<ToolChain *> ClangToolChainFactory::autoDetect(const FileName &compilerPat return QList<ToolChain *>(); } -bool ClangToolChainFactory::canRestore(const QVariantMap &data) -{ - return typeIdFromMap(data) == Constants::CLANG_TOOLCHAIN_TYPEID; -} - -GccToolChain *ClangToolChainFactory::createToolChain(bool autoDetect) -{ - return new ClangToolChain(autoDetect ? ToolChain::AutoDetection : ToolChain::ManualDetection); -} - ClangToolChainConfigWidget::ClangToolChainConfigWidget(ClangToolChain *tc) : GccToolChainConfigWidget(tc) { @@ -1594,8 +1592,8 @@ void ClangToolChainConfigWidget::makeReadOnlyImpl() // MingwToolChain // -------------------------------------------------------------------------- -MingwToolChain::MingwToolChain(Detection d) : - GccToolChain(Constants::MINGW_TOOLCHAIN_TYPEID, d) +MingwToolChain::MingwToolChain() : + GccToolChain(Constants::MINGW_TOOLCHAIN_TYPEID) { } QString MingwToolChain::typeDisplayName() const @@ -1603,40 +1601,30 @@ QString MingwToolChain::typeDisplayName() const return MingwToolChainFactory::tr("MinGW"); } -FileNameList MingwToolChain::suggestedMkspecList() const +QStringList MingwToolChain::suggestedMkspecList() const { if (HostOsInfo::isWindowsHost()) - return FileNameList() << FileName::fromLatin1("win32-g++"); + return {"win32-g++"}; if (HostOsInfo::isLinuxHost()) { if (version().startsWith("4.6.")) - return FileNameList() - << FileName::fromLatin1("win32-g++-4.6-cross") - << FileName::fromLatin1("unsupported/win32-g++-4.6-cross"); - else - return FileNameList() - << FileName::fromLatin1("win32-g++-cross") - << FileName::fromLatin1("unsupported/win32-g++-cross"); + return {"win32-g++-4.6-cross", "unsupported/win32-g++-4.6-cross"}; + return {"win32-g++-cross", "unsupported/win32-g++-cross"}; } - return FileNameList(); + return {}; } -QString MingwToolChain::makeCommand(const Environment &environment) const +FilePath MingwToolChain::makeCommand(const Environment &environment) const { const QStringList makes = HostOsInfo::isWindowsHost() ? QStringList({"mingw32-make.exe", "make.exe"}) : QStringList({"make"}); - FileName tmp; + FilePath tmp; foreach (const QString &make, makes) { tmp = environment.searchInPath(make); if (!tmp.isEmpty()) - return tmp.toString(); + return tmp; } - return makes.first(); -} - -ToolChain *MingwToolChain::clone() const -{ - return new MingwToolChain(*this); + return FilePath::fromString(makes.first()); } // -------------------------------------------------------------------------- @@ -1646,56 +1634,45 @@ ToolChain *MingwToolChain::clone() const MingwToolChainFactory::MingwToolChainFactory() { setDisplayName(tr("MinGW")); -} - -QSet<Core::Id> MingwToolChainFactory::supportedLanguages() const -{ - return {Constants::CXX_LANGUAGE_ID, - Constants::C_LANGUAGE_ID}; + setSupportedToolChainType(Constants::MINGW_TOOLCHAIN_TYPEID); + setSupportedLanguages({Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}); + setToolchainConstructor([] { return new MingwToolChain; }); } QList<ToolChain *> MingwToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown) { Abi ha = Abi::hostAbi(); ha = Abi(ha.architecture(), Abi::WindowsOS, Abi::WindowsMSysFlavor, Abi::PEFormat, ha.wordWidth()); + static const auto tcChecker = [](const ToolChain *tc) { + return tc->targetAbi().osFlavor() == Abi::WindowsMSysFlavor; + }; QList<ToolChain *> result = autoDetectToolchains( - compilerPathFromEnvironment("g++"), ha, Constants::CXX_LANGUAGE_ID, - Constants::MINGW_TOOLCHAIN_TYPEID, alreadyKnown); - result += autoDetectToolchains( - compilerPathFromEnvironment("gcc"), ha, Constants::C_LANGUAGE_ID, - Constants::MINGW_TOOLCHAIN_TYPEID, alreadyKnown); + "g++", DetectVariants::Yes, Constants::CXX_LANGUAGE_ID, + Constants::MINGW_TOOLCHAIN_TYPEID, alreadyKnown, tcChecker); + result += autoDetectToolchains("gcc", DetectVariants::Yes, Constants::C_LANGUAGE_ID, + Constants::MINGW_TOOLCHAIN_TYPEID, alreadyKnown, tcChecker); return result; } -QList<ToolChain *> MingwToolChainFactory::autoDetect(const FileName &compilerPath, const Core::Id &language) +QList<ToolChain *> MingwToolChainFactory::autoDetect(const FilePath &compilerPath, const Core::Id &language) { - Abi ha = Abi::hostAbi(); - ha = Abi(ha.architecture(), Abi::WindowsOS, Abi::WindowsMSysFlavor, Abi::PEFormat, ha.wordWidth()); const QString fileName = compilerPath.fileName(); if ((language == Constants::C_LANGUAGE_ID && (fileName.startsWith("gcc") || fileName.endsWith("gcc"))) || (language == Constants::CXX_LANGUAGE_ID && (fileName.startsWith("g++") || fileName.endsWith("g++")))) - return autoDetectToolChain(compilerPath, language, ha); + return autoDetectToolChain(compilerPath, language, [](const ToolChain *tc) { + return tc->targetAbi().osFlavor() == Abi::WindowsMSysFlavor; + }); return QList<ToolChain *>(); } -bool MingwToolChainFactory::canRestore(const QVariantMap &data) -{ - return typeIdFromMap(data) == Constants::MINGW_TOOLCHAIN_TYPEID; -} - -GccToolChain *MingwToolChainFactory::createToolChain(bool autoDetect) -{ - return new MingwToolChain(autoDetect ? ToolChain::AutoDetection : ToolChain::ManualDetection); -} - // -------------------------------------------------------------------------- // LinuxIccToolChain // -------------------------------------------------------------------------- -LinuxIccToolChain::LinuxIccToolChain(Detection d) : - GccToolChain(Constants::LINUXICC_TOOLCHAIN_TYPEID, d) +LinuxIccToolChain::LinuxIccToolChain() : + GccToolChain(Constants::LINUXICC_TOOLCHAIN_TYPEID) { } QString LinuxIccToolChain::typeDisplayName() const @@ -1730,15 +1707,9 @@ IOutputParser *LinuxIccToolChain::outputParser() const return new LinuxIccParser; } -FileNameList LinuxIccToolChain::suggestedMkspecList() const +QStringList LinuxIccToolChain::suggestedMkspecList() const { - return FileNameList() - << FileName::fromString(QString::fromLatin1("linux-icc-") + QString::number(targetAbi().wordWidth())); -} - -ToolChain *LinuxIccToolChain::clone() const -{ - return new LinuxIccToolChain(*this); + return {QString("linux-icc-%1").arg(targetAbi().wordWidth())}; } // -------------------------------------------------------------------------- @@ -1748,26 +1719,22 @@ ToolChain *LinuxIccToolChain::clone() const LinuxIccToolChainFactory::LinuxIccToolChainFactory() { setDisplayName(tr("Linux ICC")); -} - -QSet<Core::Id> LinuxIccToolChainFactory::supportedLanguages() const -{ - return {Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}; + setSupportedToolChainType(Constants::LINUXICC_TOOLCHAIN_TYPEID); + setSupportedLanguages({Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}); + setToolchainConstructor([] { return new LinuxIccToolChain; }); } QList<ToolChain *> LinuxIccToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown) { QList<ToolChain *> result - = autoDetectToolchains(compilerPathFromEnvironment("icpc"), - Abi::hostAbi(), Constants::CXX_LANGUAGE_ID, + = autoDetectToolchains("icpc", DetectVariants::No, Constants::CXX_LANGUAGE_ID, Constants::LINUXICC_TOOLCHAIN_TYPEID, alreadyKnown); - result += autoDetectToolchains(compilerPathFromEnvironment("icc"), - Abi::hostAbi(), Constants::C_LANGUAGE_ID, + result += autoDetectToolchains("icc", DetectVariants::Yes, Constants::C_LANGUAGE_ID, Constants::LINUXICC_TOOLCHAIN_TYPEID, alreadyKnown); return result; } -QList<ToolChain *> LinuxIccToolChainFactory::autoDetect(const FileName &compilerPath, const Core::Id &language) +QList<ToolChain *> LinuxIccToolChainFactory::autoDetect(const FilePath &compilerPath, const Core::Id &language) { const QString fileName = compilerPath.fileName(); if ((language == Constants::CXX_LANGUAGE_ID && fileName.startsWith("icpc")) || @@ -1776,16 +1743,6 @@ QList<ToolChain *> LinuxIccToolChainFactory::autoDetect(const FileName &compiler return {}; } -bool LinuxIccToolChainFactory::canRestore(const QVariantMap &data) -{ - return typeIdFromMap(data) == Constants::LINUXICC_TOOLCHAIN_TYPEID; -} - -GccToolChain *LinuxIccToolChainFactory::createToolChain(bool autoDetect) -{ - return new LinuxIccToolChain(autoDetect ? ToolChain::AutoDetection : ToolChain::ManualDetection); -} - GccToolChain::WarningFlagAdder::WarningFlagAdder(const QString &flag, WarningFlags &flags) : m_flags(flags) { @@ -1873,7 +1830,7 @@ void ProjectExplorerPlugin::testGccAbiGuessing_data() QTest::newRow("Linux 3 (64bit intel)") << QString::fromLatin1("x86_64-linux-gnu") << QByteArray("#define __SIZEOF_SIZE_T__ 8\n") - << QStringList({"x86-linux-generic-elf-64bit", "x86-linux-generic-elf-32bit"}); + << QStringList("x86-linux-generic-elf-64bit"); QTest::newRow("Linux 3 (64bit intel -- non 64bit)") << QString::fromLatin1("x86_64-linux-gnu") << QByteArray("#define __SIZEOF_SIZE_T__ 4\n") @@ -1885,11 +1842,11 @@ void ProjectExplorerPlugin::testGccAbiGuessing_data() QTest::newRow("Linux 5 (QTCREATORBUG-4690)") // from QTCREATORBUG-4690 << QString::fromLatin1("x86_64-redhat-linux6E") << QByteArray("#define __SIZEOF_SIZE_T__ 8\n") - << QStringList({"x86-linux-generic-elf-64bit", "x86-linux-generic-elf-32bit"}); + << QStringList("x86-linux-generic-elf-64bit"); QTest::newRow("Linux 6 (QTCREATORBUG-4690)") // from QTCREATORBUG-4690 << QString::fromLatin1("x86_64-redhat-linux") << QByteArray("#define __SIZEOF_SIZE_T__ 8\n") - << QStringList({"x86-linux-generic-elf-64bit", "x86-linux-generic-elf-32bit"}); + << QStringList("x86-linux-generic-elf-64bit"); QTest::newRow("Linux 7 (arm)") << QString::fromLatin1("armv5tl-montavista-linux-gnueabi") << QByteArray("#define __SIZEOF_SIZE_T__ 4\n") @@ -1918,7 +1875,7 @@ void ProjectExplorerPlugin::testGccAbiGuessing_data() QTest::newRow("Mingw 2 (64bit)") << QString::fromLatin1("i686-w64-mingw32") << QByteArray("#define __SIZEOF_SIZE_T__ 8\r\n") - << QStringList({"x86-windows-msys-pe-64bit", "x86-windows-msys-pe-32bit"}); + << QStringList({"x86-windows-msys-pe-64bit"}); QTest::newRow("Mingw 3 (32 bit)") << QString::fromLatin1("mingw32") << QByteArray("#define __SIZEOF_SIZE_T__ 4\r\n") @@ -1926,7 +1883,7 @@ void ProjectExplorerPlugin::testGccAbiGuessing_data() QTest::newRow("Cross Mingw 1 (64bit)") << QString::fromLatin1("amd64-mingw32msvc") << QByteArray("#define __SIZEOF_SIZE_T__ 8\r\n") - << QStringList({"x86-windows-msys-pe-64bit", "x86-windows-msys-pe-32bit"}); + << QStringList({"x86-windows-msys-pe-64bit"}); QTest::newRow("Cross Mingw 2 (32bit)") << QString::fromLatin1("i586-mingw32msvc") << QByteArray("#define __SIZEOF_SIZE_T__ 4\r\n") @@ -1934,11 +1891,11 @@ void ProjectExplorerPlugin::testGccAbiGuessing_data() QTest::newRow("Clang 1: windows") << QString::fromLatin1("x86_64-pc-win32") << QByteArray("#define __SIZEOF_SIZE_T__ 8\r\n") - << QStringList({"x86-windows-msys-pe-64bit", "x86-windows-msys-pe-32bit"}); + << QStringList("x86-windows-msys-pe-64bit"); QTest::newRow("Clang 1: linux") << QString::fromLatin1("x86_64-unknown-linux-gnu") << QByteArray("#define __SIZEOF_SIZE_T__ 8\n") - << QStringList({"x86-linux-generic-elf-64bit", "x86-linux-generic-elf-32bit"}); + << QStringList("x86-linux-generic-elf-64bit"); QTest::newRow("Mac 1") << QString::fromLatin1("i686-apple-darwin10") << QByteArray("#define __SIZEOF_SIZE_T__ 8\n") @@ -1958,7 +1915,7 @@ void ProjectExplorerPlugin::testGccAbiGuessing_data() QTest::newRow("Intel 1") << QString::fromLatin1("86_64 x86_64 GNU/Linux") << QByteArray("#define __SIZEOF_SIZE_T__ 8\n") - << QStringList({"x86-linux-generic-elf-64bit", "x86-linux-generic-elf-32bit"}); + << QStringList("x86-linux-generic-elf-64bit"); QTest::newRow("FreeBSD 1") << QString::fromLatin1("i386-portbld-freebsd9.0") << QByteArray("#define __SIZEOF_SIZE_T__ 4\n") @@ -1975,7 +1932,7 @@ void ProjectExplorerPlugin::testGccAbiGuessing() QFETCH(QByteArray, macros); QFETCH(QStringList, abiList); - QList<Abi> al = guessGccAbi(input, ProjectExplorer::Macro::toMacros(macros)); + const Abis al = guessGccAbi(input, ProjectExplorer::Macro::toMacros(macros)); QCOMPARE(al.count(), abiList.count()); for (int i = 0; i < al.count(); ++i) QCOMPARE(al.at(i).toString(), abiList.at(i)); diff --git a/src/plugins/projectexplorer/gcctoolchain.h b/src/plugins/projectexplorer/gcctoolchain.h index b34614cde8..240ae34247 100644 --- a/src/plugins/projectexplorer/gcctoolchain.h +++ b/src/plugins/projectexplorer/gcctoolchain.h @@ -29,7 +29,6 @@ #include "projectexplorerconstants.h" #include "toolchain.h" -#include "toolchaincache.h" #include "abi.h" #include "headerpath.h" @@ -68,12 +67,12 @@ inline const QStringList gccPredefinedMacrosOptions(Core::Id languageId) class PROJECTEXPLORER_EXPORT GccToolChain : public ToolChain { public: - GccToolChain(Core::Id typeId, Detection d); + GccToolChain(Core::Id typeId); QString typeDisplayName() const override; Abi targetAbi() const override; QString originalTargetTriple() const override; QString version() const; - QList<Abi> supportedAbis() const override; + Abis supportedAbis() const override; void setTargetAbi(const Abi &); bool isValid() const override; @@ -86,11 +85,11 @@ public: BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner() const override; HeaderPaths builtInHeaderPaths(const QStringList &flags, - const Utils::FileName &sysRoot) const override; + const Utils::FilePath &sysRootPath) const override; void addToEnvironment(Utils::Environment &env) const override; - QString makeCommand(const Utils::Environment &environment) const override; - Utils::FileNameList suggestedMkspecList() const override; + Utils::FilePath makeCommand(const Utils::Environment &environment) const override; + QStringList suggestedMkspecList() const override; IOutputParser *outputParser() const override; QVariantMap toMap() const override; @@ -100,28 +99,25 @@ public: bool operator ==(const ToolChain &) const override; - void resetToolChain(const Utils::FileName &); - Utils::FileName compilerCommand() const override; + void resetToolChain(const Utils::FilePath &); + Utils::FilePath compilerCommand() const override; void setPlatformCodeGenFlags(const QStringList &); QStringList extraCodeModelFlags() const override; QStringList platformCodeGenFlags() const; void setPlatformLinkerFlags(const QStringList &); QStringList platformLinkerFlags() const; - ToolChain *clone() const override; - - static void addCommandPathToEnvironment(const Utils::FileName &command, Utils::Environment &env); + static void addCommandPathToEnvironment(const Utils::FilePath &command, Utils::Environment &env); class DetectedAbisResult { public: DetectedAbisResult() = default; - DetectedAbisResult(const QList<Abi> &supportedAbis, - const QString &originalTargetTriple = QString()) : + DetectedAbisResult(const Abis &supportedAbis, const QString &originalTargetTriple = {}) : supportedAbis(supportedAbis), originalTargetTriple(originalTargetTriple) { } - QList<Abi> supportedAbis; + Abis supportedAbis; QString originalTargetTriple; }; @@ -129,10 +125,8 @@ protected: using CacheItem = QPair<QStringList, Macros>; using GccCache = QVector<CacheItem>; - GccToolChain(const GccToolChain &); - - void setCompilerCommand(const Utils::FileName &path); - void setSupportedAbis(const QList<Abi> &m_abis); + void setCompilerCommand(const Utils::FilePath &path); + void setSupportedAbis(const Abis &abis); void setOriginalTargetTriple(const QString &targetTriple); void setMacroCache(const QStringList &allCxxflags, const Macros ¯oCache) const; Macros macroCache(const QStringList &allCxxflags) const; @@ -151,7 +145,18 @@ protected: using ExtraHeaderPathsFunction = std::function<void(HeaderPaths &)>; void initExtraHeaderPathsFunction(ExtraHeaderPathsFunction &&extraHeaderPathsFunction) const; - static HeaderPaths gccHeaderPaths(const Utils::FileName &gcc, const QStringList &args, + static HeaderPaths builtInHeaderPaths(const Utils::Environment &env, + const Utils::FilePath &compilerCommand, + const QStringList &platformCodeGenFlags, + OptionsReinterpreter reinterpretOptions, + HeaderPathsCache headerCache, + Core::Id languageId, + ExtraHeaderPathsFunction extraHeaderPathsFunction, + const QStringList &flags, + const QString &sysRoot, + const QString &originalTargetTriple); + + static HeaderPaths gccHeaderPaths(const Utils::FilePath &gcc, const QStringList &args, const QStringList &env); class WarningFlagAdder @@ -167,10 +172,9 @@ protected: bool m_doesEnable = false; bool m_triggered = false; }; - void toolChainUpdated() override; private: - explicit GccToolChain(Detection d); + explicit GccToolChain(); void updateSupportedAbis() const; static QStringList gccPrepareArguments(const QStringList &flags, @@ -179,22 +183,21 @@ private: Core::Id languageId, OptionsReinterpreter reinterpretOptions); - Utils::FileName m_compilerCommand; +protected: + Utils::FilePath m_compilerCommand; QStringList m_platformCodeGenFlags; QStringList m_platformLinkerFlags; OptionsReinterpreter m_optionsReinterpreter = [](const QStringList &v) { return v; }; + mutable ExtraHeaderPathsFunction m_extraHeaderPathsFunction = [](HeaderPaths &) {}; +private: Abi m_targetAbi; - mutable QList<Abi> m_supportedAbis; + mutable Abis m_supportedAbis; mutable QString m_originalTargetTriple; mutable HeaderPaths m_headerPaths; mutable QString m_version; - mutable std::shared_ptr<Cache<MacroInspectionReport, 64>> m_predefinedMacrosCache; - mutable std::shared_ptr<Cache<HeaderPaths>> m_headerPathsCache; - mutable ExtraHeaderPathsFunction m_extraHeaderPathsFunction = [](HeaderPaths &) {}; - friend class Internal::GccToolChainConfigWidget; friend class Internal::GccToolChainFactory; friend class ToolChainFactory; @@ -207,25 +210,26 @@ private: class PROJECTEXPLORER_EXPORT ClangToolChain : public GccToolChain { public: - explicit ClangToolChain(Detection d); - ClangToolChain(Core::Id typeId, Detection d); - ClangToolChain(const ClangToolChain &other); + ClangToolChain(); + explicit ClangToolChain(Core::Id typeId); + ~ClangToolChain() override; + QString typeDisplayName() const override; - QString makeCommand(const Utils::Environment &environment) const override; + Utils::FilePath makeCommand(const Utils::Environment &environment) const override; Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; WarningFlags warningFlags(const QStringList &cflags) const override; IOutputParser *outputParser() const override; - ToolChain *clone() const override; - - Utils::FileNameList suggestedMkspecList() const override; + QStringList suggestedMkspecList() const override; void addToEnvironment(Utils::Environment &env) const override; QString originalTargetTriple() const override; QString sysRoot() const override; + BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner() const override; + std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override; QVariantMap toMap() const override; @@ -253,14 +257,12 @@ class PROJECTEXPLORER_EXPORT MingwToolChain : public GccToolChain { public: QString typeDisplayName() const override; - QString makeCommand(const Utils::Environment &environment) const override; - - ToolChain *clone() const override; + Utils::FilePath makeCommand(const Utils::Environment &environment) const override; - Utils::FileNameList suggestedMkspecList() const override; + QStringList suggestedMkspecList() const override; private: - explicit MingwToolChain(Detection d); + MingwToolChain(); friend class Internal::MingwToolChainFactory; friend class ToolChainFactory; @@ -278,12 +280,10 @@ public: Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; IOutputParser *outputParser() const override; - ToolChain *clone() const override; - - Utils::FileNameList suggestedMkspecList() const override; + QStringList suggestedMkspecList() const override; private: - explicit LinuxIccToolChain(Detection d); + LinuxIccToolChain(); friend class Internal::LinuxIccToolChainFactory; friend class ToolChainFactory; diff --git a/src/plugins/projectexplorer/gcctoolchainfactories.h b/src/plugins/projectexplorer/gcctoolchainfactories.h index 775e0db675..e33ebcaa03 100644 --- a/src/plugins/projectexplorer/gcctoolchainfactories.h +++ b/src/plugins/projectexplorer/gcctoolchainfactories.h @@ -33,6 +33,8 @@ #include <QList> #include <QSet> +#include <functional> + QT_BEGIN_NAMESPACE class QComboBox; QT_END_NAMESPACE @@ -51,33 +53,20 @@ class GccToolChainFactory : public ToolChainFactory public: GccToolChainFactory(); - QSet<Core::Id> supportedLanguages() const override; QList<ToolChain *> autoDetect(const QList<ToolChain *> &alreadyKnown) override; - QList<ToolChain *> autoDetect(const Utils::FileName &compilerPath, const Core::Id &language) override; - - bool canCreate() override; - ToolChain *create(Core::Id language) override; - - bool canRestore(const QVariantMap &data) override; - ToolChain *restore(const QVariantMap &data) override; + QList<ToolChain *> autoDetect(const Utils::FilePath &compilerPath, const Core::Id &language) override; protected: - virtual GccToolChain *createToolChain(bool autoDetect); - void versionProbe(const QString &name, - Core::Id language, - Core::Id type, - QList<ToolChain *> &tcs, - QList<ToolChain *> &known, - const QSet<QString> &filteredNames = {}); - - Utils::FileName compilerPathFromEnvironment(const QString &compilerName); - + enum class DetectVariants { Yes, No }; + using ToolchainChecker = std::function<bool(const ToolChain *)>; QList<ToolChain *> autoDetectToolchains( - const Utils::FileName &compilerPath, const Abi &requiredAbi, Core::Id language, - const Core::Id requiredTypeId, const QList<ToolChain *> &alreadyKnown); - QList<ToolChain *> autoDetectToolChain(const Utils::FileName &compilerPath, const Core::Id language, - const Abi &requiredAbi = Abi()); + const QString &compilerName, DetectVariants detectVariants, Core::Id language, + const Core::Id requiredTypeId, const QList<ToolChain *> &alreadyKnown, + const ToolchainChecker &checker = {}); + QList<ToolChain *> autoDetectToolChain( + const Utils::FilePath &compilerPath, const Core::Id language, + const ToolchainChecker &checker = {}); }; // -------------------------------------------------------------------------- @@ -147,15 +136,9 @@ class ClangToolChainFactory : public GccToolChainFactory public: ClangToolChainFactory(); - QSet<Core::Id> supportedLanguages() const override; QList<ToolChain *> autoDetect(const QList<ToolChain *> &alreadyKnown) override; - QList<ToolChain *> autoDetect(const Utils::FileName &compilerPath, const Core::Id &language) final; - - bool canRestore(const QVariantMap &data) override; - -protected: - GccToolChain *createToolChain(bool autoDetect) override; + QList<ToolChain *> autoDetect(const Utils::FilePath &compilerPath, const Core::Id &language) final; }; // -------------------------------------------------------------------------- @@ -168,15 +151,9 @@ class MingwToolChainFactory : public GccToolChainFactory public: MingwToolChainFactory(); - QSet<Core::Id> supportedLanguages() const override; QList<ToolChain *> autoDetect(const QList<ToolChain *> &alreadyKnown) override; - QList<ToolChain *> autoDetect(const Utils::FileName &compilerPath, const Core::Id &language) final; - - bool canRestore(const QVariantMap &data) override; - -protected: - GccToolChain *createToolChain(bool autoDetect) override; + QList<ToolChain *> autoDetect(const Utils::FilePath &compilerPath, const Core::Id &language) final; }; // -------------------------------------------------------------------------- @@ -189,15 +166,9 @@ class LinuxIccToolChainFactory : public GccToolChainFactory public: LinuxIccToolChainFactory(); - QSet<Core::Id> supportedLanguages() const override; QList<ToolChain *> autoDetect(const QList<ToolChain *> &alreadyKnown) override; - QList<ToolChain *> autoDetect(const Utils::FileName &compilerPath, const Core::Id &language) final; - - bool canRestore(const QVariantMap &data) override; - -protected: - GccToolChain *createToolChain(bool autoDetect) override; + QList<ToolChain *> autoDetect(const Utils::FilePath &compilerPath, const Core::Id &language) final; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/gnumakeparser.cpp b/src/plugins/projectexplorer/gnumakeparser.cpp index 2b9c0820cc..dc14301117 100644 --- a/src/plugins/projectexplorer/gnumakeparser.cpp +++ b/src/plugins/projectexplorer/gnumakeparser.cpp @@ -90,6 +90,18 @@ public: Task::TaskType type = Task::Error; }; +static Task::TaskType taskTypeFromDescription(const QString &description) +{ + if (description.contains(". Stop.")) + return Task::Error; + if (description.contains("not found")) + return Task::Error; + if (description.contains("No rule to make target")) + return Task::Error; + // Extend as needed. + return Task::Warning; +} + static Result parseDescription(const QString &description) { Result result; @@ -103,7 +115,7 @@ static Result parseDescription(const QString &description) result.isFatal = true; } else { result.description = description; - result.type = Task::Error; + result.type = taskTypeFromDescription(description); result.isFatal = false; } return result; @@ -121,7 +133,7 @@ void GnuMakeParser::stdError(const QString &line) ++m_fatalErrorCount; if (!m_suppressIssues) { taskAdded(Task(res.type, res.description, - Utils::FileName::fromUserInput(match.captured(1)) /* filename */, + Utils::FilePath::fromUserInput(match.captured(1)) /* filename */, match.captured(4).toInt(), /* line */ Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)), 1, 0); } @@ -135,7 +147,7 @@ void GnuMakeParser::stdError(const QString &line) ++m_fatalErrorCount; if (!m_suppressIssues) { Task task = Task(res.type, res.description, - Utils::FileName() /* filename */, -1, /* line */ + Utils::FilePath() /* filename */, -1, /* line */ Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)); taskAdded(task, 1, 0); } @@ -169,7 +181,7 @@ void GnuMakeParser::taskAdded(const Task &task, int linkedLines, int skippedLine QString filePath(task.file.toString()); if (!filePath.isEmpty() && !QDir::isAbsolutePath(filePath)) { - QList<QFileInfo> possibleFiles; + QFileInfoList possibleFiles; foreach (const QString &dir, m_directories) { QFileInfo candidate(dir + QLatin1Char('/') + filePath); if (candidate.exists() @@ -178,7 +190,7 @@ void GnuMakeParser::taskAdded(const Task &task, int linkedLines, int skippedLine } } if (possibleFiles.size() == 1) - editable.file = Utils::FileName(possibleFiles.first()); + editable.file = Utils::FilePath::fromFileInfo(possibleFiles.first()); // Let the Makestep apply additional heuristics (based on // files in ther project) if we cannot uniquely // identify the file! @@ -222,7 +234,7 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() QTest::addColumn<OutputParserTester::Channel>("inputChannel"); QTest::addColumn<QString>("childStdOutLines"); QTest::addColumn<QString>("childStdErrLines"); - QTest::addColumn<QList<Task> >("tasks"); + QTest::addColumn<Tasks >("tasks"); QTest::addColumn<QString>("outputLines"); QTest::addColumn<QStringList>("additionalSearchDirs"); @@ -230,14 +242,14 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() << QStringList() << QString::fromLatin1("Sometext") << OutputParserTester::STDOUT << QString::fromLatin1("Sometext\n") << QString() - << QList<Task>() + << Tasks() << QString() << QStringList(); QTest::newRow("pass-through stderr") << QStringList() << QString::fromLatin1("Sometext") << OutputParserTester::STDERR << QString() << QString::fromLatin1("Sometext\n") - << QList<Task>() + << Tasks() << QString() << QStringList(); QTest::newRow("pass-through gcc infos") @@ -254,7 +266,7 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() "../../scriptbug/main.cpp: In instantiation of void bar(i) [with i = double]:\n" "../../scriptbug/main.cpp:8: instantiated from void foo(i) [with i = double]\n" "../../scriptbug/main.cpp:22: instantiated from here\n") - << QList<Task>() + << Tasks() << QString() << QStringList(); @@ -265,7 +277,7 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() "make[4]: Entering directory `/home/code/build/qt/examples/opengl/grabber'") << OutputParserTester::STDOUT << QString() << QString() - << QList<Task>() + << Tasks() << QString() << QStringList({"/home/code/build/qt/examples/opengl/grabber", "/home/code/build/qt/examples/opengl/grabber", "/test/dir"}); @@ -274,7 +286,7 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() << QString::fromLatin1("make[4]: Leaving directory `/home/code/build/qt/examples/opengl/grabber'") << OutputParserTester::STDOUT << QString() << QString() - << QList<Task>() + << Tasks() << QString() << QStringList("/test/dir"); QTest::newRow("make error") @@ -282,10 +294,10 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() << QString::fromLatin1("make: *** No rule to make target `hello.c', needed by `hello.o'. Stop.") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Error, QString::fromLatin1("No rule to make target `hello.c', needed by `hello.o'. Stop."), - Utils::FileName(), -1, + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))) << QString() << QStringList(); @@ -296,10 +308,10 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() "make[2]: *** [sub-projectexplorer-make_default] Error 2") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Error, QString::fromLatin1("[.obj/debug-shared/gnumakeparser.o] Error 1"), - Utils::FileName(), -1, + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))) << QString() << QStringList(); @@ -308,10 +320,10 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() << QString::fromLatin1("Makefile:360: *** missing separator (did you mean TAB instead of 8 spaces?). Stop.") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Error, QString::fromLatin1("missing separator (did you mean TAB instead of 8 spaces?). Stop."), - Utils::FileName::fromUserInput(QLatin1String("Makefile")), 360, + Utils::FilePath::fromUserInput(QLatin1String("Makefile")), 360, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))) << QString() << QStringList(); @@ -321,10 +333,10 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() "mingw32-make: *** [debug] Error 2") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Error, QString::fromLatin1("[debug/qplotaxis.o] Error 1"), - Utils::FileName(), -1, + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))) << QString() << QStringList(); @@ -333,10 +345,10 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() << QString::fromLatin1("mingw64-make.exe[1]: *** [dynlib.inst] Error -1073741819") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Error, QString::fromLatin1("[dynlib.inst] Error -1073741819"), - Utils::FileName(), -1, + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))) << QString() << QStringList(); @@ -345,10 +357,10 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() << QString::fromLatin1("make[2]: warning: jobserver unavailable: using -j1. Add `+' to parent make rule.") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Warning, QString::fromLatin1("jobserver unavailable: using -j1. Add `+' to parent make rule."), - Utils::FileName(), -1, + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))) << QString() << QStringList(); @@ -357,7 +369,7 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() << QString::fromLatin1("/home/dev/creator/share/qtcreator/debugger/dumper.cpp:1079: note: initialized from here") << OutputParserTester::STDERR << QString() << QString::fromLatin1("/home/dev/creator/share/qtcreator/debugger/dumper.cpp:1079: note: initialized from here\n") - << QList<Task>() + << Tasks() << QString() << QStringList(); QTest::newRow("Full path make exe") @@ -365,10 +377,10 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() << QString::fromLatin1("C:\\Qt\\4.6.2-Symbian\\s60sdk\\epoc32\\tools\\make.exe: *** [sis] Error 2") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Error, QString::fromLatin1("[sis] Error 2"), - Utils::FileName(), -1, + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))) << QString() << QStringList(); @@ -377,10 +389,10 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() << QString::fromLatin1("make: g++: Command not found") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Error, QString::fromLatin1("g++: Command not found"), - Utils::FileName(), -1, + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))) << QString() << QStringList(); @@ -389,10 +401,10 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing_data() << QString::fromLatin1("Makefile:794: warning: overriding commands for target `xxxx.app/Contents/Info.plist'") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Warning, QString::fromLatin1("overriding commands for target `xxxx.app/Contents/Info.plist'"), - Utils::FileName::fromString(QLatin1String("Makefile")), 794, + Utils::FilePath::fromString(QLatin1String("Makefile")), 794, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))) << QString() << QStringList(); @@ -410,7 +422,7 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing() QFETCH(QStringList, extraSearchDirs); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); - QFETCH(QList<Task>, tasks); + QFETCH(Tasks, tasks); QFETCH(QString, childStdOutLines); QFETCH(QString, childStdErrLines); QFETCH(QString, outputLines); @@ -455,12 +467,12 @@ void ProjectExplorerPlugin::testGnuMakeParserTaskMangling_data() << QStringList() << Task(Task::Error, QLatin1String("no filename, no mangling"), - Utils::FileName(), + Utils::FilePath(), -1, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Error, QLatin1String("no filename, no mangling"), - Utils::FileName(), + Utils::FilePath(), -1, Constants::TASK_CATEGORY_COMPILE); QTest::newRow("no mangling") @@ -468,12 +480,12 @@ void ProjectExplorerPlugin::testGnuMakeParserTaskMangling_data() << QStringList() << Task(Task::Error, QLatin1String("unknown filename, no mangling"), - Utils::FileName::fromUserInput(QLatin1String("some/path/unknown.cpp")), + Utils::FilePath::fromUserInput(QLatin1String("some/path/unknown.cpp")), -1, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Error, QLatin1String("unknown filename, no mangling"), - Utils::FileName::fromUserInput(QLatin1String("some/path/unknown.cpp")), + Utils::FilePath::fromUserInput(QLatin1String("some/path/unknown.cpp")), -1, Constants::TASK_CATEGORY_COMPILE); QTest::newRow("find file") @@ -481,12 +493,12 @@ void ProjectExplorerPlugin::testGnuMakeParserTaskMangling_data() << QStringList("test") << Task(Task::Error, QLatin1String("mangling"), - Utils::FileName::fromUserInput(QLatin1String("file.cpp")), + Utils::FilePath::fromUserInput(QLatin1String("file.cpp")), 10, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Error, QLatin1String("mangling"), - Utils::FileName::fromUserInput(QLatin1String("$TMPDIR/test/file.cpp")), + Utils::FilePath::fromUserInput(QLatin1String("$TMPDIR/test/file.cpp")), 10, Constants::TASK_CATEGORY_COMPILE); } @@ -529,7 +541,7 @@ void ProjectExplorerPlugin::testGnuMakeParserTaskMangling() // fix up output task file: QString filePath = outputTask.file.toString(); if (filePath.startsWith(QLatin1String("$TMPDIR/"))) - outputTask.file = Utils::FileName::fromString(filePath.replace(QLatin1String("$TMPDIR/"), tempdir)); + outputTask.file = Utils::FilePath::fromString(filePath.replace(QLatin1String("$TMPDIR/"), tempdir)); // test mangling: testbench.testTaskMangling(inputTask, outputTask); diff --git a/src/plugins/projectexplorer/images/fileoverlay_py.png b/src/plugins/projectexplorer/images/fileoverlay_py.png Binary files differnew file mode 100644 index 0000000000..3cda889a4b --- /dev/null +++ b/src/plugins/projectexplorer/images/fileoverlay_py.png diff --git a/src/plugins/projectexplorer/images/fileoverlay_py@2x.png b/src/plugins/projectexplorer/images/fileoverlay_py@2x.png Binary files differnew file mode 100644 index 0000000000..071c946456 --- /dev/null +++ b/src/plugins/projectexplorer/images/fileoverlay_py@2x.png diff --git a/src/plugins/projectexplorer/importwidget.cpp b/src/plugins/projectexplorer/importwidget.cpp index d9bd629179..eac2c44c94 100644 --- a/src/plugins/projectexplorer/importwidget.cpp +++ b/src/plugins/projectexplorer/importwidget.cpp @@ -29,6 +29,7 @@ #include <utils/pathchooser.h> #include <QPushButton> +#include <QTimer> #include <QVBoxLayout> namespace ProjectExplorer { @@ -60,19 +61,32 @@ ImportWidget::ImportWidget(QWidget *parent) : layout->addWidget(importButton); connect(importButton, &QAbstractButton::clicked, this, &ImportWidget::handleImportRequest); + connect(m_pathChooser->lineEdit(), &QLineEdit::returnPressed, this, [this] { + if (m_pathChooser->isValid()) { + handleImportRequest(); + + // The next return should trigger the "Configure" button. + QTimer::singleShot(0, this, QOverload<>::of(&QWidget::setFocus)); + } + }); detailsWidget->setWidget(widget); } -void ImportWidget::setCurrentDirectory(const Utils::FileName &dir) +void ImportWidget::setCurrentDirectory(const Utils::FilePath &dir) { m_pathChooser->setBaseFileName(dir); m_pathChooser->setFileName(dir); } +bool ImportWidget::lineEditHasFocus() const +{ + return m_pathChooser->lineEdit()->hasFocus(); +} + void ImportWidget::handleImportRequest() { - Utils::FileName dir = m_pathChooser->fileName(); + Utils::FilePath dir = m_pathChooser->fileName(); emit importFrom(dir); m_pathChooser->setFileName(m_pathChooser->baseFileName()); diff --git a/src/plugins/projectexplorer/importwidget.h b/src/plugins/projectexplorer/importwidget.h index 9a55f64e1b..8999bc117d 100644 --- a/src/plugins/projectexplorer/importwidget.h +++ b/src/plugins/projectexplorer/importwidget.h @@ -29,7 +29,7 @@ namespace Utils { class PathChooser; -class FileName; +class FilePath; } // namespace Utils namespace ProjectExplorer { @@ -42,10 +42,12 @@ class ImportWidget : public QWidget public: explicit ImportWidget(QWidget *parent = nullptr); - void setCurrentDirectory(const Utils::FileName &dir); + void setCurrentDirectory(const Utils::FilePath &dir); + + bool lineEditHasFocus() const; signals: - void importFrom(const Utils::FileName &dir); + void importFrom(const Utils::FilePath &dir); private: void handleImportRequest(); diff --git a/src/plugins/projectexplorer/ioutputparser.cpp b/src/plugins/projectexplorer/ioutputparser.cpp index 0da7005948..8dd6d99264 100644 --- a/src/plugins/projectexplorer/ioutputparser.cpp +++ b/src/plugins/projectexplorer/ioutputparser.cpp @@ -207,6 +207,11 @@ void IOutputParser::setWorkingDirectory(const QString &workingDirectory) m_parser->setWorkingDirectory(workingDirectory); } +void IOutputParser::setWorkingDirectory(const Utils::FilePath &fn) +{ + setWorkingDirectory(fn.toString()); +} + void IOutputParser::flush() { doFlush(); diff --git a/src/plugins/projectexplorer/ioutputparser.h b/src/plugins/projectexplorer/ioutputparser.h index 47c8f4f1c0..1872461cbb 100644 --- a/src/plugins/projectexplorer/ioutputparser.h +++ b/src/plugins/projectexplorer/ioutputparser.h @@ -53,6 +53,7 @@ public: virtual bool hasFatalErrors() const; virtual void setWorkingDirectory(const QString &workingDirectory); + void setWorkingDirectory(const Utils::FilePath &fn); void flush(); // flush out pending tasks diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp index d99f5fe0ec..835f3a0429 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp @@ -388,6 +388,7 @@ QWidget *LabelField::createWidget(const QString &displayName, JsonFieldPage *pag auto w = new QLabel; w->setWordWrap(m_wordWrap); w->setText(m_text); + w->setSizePolicy(QSizePolicy::Expanding, w->sizePolicy().verticalPolicy()); return w; } @@ -809,7 +810,7 @@ std::unique_ptr<QStandardItem> createStandardItemFromListItem(const QVariant &it if (item.type() == QVariant::Map) { QVariantMap tmp = item.toMap(); const QString key = JsonWizardFactory::localizedString(consumeValue(tmp, "trKey", QString()).toString()); - const QString value = consumeValue(tmp, "value", key).toString(); + const QVariant value = consumeValue(tmp, "value", key); if (key.isNull() || key.isEmpty()) { *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage", @@ -919,7 +920,7 @@ void ListField::initializeData(MacroExpander *expander) if (item.get() == currentItem) currentItem = expandedValuesItem; expandedValuesItem->setText(expander->expand(item->text())); - expandedValuesItem->setData(expander->expand(item->data(ValueRole).toString()), ValueRole); + expandedValuesItem->setData(expander->expandVariant(item->data(ValueRole)), ValueRole); expandedValuesItem->setData(expander->expand(item->data(IconStringRole).toString()), IconStringRole); expandedValuesItem->setData(condition, ConditionRole); @@ -1006,7 +1007,7 @@ void ComboBoxField::setup(JsonFieldPage *page, const QString &name) // the selectionModel does not behave like expected and wanted - so we block signals here // (for example there was some losing focus thing when hovering over items, ...) selectionModel()->blockSignals(true); - QObject::connect(w, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated), [w, this](int index) { + QObject::connect(w, QOverload<int>::of(&QComboBox::activated), [w, this](int index) { w->blockSignals(true); selectionModel()->clearSelection(); @@ -1019,8 +1020,8 @@ void ComboBoxField::setup(JsonFieldPage *page, const QString &name) page->registerObjectAsFieldWithName<QItemSelectionModel>(name, selectionModel(), &QItemSelectionModel::selectionChanged, [this]() { const QModelIndex i = selectionModel()->currentIndex(); if (i.isValid()) - return i.data(ValueRole).toString(); - return QString(); + return i.data(ValueRole); + return QVariant(); }); QObject::connect(selectionModel(), &QItemSelectionModel::selectionChanged, page, [page]() { emit page->completeChanged(); @@ -1057,8 +1058,8 @@ void IconListField::setup(JsonFieldPage *page, const QString &name) page->registerObjectAsFieldWithName<QItemSelectionModel>(name, selectionModel(), &QItemSelectionModel::selectionChanged, [this]() { const QModelIndex i = selectionModel()->currentIndex(); if (i.isValid()) - return i.data(ValueRole).toString(); - return QString(); + return i.data(ValueRole); + return QVariant(); }); QObject::connect(selectionModel(), &QItemSelectionModel::selectionChanged, page, [page]() { emit page->completeChanged(); diff --git a/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp index 5faa5c3642..7501521be7 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp @@ -107,7 +107,7 @@ void JsonKitsPage::setupProjectFiles(const JsonWizard::GeneratorFiles &files) const QFileInfo fi(f.file.path()); const QString path = fi.absoluteFilePath(); Project *project = ProjectManager::openProject(Utils::mimeTypeForFile(fi), - Utils::FileName::fromString(path)); + Utils::FilePath::fromString(path)); if (project) { if (setupProject(project)) project->saveSettings(); diff --git a/src/plugins/projectexplorer/jsonwizard/jsonprojectpage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonprojectpage.cpp index 45f332a2de..fcddd7ffd8 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonprojectpage.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonprojectpage.cpp @@ -52,7 +52,7 @@ bool JsonProjectPage::validatePage() { if (isComplete() && useAsDefaultPath()) { // Store the path as default path for new projects if desired. - Core::DocumentManager::setProjectsDirectory(Utils::FileName::fromString(path())); + Core::DocumentManager::setProjectsDirectory(Utils::FilePath::fromString(path())); Core::DocumentManager::setUseProjectsDirectory(true); } diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp index a3bad8656f..7684de0795 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp @@ -36,11 +36,20 @@ #include <coreplugin/messagemanager.h> #include <utils/algorithm.h> +#include <utils/itemviews.h> #include <utils/qtcassert.h> +#include <utils/treemodel.h> #include <utils/wizardpage.h> +#include <QDialog> +#include <QDialogButtonBox> +#include <QDir> #include <QFileInfo> +#include <QJSEngine> +#include <QLabel> #include <QMessageBox> +#include <QPushButton> +#include <QVBoxLayout> #include <QVariant> #ifdef WITH_TESTS @@ -49,7 +58,94 @@ namespace ProjectExplorer { -JsonWizard::JsonWizard(QWidget *parent) : Utils::Wizard(parent) +namespace Internal { + +class ProjectFileTreeItem : public Utils::TreeItem +{ +public: + ProjectFileTreeItem(JsonWizard::GeneratorFile *candidate) : m_candidate(candidate) + { + toggleProjectFileStatus(false); + } + + void toggleProjectFileStatus(bool on) + { + m_candidate->file.setAttributes(m_candidate->file.attributes() + .setFlag(Core::GeneratedFile::OpenProjectAttribute, on)); + } + +private: + QVariant data(int column, int role) const override + { + if (column != 0 || role != Qt::DisplayRole) + return QVariant(); + return QDir::toNativeSeparators(m_candidate->file.path()); + } + + JsonWizard::GeneratorFile * const m_candidate; +}; + +class ProjectFilesModel : public Utils::TreeModel<Utils::TreeItem, ProjectFileTreeItem> +{ +public: + ProjectFilesModel(const QList<JsonWizard::GeneratorFile *> &candidates, QObject *parent) + : TreeModel(parent) + { + setHeader({QCoreApplication::translate("ProjectExplorer::JsonWizard", "Project File")}); + for (JsonWizard::GeneratorFile * const candidate : candidates) + rootItem()->appendChild(new ProjectFileTreeItem(candidate)); + } +}; + +class ProjectFileChooser : public QDialog +{ +public: + ProjectFileChooser(const QList<JsonWizard::GeneratorFile *> &candidates, QWidget *parent) + : QDialog(parent), m_view(new Utils::TreeView(this)) + { + setWindowTitle(QCoreApplication::translate("ProjectExplorer::JsonWizard", + "Choose Project File")); + const auto model = new ProjectFilesModel(candidates, this); + m_view->setSelectionMode(Utils::TreeView::ExtendedSelection); + m_view->setSelectionBehavior(Utils::TreeView::SelectRows); + m_view->setModel(model); + const auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); + const auto updateOkButton = [buttonBox, this] { + buttonBox->button(QDialogButtonBox::Ok) + ->setEnabled(m_view->selectionModel()->hasSelection()); + }; + connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, + this, updateOkButton); + updateOkButton(); + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + const auto layout = new QVBoxLayout(this); + layout->addWidget(new QLabel(QCoreApplication::translate("ProjectExplorer::JsonWizard", + "The project contains more than one project file. " + "Select the one you would like to use."))); + layout->addWidget(m_view); + layout->addWidget(buttonBox); + } + +private: + void accept() override + { + const QModelIndexList selected = m_view->selectionModel()->selectedRows(); + const auto * const model = static_cast<ProjectFilesModel *>(m_view->model()); + for (const QModelIndex &index : selected) { + const auto item = static_cast<ProjectFileTreeItem *>(model->itemForIndex(index)); + QTC_ASSERT(item, continue); + item->toggleProjectFileStatus(true); + } + QDialog::accept(); + } + + Utils::TreeView * const m_view; +}; + +} // namespace Internal + +JsonWizard::JsonWizard(QWidget *parent) + : Utils::Wizard(parent) { setMinimumSize(800, 500); m_expander.registerExtraResolver([this](const QString &name, QString *ret) -> bool { @@ -63,7 +159,10 @@ JsonWizard::JsonWizard(QWidget *parent) : Utils::Wizard(parent) const QString key = QString::fromLatin1("%{") + value + QLatin1Char('}'); return m_expander.expand(key) == key ? QString() : QLatin1String("true"); }); - + // override default JS macro by custom one that adds Wizard specific features + m_jsExpander.registerObject("Wizard", new Internal::JsonWizardJsExtension(this)); + m_jsExpander.engine().evaluate("var value = Wizard.value"); + m_jsExpander.registerForExpander(&m_expander); } JsonWizard::~JsonWizard() @@ -113,6 +212,14 @@ JsonWizard::GeneratorFiles JsonWizard::generateFileList() return GeneratorFiles(); } + QList<GeneratorFile *> projectFiles; + for (JsonWizard::GeneratorFile &f : list) { + if (f.file.attributes().testFlag(Core::GeneratedFile::OpenProjectAttribute)) + projectFiles << &f; + } + if (projectFiles.count() > 1) + Internal::ProjectFileChooser(projectFiles, this).exec(); + return list; } @@ -392,11 +499,16 @@ void JsonWizard::openProjectForNode(Node *node) { using namespace Utils; - ProjectNode *projNode = node->asProjectNode() ? node->asProjectNode() : node->parentProjectNode(); - + const ProjectNode *projNode = node->asProjectNode(); + if (!projNode) { + if (ContainerNode * const cn = node->asContainerNode()) + projNode = cn->rootProjectNode(); + else + projNode = node->parentProjectNode(); + } QTC_ASSERT(projNode, return); - Utils::optional<FileName> projFilePath = projNode->visibleAfterAddFileAction(); + Utils::optional<FilePath> projFilePath = projNode->visibleAfterAddFileAction(); if (projFilePath && !Core::EditorManager::openEditor(projFilePath.value().toString())) { auto errorMessage = QCoreApplication::translate("ProjectExplorer::JsonWizard", @@ -418,4 +530,17 @@ bool JsonWizard::OptionDefinition::condition(Utils::MacroExpander &expander) con return JsonWizard::boolFromVariant(m_condition, &expander); } +namespace Internal { + +JsonWizardJsExtension::JsonWizardJsExtension(JsonWizard *wizard) + : m_wizard(wizard) +{} + +QVariant JsonWizardJsExtension::value(const QString &name) const +{ + const QVariant value = m_wizard->value(name); + return m_wizard->expander()->expandVariant(m_wizard->value(name)); +} + +} // namespace Internal } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizard.h b/src/plugins/projectexplorer/jsonwizard/jsonwizard.h index 1f8aca7741..99a9ce4952 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizard.h +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizard.h @@ -29,6 +29,7 @@ #include <projectexplorer/projectnodes.h> #include <coreplugin/generatedfile.h> +#include <coreplugin/jsexpander.h> #include <utils/wizard.h> #include <utils/macroexpander.h> @@ -37,8 +38,25 @@ namespace ProjectExplorer { +class JsonWizard; class JsonWizardGenerator; +namespace Internal { + +class JsonWizardJsExtension : public QObject +{ + Q_OBJECT +public: + JsonWizardJsExtension(JsonWizard *wizard); + + Q_INVOKABLE QVariant value(const QString &name) const; + +private: + JsonWizard *m_wizard; +}; + +} // namespace Internal + // Documentation inside. class PROJECTEXPLORER_EXPORT JsonWizard : public Utils::Wizard { @@ -126,6 +144,7 @@ private: GeneratorFiles m_files; Utils::MacroExpander m_expander; + Core::JsExpander m_jsExpander; }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp index 3a01d9e045..530ace9ca7 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp @@ -34,6 +34,7 @@ #include <coreplugin/coreconstants.h> #include <coreplugin/icontext.h> #include <coreplugin/icore.h> +#include <coreplugin/jsexpander.h> #include <coreplugin/messagemanager.h> #include <extensionsystem/pluginmanager.h> @@ -46,10 +47,11 @@ #include <QDebug> #include <QDir> -#include <QMap> +#include <QJSEngine> #include <QJsonDocument> #include <QJsonObject> #include <QJsonParseError> +#include <QMap> #include <QUuid> namespace ProjectExplorer { @@ -209,7 +211,7 @@ QList<Core::IWizardFactory *> JsonWizardFactory::createWizardFactories() const QString wizardFileName = QLatin1String(WIZARD_FILE); QList <Core::IWizardFactory *> result; - foreach (const Utils::FileName &path, searchPaths()) { + foreach (const Utils::FilePath &path, searchPaths()) { if (path.isEmpty()) continue; @@ -223,7 +225,7 @@ QList<Core::IWizardFactory *> JsonWizardFactory::createWizardFactories() const QDir::Filters filters = QDir::Dirs|QDir::Readable|QDir::NoDotAndDotDot; const QDir::SortFlags sortflags = QDir::Name|QDir::IgnoreCase; - QList<QFileInfo> dirs = dir.entryInfoList(filters, sortflags); + QFileInfoList dirs = dir.entryInfoList(filters, sortflags); while (!dirs.isEmpty()) { const QFileInfo dirFi = dirs.takeFirst(); @@ -283,7 +285,7 @@ QList<Core::IWizardFactory *> JsonWizardFactory::createWizardFactories() result << factory; } else { - QList<QFileInfo> subDirs = current.entryInfoList(filters, sortflags); + QFileInfoList subDirs = current.entryInfoList(filters, sortflags); if (!subDirs.isEmpty()) { // There is no QList::prepend(QList)... dirs.swap(subDirs); @@ -332,20 +334,20 @@ static QStringList environmentTemplatesPaths() return paths; } -Utils::FileNameList &JsonWizardFactory::searchPaths() +Utils::FilePathList &JsonWizardFactory::searchPaths() { - static Utils::FileNameList m_searchPaths = Utils::FileNameList() - << Utils::FileName::fromString(Core::ICore::userResourcePath() + QLatin1Char('/') + + static Utils::FilePathList m_searchPaths = Utils::FilePathList() + << Utils::FilePath::fromString(Core::ICore::userResourcePath() + QLatin1Char('/') + QLatin1String(WIZARD_PATH)) - << Utils::FileName::fromString(Core::ICore::resourcePath() + QLatin1Char('/') + + << Utils::FilePath::fromString(Core::ICore::resourcePath() + QLatin1Char('/') + QLatin1String(WIZARD_PATH)); for (const QString &environmentTemplateDirName : environmentTemplatesPaths()) - m_searchPaths << Utils::FileName::fromString(environmentTemplateDirName); + m_searchPaths << Utils::FilePath::fromString(environmentTemplateDirName); return m_searchPaths; } -void JsonWizardFactory::addWizardPath(const Utils::FileName &path) +void JsonWizardFactory::addWizardPath(const Utils::FilePath &path) { searchPaths().append(path); } @@ -513,9 +515,17 @@ bool JsonWizardFactory::isAvailable(Core::Id platformId) const [platformId]() { return platformId.toString(); }); expander.registerVariable("Features", tr("The features available to this wizard."), [this, e, platformId]() { return JsonWizard::stringListToArrayString(Core::Id::toStringList(availableFeatures(platformId)), e); }); - expander.registerVariable("Plugins", tr("The plugins loaded."), - [this, e]() { return JsonWizard::stringListToArrayString(Core::Id::toStringList(pluginFeatures()), e); }); - + expander.registerVariable("Plugins", tr("The plugins loaded."), [this, e]() { + return JsonWizard::stringListToArrayString(Core::Id::toStringList(pluginFeatures()), e); + }); + Core::JsExpander jsExpander; + jsExpander.registerObject("Wizard", + new Internal::JsonWizardFactoryJsExtension(platformId, + availableFeatures( + platformId), + pluginFeatures())); + jsExpander.engine().evaluate("var value = Wizard.value"); + jsExpander.registerForExpander(e); return JsonWizard::boolFromVariant(m_enabledExpression, &expander); } @@ -660,4 +670,26 @@ bool JsonWizardFactory::initialize(const QVariantMap &data, const QDir &baseDir, return errorMessage->isEmpty(); } +namespace Internal { + +JsonWizardFactoryJsExtension::JsonWizardFactoryJsExtension(Core::Id platformId, + const QSet<Core::Id> &availableFeatures, + const QSet<Core::Id> &pluginFeatures) + : m_platformId(platformId) + , m_availableFeatures(availableFeatures) + , m_pluginFeatures(pluginFeatures) +{} + +QVariant JsonWizardFactoryJsExtension::value(const QString &name) const +{ + if (name == "Platform") + return m_platformId.toString(); + if (name == "Features") + return Core::Id::toStringList(m_availableFeatures); + if (name == "Plugins") + return Core::Id::toStringList(m_pluginFeatures); + return QVariant(); +} + +} // namespace Internal } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h index ead93f4841..5573bb2979 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h @@ -55,7 +55,7 @@ class PROJECTEXPLORER_EXPORT JsonWizardFactory : public Core::IWizardFactory public: // Add search paths for wizard.json files. All subdirs are going to be checked. - static void addWizardPath(const Utils::FileName &path); + static void addWizardPath(const Utils::FilePath &path); // actual interface of the wizard factory: class Generator { @@ -96,7 +96,7 @@ private: static QList<IWizardFactory *> createWizardFactories(); static JsonWizardFactory *createWizardFactory(const QVariantMap &data, const QDir &baseDir, QString *errorMessage); - static Utils::FileNameList &searchPaths(); + static Utils::FilePathList &searchPaths(); static void setVerbose(int level); static int verbose(); @@ -119,4 +119,23 @@ private: friend class ProjectExplorerPluginPrivate; }; -} //namespace ProjectExplorer +namespace Internal { + +class JsonWizardFactoryJsExtension : public QObject +{ + Q_OBJECT +public: + JsonWizardFactoryJsExtension(Core::Id platformId, + const QSet<Core::Id> &availableFeatures, + const QSet<Core::Id> &pluginFeatures); + + Q_INVOKABLE QVariant value(const QString &name) const; + +private: + Core::Id m_platformId; + QSet<Core::Id> m_availableFeatures; + QSet<Core::Id> m_pluginFeatures; +}; + +} // namespace Internal +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfilegenerator.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardfilegenerator.cpp index ee7da5b419..a168fea084 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfilegenerator.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfilegenerator.cpp @@ -127,7 +127,9 @@ Core::GeneratedFile JsonWizardFileGenerator::generateFile(const File &file, *ret = options.value(n); return true; }); - nested.registerExtraResolver([expander](QString n, QString *ret) { return expander->resolveMacro(n, ret); }); + nested.registerExtraResolver([expander](QString n, QString *ret) { + return expander->resolveMacro(n, ret); + }); gf.setContents(Utils::TemplateEngine::processText(&nested, QString::fromUtf8(reader.data()), errorMessage)); diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.cpp index e0a06c824e..cf84f8b5e4 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.cpp @@ -98,7 +98,7 @@ bool JsonWizardGenerator::formatFile(const JsonWizard *wizard, GeneratedFile *fi Indenter *indenter = nullptr; if (factory) { indenter = factory->createIndenter(&doc); - indenter->setFileName(Utils::FileName::fromString(file->path())); + indenter->setFileName(Utils::FilePath::fromString(file->path())); } if (!indenter) indenter = new NormalIndenter(&doc); diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp index 08d3ed1f88..9f24f00718 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp @@ -42,6 +42,8 @@ #include <QDir> #include <QVariant> +#include <limits> + namespace ProjectExplorer { namespace Internal { @@ -70,8 +72,6 @@ bool JsonWizardScannerGenerator::setup(const QVariant &data, QString *errorMessa m_subDirectoryExpressions << regexp; } - m_firstProjectOnly = gen.value(QLatin1String("firstProjectOnly"), QLatin1String("true")).toString(); - return true; } @@ -97,17 +97,28 @@ Core::GeneratedFiles JsonWizardScannerGenerator::fileList(Utils::MacroExpander * } } - bool onlyFirst = JsonWizard::boolFromVariant(m_firstProjectOnly, expander); - result = scan(project.absolutePath(), project); - int projectCount = 0; + static const auto getDepth = [](const QString &filePath) { return filePath.count('/'); }; + int minDepth = std::numeric_limits<int>::max(); for (auto it = result.begin(); it != result.end(); ++it) { const QString relPath = project.relativeFilePath(it->path()); it->setBinary(binaryPattern.match(relPath).hasMatch()); bool found = ProjectManager::canOpenProjectForMimeType(Utils::mimeTypeForFile(relPath)); - if (found && !(onlyFirst && projectCount++)) + if (found) { it->setAttributes(it->attributes() | Core::GeneratedFile::OpenProjectAttribute); + minDepth = std::min(minDepth, getDepth(it->path())); + } + } + + // Project files that appear on a lower level in the file system hierarchy than + // other project files are not candidates for opening. + for (Core::GeneratedFile &f : result) { + if (f.attributes().testFlag(Core::GeneratedFile::OpenProjectAttribute) + && getDepth(f.path()) > minDepth) { + f.setAttributes(f.attributes().setFlag(Core::GeneratedFile::OpenProjectAttribute, + false)); + } } return result; @@ -130,7 +141,7 @@ Core::GeneratedFiles JsonWizardScannerGenerator::scan(const QString &dir, const if (!directory.exists()) return result; - QList<QFileInfo> entries = directory.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot, + QFileInfoList entries = directory.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot, QDir::DirsLast | QDir::Name); foreach (const QFileInfo &fi, entries) { const QString relativePath = base.relativeFilePath(fi.absoluteFilePath()); diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.h b/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.h index e852ad1fe0..76475d4f97 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.h +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.h @@ -43,13 +43,11 @@ public: Core::GeneratedFiles fileList(Utils::MacroExpander *expander, const QString &wizardDir, const QString &projectDir, QString *errorMessage) override; - private: Core::GeneratedFiles scan(const QString &dir, const QDir &base); bool matchesSubdirectoryPattern(const QString &path); QString m_binaryPattern; - QString m_firstProjectOnly; QList<QRegularExpression> m_subDirectoryExpressions; }; diff --git a/src/plugins/projectexplorer/kit.cpp b/src/plugins/projectexplorer/kit.cpp index 5376f0c766..88e2e65ada 100644 --- a/src/plugins/projectexplorer/kit.cpp +++ b/src/plugins/projectexplorer/kit.cpp @@ -36,7 +36,10 @@ #include <utils/fileutils.h> #include <utils/icon.h> #include <utils/macroexpander.h> +#include <utils/optional.h> #include <utils/qtcassert.h> +#include <utils/stringutils.h> +#include <utils/utilsicons.h> #include <QApplication> #include <QFileInfo> @@ -45,6 +48,8 @@ #include <QTextStream> #include <QUuid> +#include <numeric> + using namespace Core; using namespace Utils; @@ -56,8 +61,10 @@ const char AUTODETECTIONSOURCE_KEY[] = "PE.Profile.AutoDetectionSource"; const char SDK_PROVIDED_KEY[] = "PE.Profile.SDK"; const char DATA_KEY[] = "PE.Profile.Data"; const char ICON_KEY[] = "PE.Profile.Icon"; +const char DEVICE_TYPE_FOR_ICON_KEY[] = "PE.Profile.DeviceTypeForIcon"; const char MUTABLE_INFO_KEY[] = "PE.Profile.MutableInfo"; const char STICKY_INFO_KEY[] = "PE.Profile.StickyInfo"; +const char IRRELEVANT_ASPECTS_KEY[] = "PE.Kit.IrrelevantAspects"; namespace ProjectExplorer { namespace Internal { @@ -86,8 +93,8 @@ public: [kit] { return kit->id().toString(); }); m_macroExpander.registerVariable("Kit:FileSystemName", tr("Kit filesystem-friendly name"), [kit] { return kit->fileSystemFriendlyName(); }); - foreach (KitInformation *ki, KitManager::kitInformation()) - ki->addToMacroExpander(kit, &m_macroExpander); + for (KitAspect *aspect : KitManager::kitAspects()) + aspect->addToMacroExpander(kit, &m_macroExpander); // This provides the same global fall back as the global expander // without relying on the currentKit() discovery process there. @@ -117,11 +124,13 @@ public: bool m_hasValidityInfo = false; bool m_mustNotify = false; QIcon m_cachedIcon; - FileName m_iconPath; + FilePath m_iconPath; + Id m_deviceTypeForIcon; QHash<Id, QVariant> m_data; QSet<Id> m_sticky; QSet<Id> m_mutable; + optional<QSet<Id>> m_irrelevantAspects; MacroExpander m_macroExpander; }; @@ -134,8 +143,6 @@ public: Kit::Kit(Id id) : d(std::make_unique<Internal::KitPrivate>(id, this)) { - foreach (KitInformation *sti, KitManager::kitInformation()) - d->m_data.insert(sti->id(), sti->defaultValue(this)); } Kit::Kit(const QVariantMap &data) : @@ -156,8 +163,12 @@ Kit::Kit(const QVariantMap &data) : d->m_unexpandedDisplayName = data.value(QLatin1String(DISPLAYNAME_KEY), d->m_unexpandedDisplayName).toString(); d->m_fileSystemFriendlyName = data.value(QLatin1String(FILESYSTEMFRIENDLYNAME_KEY)).toString(); - d->m_iconPath = FileName::fromString(data.value(QLatin1String(ICON_KEY), + d->m_iconPath = FilePath::fromString(data.value(QLatin1String(ICON_KEY), d->m_iconPath.toString()).toString()); + d->m_deviceTypeForIcon = Id::fromSetting(data.value(DEVICE_TYPE_FOR_ICON_KEY)); + const auto it = data.constFind(IRRELEVANT_ASPECTS_KEY); + if (it != data.constEnd()) + d->m_irrelevantAspects = transform<QSet<Id>>(it.value().toList(), &Id::fromSetting); QVariantMap extra = data.value(QLatin1String(DATA_KEY)).toMap(); d->m_data.clear(); // remove default values @@ -190,38 +201,40 @@ void Kit::unblockNotification() kitUpdated(); } +void Kit::copyKitCommon(Kit *target, const Kit *source) +{ + target->d->m_data = source->d->m_data; + target->d->m_iconPath = source->d->m_iconPath; + target->d->m_deviceTypeForIcon = source->d->m_deviceTypeForIcon; + target->d->m_cachedIcon = source->d->m_cachedIcon; + target->d->m_sticky = source->d->m_sticky; + target->d->m_mutable = source->d->m_mutable; + target->d->m_irrelevantAspects = source->d->m_irrelevantAspects; +} + Kit *Kit::clone(bool keepName) const { auto k = new Kit; + copyKitCommon(k, this); if (keepName) k->d->m_unexpandedDisplayName = d->m_unexpandedDisplayName; else - k->d->m_unexpandedDisplayName = QCoreApplication::translate("ProjectExplorer::Kit", "Clone of %1") - .arg(d->m_unexpandedDisplayName); + k->d->m_unexpandedDisplayName = newKitName(KitManager::kits()); k->d->m_autodetected = false; - k->d->m_data = d->m_data; // Do not clone m_fileSystemFriendlyName, needs to be unique - k->d->m_hasError = d->m_hasError; - k->d->m_cachedIcon = d->m_cachedIcon; - k->d->m_iconPath = d->m_iconPath; - k->d->m_sticky = d->m_sticky; - k->d->m_mutable = d->m_mutable; + k->d->m_hasError = d->m_hasError; // TODO: Is this intentionally not done for copyFrom()? return k; } void Kit::copyFrom(const Kit *k) { KitGuard g(this); - d->m_data = k->d->m_data; - d->m_iconPath = k->d->m_iconPath; - d->m_cachedIcon = k->d->m_cachedIcon; + copyKitCommon(this, k); d->m_autodetected = k->d->m_autodetected; d->m_autoDetectionSource = k->d->m_autoDetectionSource; d->m_unexpandedDisplayName = k->d->m_unexpandedDisplayName; d->m_fileSystemFriendlyName = k->d->m_fileSystemFriendlyName; d->m_mustNotify = true; - d->m_sticky = k->d->m_sticky; - d->m_mutable = k->d->m_mutable; } bool Kit::isValid() const @@ -243,14 +256,12 @@ bool Kit::hasWarning() const return d->m_hasWarning; } -QList<Task> Kit::validate() const +Tasks Kit::validate() const { - QList<Task> result; - QList<KitInformation *> infoList = KitManager::kitInformation(); - for (KitInformation *i : infoList) { - QList<Task> tmp = i->validate(this); - result.append(tmp); - } + Tasks result; + for (KitAspect *aspect : KitManager::kitAspects()) + result.append(aspect->validate(this)); + d->m_hasError = containsType(result, Task::TaskType::Error); d->m_hasWarning = containsType(result, Task::TaskType::Warning); @@ -262,25 +273,25 @@ QList<Task> Kit::validate() const void Kit::fix() { KitGuard g(this); - foreach (KitInformation *i, KitManager::kitInformation()) - i->fix(this); + for (KitAspect *aspect : KitManager::kitAspects()) + aspect->fix(this); } void Kit::setup() { KitGuard g(this); - const QList<KitInformation *> info = KitManager::kitInformation(); - for (KitInformation * const ki : info) - ki->setup(this); + const QList<KitAspect *> aspects = KitManager::kitAspects(); + for (KitAspect * const aspect : aspects) + aspect->setup(this); } void Kit::upgrade() { KitGuard g(this); - // Process the KitInfos in reverse order: They may only be based on other information lower in - // the stack. - for (KitInformation *ki : KitManager::kitInformation()) - ki->upgrade(this); + // Process the KitAspects in reverse order: They may only be based on other information + // lower in the stack. + for (KitAspect *aspect : KitManager::kitAspects()) + aspect->upgrade(this); } QString Kit::unexpandedDisplayName() const @@ -350,6 +361,15 @@ Id Kit::id() const return d->m_id; } +int Kit::weight() const +{ + const QList<KitAspect *> &aspects = KitManager::kitAspects(); + return std::accumulate(aspects.begin(), aspects.end(), 0, + [this](int sum, const KitAspect *aspect) { + return sum + aspect->weight(this); + }); +} + static QIcon iconForDeviceType(Core::Id deviceType) { const IDeviceFactory *factory = Utils::findOrDefault(IDeviceFactory::allDeviceFactories(), @@ -364,12 +384,13 @@ QIcon Kit::icon() const if (!d->m_cachedIcon.isNull()) return d->m_cachedIcon; - if (!d->m_iconPath.isEmpty() && d->m_iconPath.exists()) { + if (!d->m_deviceTypeForIcon.isValid() && !d->m_iconPath.isEmpty() && d->m_iconPath.exists()) { d->m_cachedIcon = QIcon(d->m_iconPath.toString()); return d->m_cachedIcon; } - const Core::Id deviceType = DeviceTypeKitInformation::deviceTypeId(this); + const Core::Id deviceType = d->m_deviceTypeForIcon.isValid() + ? d->m_deviceTypeForIcon : DeviceTypeKitAspect::deviceTypeId(this); const QIcon deviceTypeIcon = iconForDeviceType(deviceType); if (!deviceTypeIcon.isNull()) { d->m_cachedIcon = deviceTypeIcon; @@ -380,19 +401,43 @@ QIcon Kit::icon() const return d->m_cachedIcon; } -FileName Kit::iconPath() const +QIcon Kit::displayIcon() const +{ + QIcon result = icon(); + if (hasWarning()) { + static const QIcon warningIcon(Utils::Icons::WARNING.icon()); + result = warningIcon; + } + if (!isValid()) { + static const QIcon errorIcon(Utils::Icons::CRITICAL.icon()); + result = errorIcon; + } + return result; +} + +FilePath Kit::iconPath() const { return d->m_iconPath; } -void Kit::setIconPath(const FileName &path) +void Kit::setIconPath(const FilePath &path) { if (d->m_iconPath == path) return; + d->m_deviceTypeForIcon = Id(); d->m_iconPath = path; kitUpdated(); } +void Kit::setDeviceTypeForIcon(Id deviceType) +{ + if (d->m_deviceTypeForIcon == deviceType) + return; + d->m_iconPath.clear(); + d->m_deviceTypeForIcon = deviceType; + kitUpdated(); +} + QList<Id> Kit::allKeys() const { return d->m_data.keys(); @@ -459,10 +504,11 @@ bool Kit::isEqual(const Kit *other) const { return isDataEqual(other) && d->m_iconPath == other->d->m_iconPath + && d->m_deviceTypeForIcon == other->d->m_deviceTypeForIcon && d->m_unexpandedDisplayName == other->d->m_unexpandedDisplayName && d->m_fileSystemFriendlyName == other->d->m_fileSystemFriendlyName + && d->m_irrelevantAspects == other->d->m_irrelevantAspects && d->m_mutable == other->d->m_mutable; - } QVariantMap Kit::toMap() const @@ -478,6 +524,7 @@ QVariantMap Kit::toMap() const data.insert(QLatin1String(AUTODETECTIONSOURCE_KEY), d->m_autoDetectionSource); data.insert(QLatin1String(SDK_PROVIDED_KEY), d->m_sdkProvided); data.insert(QLatin1String(ICON_KEY), d->m_iconPath.toString()); + data.insert(DEVICE_TYPE_FOR_ICON_KEY, d->m_deviceTypeForIcon.toSetting()); QStringList mutableInfo; foreach (Id id, d->m_mutable) @@ -489,6 +536,11 @@ QVariantMap Kit::toMap() const stickyInfo << id.toString(); data.insert(QLatin1String(STICKY_INFO_KEY), stickyInfo); + if (d->m_irrelevantAspects) { + data.insert(IRRELEVANT_ASPECTS_KEY, transform<QVariantList>(d->m_irrelevantAspects.value(), + &Id::toSetting)); + } + QVariantMap extra; const IdVariantConstIt cend = d->m_data.constEnd(); @@ -501,21 +553,19 @@ QVariantMap Kit::toMap() const void Kit::addToEnvironment(Environment &env) const { - QList<KitInformation *> infoList = KitManager::kitInformation(); - foreach (KitInformation *ki, infoList) - ki->addToEnvironment(this, env); + for (KitAspect *aspect : KitManager::kitAspects()) + aspect->addToEnvironment(this, env); } IOutputParser *Kit::createOutputParser() const { auto first = new OsParser; - QList<KitInformation *> infoList = KitManager::kitInformation(); - foreach (KitInformation *ki, infoList) - first->appendOutputParser(ki->createOutputParser(this)); + for (KitAspect *aspect : KitManager::kitAspects()) + first->appendOutputParser(aspect->createOutputParser(this)); return first; } -QString Kit::toHtml(const QList<Task> &additional) const +QString Kit::toHtml(const Tasks &additional) const { QString result; QTextStream str(&result); @@ -526,10 +576,9 @@ QString Kit::toHtml(const QList<Task> &additional) const str << "<p>" << ProjectExplorer::toHtml(additional + validate()) << "</p>"; str << "<table>"; - QList<KitInformation *> infoList = KitManager::kitInformation(); - foreach (KitInformation *ki, infoList) { - KitInformation::ItemList list = ki->toUserOutput(this); - foreach (const KitInformation::Item &j, list) { + for (KitAspect *aspect : KitManager::kitAspects()) { + const KitAspect::ItemList list = aspect->toUserOutput(this); + for (const KitAspect::Item &j : list) { QString contents = j.second; if (contents.count() > 256) { int pos = contents.lastIndexOf("<br>", 256); @@ -571,9 +620,9 @@ void Kit::setSdkProvided(bool sdkProvided) void Kit::makeSticky() { - foreach (KitInformation *ki, KitManager::kitInformation()) { - if (hasValue(ki->id())) - setSticky(ki->id(), true); + for (KitAspect *aspect : KitManager::kitAspects()) { + if (hasValue(aspect->id())) + setSticky(aspect->id(), true); } } @@ -614,11 +663,21 @@ bool Kit::isMutable(Id id) const return d->m_mutable.contains(id); } +void Kit::setIrrelevantAspects(const QSet<Id> &irrelevant) +{ + d->m_irrelevantAspects = irrelevant; +} + +QSet<Id> Kit::irrelevantAspects() const +{ + return d->m_irrelevantAspects.value_or(KitManager::irrelevantAspects()); +} + QSet<Id> Kit::supportedPlatforms() const { QSet<Id> platforms; - foreach (const KitInformation *ki, KitManager::kitInformation()) { - const QSet<Id> ip = ki->supportedPlatforms(this); + for (const KitAspect *aspect : KitManager::kitAspects()) { + const QSet<Id> ip = aspect->supportedPlatforms(this); if (ip.isEmpty()) continue; if (platforms.isEmpty()) @@ -632,8 +691,8 @@ QSet<Id> Kit::supportedPlatforms() const QSet<Id> Kit::availableFeatures() const { QSet<Id> features; - foreach (const KitInformation *ki, KitManager::kitInformation()) - features |= ki->availableFeatures(this); + for (const KitAspect *aspect : KitManager::kitAspects()) + features |= aspect->availableFeatures(this); return features; } @@ -647,6 +706,19 @@ MacroExpander *Kit::macroExpander() const return &d->m_macroExpander; } +QString Kit::newKitName(const QList<Kit *> &allKits) const +{ + return newKitName(unexpandedDisplayName(), allKits); +} + +QString Kit::newKitName(const QString &name, const QList<Kit *> &allKits) +{ + const QString baseName = name.isEmpty() + ? QCoreApplication::translate("ProjectExplorer::Kit", "Unnamed") + : QCoreApplication::translate("ProjectExplorer::Kit", "Clone of %1").arg(name); + return Utils::makeUniquelyNumbered(baseName, transform(allKits, &Kit::unexpandedDisplayName)); +} + void Kit::kitUpdated() { if (d->m_nestedBlockingLevel > 0) { diff --git a/src/plugins/projectexplorer/kit.h b/src/plugins/projectexplorer/kit.h index ab9b1681f6..8e45911d17 100644 --- a/src/plugins/projectexplorer/kit.h +++ b/src/plugins/projectexplorer/kit.h @@ -71,7 +71,7 @@ public: bool isValid() const; bool hasWarning() const; - QList<Task> validate() const; + Tasks validate() const; void fix(); // Fix the individual kit information: Make sure it contains a valid value. // Fix will not look at other information in the kit! void setup(); // Apply advanced magic(TM). Used only once on each kit during initial setup. @@ -90,9 +90,17 @@ public: bool isSdkProvided() const; Core::Id id() const; - QIcon icon() const; - Utils::FileName iconPath() const; - void setIconPath(const Utils::FileName &path); + // The higher the weight, the more aspects have sensible values for this kit. + // For instance, a kit where a matching debugger was found for the toolchain will have a + // higher weight than one whose toolchain does not match a known debugger, assuming + // all other aspects are equal. + int weight() const; + + QIcon icon() const; // Raw device icon, independent of warning or error. + QIcon displayIcon() const; // Error or warning or device icon. + Utils::FilePath iconPath() const; + void setIconPath(const Utils::FilePath &path); + void setDeviceTypeForIcon(Core::Id deviceType); QList<Core::Id> allKeys() const; QVariant value(Core::Id key, const QVariant &unset = QVariant()) const; @@ -109,7 +117,7 @@ public: void addToEnvironment(Utils::Environment &env) const; IOutputParser *createOutputParser() const; - QString toHtml(const QList<Task> &additional = QList<Task>()) const; + QString toHtml(const Tasks &additional = Tasks()) const; Kit *clone(bool keepName = false) const; void copyFrom(const Kit *k); @@ -123,12 +131,19 @@ public: void setMutable(Core::Id id, bool b); bool isMutable(Core::Id id) const; + void setIrrelevantAspects(const QSet<Core::Id> &irrelevant); + QSet<Core::Id> irrelevantAspects() const; + QSet<Core::Id> supportedPlatforms() const; QSet<Core::Id> availableFeatures() const; bool hasFeatures(const QSet<Core::Id> &features) const; Utils::MacroExpander *macroExpander() const; + QString newKitName(const QList<Kit *> &allKits) const; + static QString newKitName(const QString &name, const QList<Kit *> &allKits); + private: + static void copyKitCommon(Kit *target, const Kit *source); void setSdkProvided(bool sdkProvided); // Unimplemented. @@ -142,7 +157,7 @@ private: const std::unique_ptr<Internal::KitPrivate> d; - friend class KitInformation; + friend class KitAspect; friend class KitManager; friend class Internal::KitManagerPrivate; friend class Internal::KitModel; // needed for setAutoDetected() when cloning kits diff --git a/src/plugins/projectexplorer/kitchooser.cpp b/src/plugins/projectexplorer/kitchooser.cpp index 9e0b4b25f1..c3a0204fba 100644 --- a/src/plugins/projectexplorer/kitchooser.cpp +++ b/src/plugins/projectexplorer/kitchooser.cpp @@ -25,7 +25,6 @@ #include "kitchooser.h" -#include "kitconfigwidget.h" #include "kitinformation.h" #include "kitmanager.h" #include "project.h" @@ -52,7 +51,7 @@ KitChooser::KitChooser(QWidget *parent) : { m_chooser = new QComboBox(this); m_chooser->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - m_manageButton = new QPushButton(KitConfigWidget::msgManage(), this); + m_manageButton = new QPushButton(KitAspectWidget::msgManage(), this); auto layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -60,9 +59,9 @@ KitChooser::KitChooser(QWidget *parent) : layout->addWidget(m_manageButton); setFocusProxy(m_manageButton); - connect(m_chooser, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(m_chooser, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &KitChooser::onCurrentIndexChanged); - connect(m_chooser, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), + connect(m_chooser, QOverload<int>::of(&QComboBox::activated), this, &KitChooser::onActivated); connect(m_manageButton, &QAbstractButton::clicked, this, &KitChooser::onManageButtonClicked); connect(KitManager::instance(), &KitManager::kitsChanged, this, &KitChooser::populate); @@ -73,6 +72,11 @@ void KitChooser::onManageButtonClicked() Core::ICore::showOptionsDialog(Constants::KITS_SETTINGS_PAGE_ID, this); } +void KitChooser::setShowIcons(bool showIcons) +{ + m_showIcons = showIcons; +} + void KitChooser::onCurrentIndexChanged() { const Id id = Id::fromSetting(m_chooser->currentData()); @@ -128,9 +132,12 @@ void KitChooser::populate() foreach (Kit *kit, KitManager::sortKits(KitManager::kits())) { if (m_kitPredicate(kit)) { m_chooser->addItem(kitText(kit), kit->id().toSetting()); - m_chooser->setItemData(m_chooser->count() - 1, kitToolTip(kit), Qt::ToolTipRole); + const int pos = m_chooser->count() - 1; + m_chooser->setItemData(pos, kitToolTip(kit), Qt::ToolTipRole); + if (m_showIcons) + m_chooser->setItemData(pos, kit->displayIcon(), Qt::DecorationRole); if (!didActivate && kit->id() == lastKit) { - m_chooser->setCurrentIndex(m_chooser->count() - 1); + m_chooser->setCurrentIndex(pos); didActivate = true; } } diff --git a/src/plugins/projectexplorer/kitchooser.h b/src/plugins/projectexplorer/kitchooser.h index bd7b8ce5cf..046c18f571 100644 --- a/src/plugins/projectexplorer/kitchooser.h +++ b/src/plugins/projectexplorer/kitchooser.h @@ -54,8 +54,10 @@ public: Core::Id currentKitId() const; void setKitPredicate(const Kit::Predicate &predicate); + void setShowIcons(bool showIcons); Kit *currentKit() const; + bool hasStartupKit() const { return m_hasStartupKit; } signals: void currentIndexChanged(); @@ -77,6 +79,7 @@ private: QComboBox *m_chooser; QPushButton *m_manageButton; bool m_hasStartupKit = false; + bool m_showIcons = false; }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitinformation.cpp b/src/plugins/projectexplorer/kitinformation.cpp index f4da43f095..4ca258df1a 100644 --- a/src/plugins/projectexplorer/kitinformation.cpp +++ b/src/plugins/projectexplorer/kitinformation.cpp @@ -28,20 +28,32 @@ #include "abi.h" #include "devicesupport/desktopdevice.h" #include "devicesupport/devicemanager.h" +#include "devicesupport/devicemanagermodel.h" +#include "devicesupport/idevicefactory.h" #include "projectexplorerconstants.h" #include "kit.h" -#include "kitinformationconfigwidget.h" #include "toolchain.h" #include "toolchainmanager.h" +#include <coreplugin/icore.h> +#include <coreplugin/variablechooser.h> #include <ssh/sshconnection.h> - #include <utils/algorithm.h> +#include <utils/environment.h> +#include <utils/environmentdialog.h> #include <utils/macroexpander.h> +#include <utils/pathchooser.h> #include <utils/qtcassert.h> +#include <QCheckBox> +#include <QComboBox> #include <QDir> #include <QFileInfo> +#include <QFontMetrics> +#include <QGridLayout> +#include <QLabel> +#include <QPushButton> +#include <QVBoxLayout> namespace ProjectExplorer { @@ -50,26 +62,70 @@ const char KITINFORMATION_ID_V2[] = "PE.Profile.ToolChains"; const char KITINFORMATION_ID_V3[] = "PE.Profile.ToolChainsV3"; // -------------------------------------------------------------------------- -// SysRootKitInformation: +// SysRootKitAspect: // -------------------------------------------------------------------------- -SysRootKitInformation::SysRootKitInformation() +namespace Internal { +class SysRootKitAspectWidget : public KitAspectWidget { - setObjectName(QLatin1String("SysRootInformation")); - setId(SysRootKitInformation::id()); - setPriority(31000); -} + Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::SysRootKitAspect) + +public: + SysRootKitAspectWidget(Kit *k, const KitAspect *ki) : KitAspectWidget(k, ki) + { + m_chooser = new Utils::PathChooser; + m_chooser->setExpectedKind(Utils::PathChooser::ExistingDirectory); + m_chooser->setHistoryCompleter(QLatin1String("PE.SysRoot.History")); + m_chooser->setFileName(SysRootKitAspect::sysRoot(k)); + connect(m_chooser, &Utils::PathChooser::pathChanged, + this, &SysRootKitAspectWidget::pathWasChanged); + } + + ~SysRootKitAspectWidget() override { delete m_chooser; } + +private: + void makeReadOnly() override { m_chooser->setReadOnly(true); } + QWidget *buttonWidget() const override { return m_chooser->buttonAtIndex(0); } + QWidget *mainWidget() const override { return m_chooser->lineEdit(); } + + void refresh() override + { + if (!m_ignoreChange) + m_chooser->setFileName(SysRootKitAspect::sysRoot(m_kit)); + } + + void setPalette(const QPalette &p) override + { + KitAspectWidget::setPalette(p); + m_chooser->setOkColor(p.color(QPalette::Active, QPalette::Text)); + } + + void pathWasChanged() + { + m_ignoreChange = true; + SysRootKitAspect::setSysRoot(m_kit, m_chooser->fileName()); + m_ignoreChange = false; + } + + Utils::PathChooser *m_chooser; + bool m_ignoreChange = false; +}; +} // namespace Internal -QVariant SysRootKitInformation::defaultValue(const Kit *k) const +SysRootKitAspect::SysRootKitAspect() { - Q_UNUSED(k) - return QString(); + setObjectName(QLatin1String("SysRootInformation")); + setId(SysRootKitAspect::id()); + setDisplayName(tr("Sysroot")); + setDescription(tr("The root directory of the system image to use.<br>" + "Leave empty when building for the desktop.")); + setPriority(31000); } -QList<Task> SysRootKitInformation::validate(const Kit *k) const +Tasks SysRootKitAspect::validate(const Kit *k) const { - QList<Task> result; - const Utils::FileName dir = SysRootKitInformation::sysRoot(k); + Tasks result; + const Utils::FilePath dir = SysRootKitAspect::sysRoot(k); if (dir.isEmpty()) return result; @@ -80,65 +136,65 @@ QList<Task> SysRootKitInformation::validate(const Kit *k) const if (!fi.exists()) { result << Task(Task::Warning, tr("Sys Root \"%1\" does not exist in the file system.").arg(dir.toUserOutput()), - Utils::FileName(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)); + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)); } else if (!fi.isDir()) { result << Task(Task::Warning, tr("Sys Root \"%1\" is not a directory.").arg(dir.toUserOutput()), - Utils::FileName(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)); + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)); } else if (QDir(dir.toString()).entryList(QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty()) { result << Task(Task::Warning, tr("Sys Root \"%1\" is empty.").arg(dir.toUserOutput()), - Utils::FileName(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)); + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)); } return result; } -KitConfigWidget *SysRootKitInformation::createConfigWidget(Kit *k) const +KitAspectWidget *SysRootKitAspect::createConfigWidget(Kit *k) const { QTC_ASSERT(k, return nullptr); - return new Internal::SysRootInformationConfigWidget(k, this); + return new Internal::SysRootKitAspectWidget(k, this); } -KitInformation::ItemList SysRootKitInformation::toUserOutput(const Kit *k) const +KitAspect::ItemList SysRootKitAspect::toUserOutput(const Kit *k) const { - return ItemList() << qMakePair(tr("Sys Root"), sysRoot(k).toUserOutput()); + return {{tr("Sys Root"), sysRoot(k).toUserOutput()}}; } -void SysRootKitInformation::addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const +void SysRootKitAspect::addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const { QTC_ASSERT(kit, return); expander->registerFileVariables("SysRoot", tr("Sys Root"), [kit]() -> QString { - return SysRootKitInformation::sysRoot(kit).toString(); + return SysRootKitAspect::sysRoot(kit).toString(); }); } -Core::Id SysRootKitInformation::id() +Core::Id SysRootKitAspect::id() { return "PE.Profile.SysRoot"; } -Utils::FileName SysRootKitInformation::sysRoot(const Kit *k) +Utils::FilePath SysRootKitAspect::sysRoot(const Kit *k) { if (!k) - return Utils::FileName(); + return Utils::FilePath(); - if (!k->value(SysRootKitInformation::id()).toString().isEmpty()) - return Utils::FileName::fromString(k->value(SysRootKitInformation::id()).toString()); + if (!k->value(SysRootKitAspect::id()).toString().isEmpty()) + return Utils::FilePath::fromString(k->value(SysRootKitAspect::id()).toString()); - for (ToolChain *tc : ToolChainKitInformation::toolChains(k)) { + for (ToolChain *tc : ToolChainKitAspect::toolChains(k)) { if (!tc->sysRoot().isEmpty()) - return Utils::FileName::fromString(tc->sysRoot()); + return Utils::FilePath::fromString(tc->sysRoot()); } - return Utils::FileName(); + return Utils::FilePath(); } -void SysRootKitInformation::setSysRoot(Kit *k, const Utils::FileName &v) +void SysRootKitAspect::setSysRoot(Kit *k, const Utils::FilePath &v) { if (!k) return; - for (ToolChain *tc : ToolChainKitInformation::toolChains(k)) { + for (ToolChain *tc : ToolChainKitAspect::toolChains(k)) { if (!tc->sysRoot().isEmpty()) { // It's the sysroot from toolchain, don't set it. if (tc->sysRoot() == v.toString()) @@ -148,21 +204,145 @@ void SysRootKitInformation::setSysRoot(Kit *k, const Utils::FileName &v) break; } } - k->setValue(SysRootKitInformation::id(), v.toString()); + k->setValue(SysRootKitAspect::id(), v.toString()); } // -------------------------------------------------------------------------- -// ToolChainKitInformation: +// ToolChainKitAspect: // -------------------------------------------------------------------------- -ToolChainKitInformation::ToolChainKitInformation() +namespace Internal { +class ToolChainKitAspectWidget : public KitAspectWidget +{ + Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::ToolChainKitAspect) + +public: + ToolChainKitAspectWidget(Kit *k, const KitAspect *ki) : KitAspectWidget(k, ki) + { + m_mainWidget = new QWidget; + m_mainWidget->setContentsMargins(0, 0, 0, 0); + + auto layout = new QGridLayout(m_mainWidget); + layout->setContentsMargins(0, 0, 0, 0); + layout->setColumnStretch(1, 2); + + QList<Core::Id> languageList = ToolChainManager::allLanguages().toList(); + Utils::sort(languageList, [](Core::Id l1, Core::Id l2) { + return ToolChainManager::displayNameOfLanguageId(l1) + < ToolChainManager::displayNameOfLanguageId(l2); + }); + QTC_ASSERT(!languageList.isEmpty(), return); + int row = 0; + foreach (Core::Id l, languageList) { + layout->addWidget(new QLabel(ToolChainManager::displayNameOfLanguageId(l) + ':'), row, 0); + auto cb = new QComboBox; + cb->setSizePolicy(QSizePolicy::Ignored, cb->sizePolicy().verticalPolicy()); + cb->setToolTip(ki->description()); + + m_languageComboboxMap.insert(l, cb); + layout->addWidget(cb, row, 1); + ++row; + + connect(cb, QOverload<int>::of(&QComboBox::currentIndexChanged), + this, [this, l](int idx) { currentToolChainChanged(l, idx); }); + } + + refresh(); + + m_manageButton = new QPushButton(KitAspectWidget::msgManage()); + m_manageButton->setContentsMargins(0, 0, 0, 0); + connect(m_manageButton, &QAbstractButton::clicked, + this, &ToolChainKitAspectWidget::manageToolChains); + } + + ~ToolChainKitAspectWidget() override + { + delete m_mainWidget; + delete m_manageButton; + } + +private: + QWidget *mainWidget() const override { return m_mainWidget; } + QWidget *buttonWidget() const override { return m_manageButton; } + + void refresh() override + { + m_ignoreChanges = true; + foreach (Core::Id l, m_languageComboboxMap.keys()) { + const QList<ToolChain *> ltcList + = ToolChainManager::toolChains(Utils::equal(&ToolChain::language, l)); + + QComboBox *cb = m_languageComboboxMap.value(l); + cb->clear(); + cb->addItem(tr("<No compiler>"), QByteArray()); + + foreach (ToolChain *tc, ltcList) + cb->addItem(tc->displayName(), tc->id()); + + cb->setEnabled(cb->count() > 1 && !m_isReadOnly); + const int index = indexOf(cb, ToolChainKitAspect::toolChain(m_kit, l)); + cb->setCurrentIndex(index); + } + m_ignoreChanges = false; + } + + void makeReadOnly() override + { + m_isReadOnly = true; + foreach (Core::Id l, m_languageComboboxMap.keys()) { + m_languageComboboxMap.value(l)->setEnabled(false); + } + } + + void manageToolChains() + { + Core::ICore::showOptionsDialog(Constants::TOOLCHAIN_SETTINGS_PAGE_ID, buttonWidget()); + } + + void currentToolChainChanged(Core::Id language, int idx) + { + if (m_ignoreChanges || idx < 0) + return; + + const QByteArray id = m_languageComboboxMap.value(language)->itemData(idx).toByteArray(); + ToolChain *tc = ToolChainManager::findToolChain(id); + QTC_ASSERT(!tc || tc->language() == language, return); + if (tc) + ToolChainKitAspect::setToolChain(m_kit, tc); + else + ToolChainKitAspect::clearToolChain(m_kit, language); + } + + int indexOf(QComboBox *cb, const ToolChain *tc) + { + const QByteArray id = tc ? tc->id() : QByteArray(); + for (int i = 0; i < cb->count(); ++i) { + if (id == cb->itemData(i).toByteArray()) + return i; + } + return -1; + } + + QWidget *m_mainWidget = nullptr; + QPushButton *m_manageButton = nullptr; + QHash<Core::Id, QComboBox *> m_languageComboboxMap; + bool m_ignoreChanges = false; + bool m_isReadOnly = false; +}; +} // namespace Internal + +ToolChainKitAspect::ToolChainKitAspect() { setObjectName(QLatin1String("ToolChainInformation")); - setId(ToolChainKitInformation::id()); + setId(ToolChainKitAspect::id()); + setDisplayName(tr("Compiler")); + setDescription(tr("The compiler to use for building.<br>" + "Make sure the compiler will produce binaries compatible " + "with the target device, Qt version and other libraries used.")); setPriority(30000); connect(KitManager::instance(), &KitManager::kitsLoaded, - this, &ToolChainKitInformation::kitsWereLoaded); + this, &ToolChainKitAspect::kitsWereLoaded); } // language id -> tool chain id @@ -189,20 +369,14 @@ static QVariant defaultToolChainValue() return result; } -QVariant ToolChainKitInformation::defaultValue(const Kit *k) const +Tasks ToolChainKitAspect::validate(const Kit *k) const { - Q_UNUSED(k); - return defaultToolChainValue(); -} - -QList<Task> ToolChainKitInformation::validate(const Kit *k) const -{ - QList<Task> result; + Tasks result; const QList<ToolChain*> tcList = toolChains(k); if (tcList.isEmpty()) { - result << Task(Task::Warning, ToolChainKitInformation::msgNoToolChainInTarget(), - Utils::FileName(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)); + result << Task(Task::Warning, ToolChainKitAspect::msgNoToolChainInTarget(), + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)); } else { QSet<Abi> targetAbis; foreach (ToolChain *tc, tcList) { @@ -212,13 +386,13 @@ QList<Task> ToolChainKitInformation::validate(const Kit *k) const if (targetAbis.count() != 1) { result << Task(Task::Error, tr("Compilers produce code for different ABIs: %1") .arg(Utils::transform(targetAbis, &Abi::toString).toList().join(", ")), - Utils::FileName(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)); + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)); } } return result; } -void ToolChainKitInformation::upgrade(Kit *k) +void ToolChainKitAspect::upgrade(Kit *k) { QTC_ASSERT(k, return); @@ -238,7 +412,7 @@ void ToolChainKitInformation::upgrade(Kit *k) // Used up to 4.1: newValue.insert(Deprecated::Toolchain::languageId(Deprecated::Toolchain::Cxx), oldValue.toString()); - const Core::Id typeId = DeviceTypeKitInformation::deviceTypeId(k); + const Core::Id typeId = DeviceTypeKitAspect::deviceTypeId(k); if (typeId == Constants::DESKTOP_DEVICE_TYPE) { // insert default C compiler which did not exist before newValue.insert(Deprecated::Toolchain::languageId(Deprecated::Toolchain::C), @@ -253,7 +427,7 @@ void ToolChainKitInformation::upgrade(Kit *k) // upgrade 4.2 to 4.3 (keep old settings around for now) { const QVariant oldValue = k->value(oldIdV2); - const QVariant value = k->value(ToolChainKitInformation::id()); + const QVariant value = k->value(ToolChainKitAspect::id()); if (value.isNull() && !oldValue.isNull()) { QVariantMap newValue = oldValue.toMap(); QVariantMap::iterator it = newValue.find(Deprecated::Toolchain::languageId(Deprecated::Toolchain::C)); @@ -262,14 +436,14 @@ void ToolChainKitInformation::upgrade(Kit *k) it = newValue.find(Deprecated::Toolchain::languageId(Deprecated::Toolchain::Cxx)); if (it != newValue.end()) newValue.insert(Core::Id(Constants::CXX_LANGUAGE_ID).toString(), it.value()); - k->setValue(ToolChainKitInformation::id(), newValue); - k->setSticky(ToolChainKitInformation::id(), k->isSticky(oldIdV2)); + k->setValue(ToolChainKitAspect::id(), newValue); + k->setSticky(ToolChainKitAspect::id(), k->isSticky(oldIdV2)); } } // upgrade 4.3-temporary-master-state to 4.3: { - const QVariantMap valueMap = k->value(ToolChainKitInformation::id()).toMap(); + const QVariantMap valueMap = k->value(ToolChainKitAspect::id()).toMap(); QVariantMap result; for (const QString &key : valueMap.keys()) { const int pos = key.lastIndexOf('.'); @@ -278,11 +452,11 @@ void ToolChainKitInformation::upgrade(Kit *k) else result.insert(key, valueMap.value(key)); } - k->setValue(ToolChainKitInformation::id(), result); + k->setValue(ToolChainKitAspect::id(), result); } } -void ToolChainKitInformation::fix(Kit *k) +void ToolChainKitAspect::fix(Kit *k) { QTC_ASSERT(ToolChainManager::isLoaded(), return); foreach (const Core::Id& l, ToolChainManager::allLanguages()) { @@ -303,12 +477,14 @@ static Core::Id findLanguage(const QString &ls) [lsUpper](Core::Id l) { return lsUpper == l.toString().toUpper(); }); } -void ToolChainKitInformation::setup(Kit *k) +void ToolChainKitAspect::setup(Kit *k) { QTC_ASSERT(ToolChainManager::isLoaded(), return); QTC_ASSERT(k, return); - const QVariantMap value = k->value(ToolChainKitInformation::id()).toMap(); + QVariantMap value = k->value(id()).toMap(); + if (value.empty()) + value = defaultToolChainValue().toMap(); for (auto i = value.constBegin(); i != value.constEnd(); ++i) { Core::Id l = findLanguage(i.key()); @@ -333,32 +509,32 @@ void ToolChainKitInformation::setup(Kit *k) } } -KitConfigWidget *ToolChainKitInformation::createConfigWidget(Kit *k) const +KitAspectWidget *ToolChainKitAspect::createConfigWidget(Kit *k) const { QTC_ASSERT(k, return nullptr); - return new Internal::ToolChainInformationConfigWidget(k, this); + return new Internal::ToolChainKitAspectWidget(k, this); } -QString ToolChainKitInformation::displayNamePostfix(const Kit *k) const +QString ToolChainKitAspect::displayNamePostfix(const Kit *k) const { ToolChain *tc = toolChain(k, Constants::CXX_LANGUAGE_ID); return tc ? tc->displayName() : QString(); } -KitInformation::ItemList ToolChainKitInformation::toUserOutput(const Kit *k) const +KitAspect::ItemList ToolChainKitAspect::toUserOutput(const Kit *k) const { ToolChain *tc = toolChain(k, Constants::CXX_LANGUAGE_ID); - return ItemList() << qMakePair(tr("Compiler"), tc ? tc->displayName() : tr("None")); + return {{tr("Compiler"), tc ? tc->displayName() : tr("None")}}; } -void ToolChainKitInformation::addToEnvironment(const Kit *k, Utils::Environment &env) const +void ToolChainKitAspect::addToEnvironment(const Kit *k, Utils::Environment &env) const { ToolChain *tc = toolChain(k, Constants::CXX_LANGUAGE_ID); if (tc) tc->addToEnvironment(env); } -void ToolChainKitInformation::addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const +void ToolChainKitAspect::addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const { QTC_ASSERT(kit, return); @@ -388,7 +564,7 @@ void ToolChainKitInformation::addToMacroExpander(Kit *kit, Utils::MacroExpander } -IOutputParser *ToolChainKitInformation::createOutputParser(const Kit *k) const +IOutputParser *ToolChainKitAspect::createOutputParser(const Kit *k) const { for (const Core::Id langId : {Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}) { if (const ToolChain * const tc = toolChain(k, langId)) @@ -397,7 +573,7 @@ IOutputParser *ToolChainKitInformation::createOutputParser(const Kit *k) const return nullptr; } -QSet<Core::Id> ToolChainKitInformation::availableFeatures(const Kit *k) const +QSet<Core::Id> ToolChainKitAspect::availableFeatures(const Kit *k) const { QSet<Core::Id> result; for (ToolChain *tc : toolChains(k)) @@ -405,30 +581,30 @@ QSet<Core::Id> ToolChainKitInformation::availableFeatures(const Kit *k) const return result; } -Core::Id ToolChainKitInformation::id() +Core::Id ToolChainKitAspect::id() { return KITINFORMATION_ID_V3; } -QByteArray ToolChainKitInformation::toolChainId(const Kit *k, Core::Id language) +QByteArray ToolChainKitAspect::toolChainId(const Kit *k, Core::Id language) { QTC_ASSERT(ToolChainManager::isLoaded(), return nullptr); if (!k) return QByteArray(); - QVariantMap value = k->value(ToolChainKitInformation::id()).toMap(); + QVariantMap value = k->value(ToolChainKitAspect::id()).toMap(); return value.value(language.toString(), QByteArray()).toByteArray(); } -ToolChain *ToolChainKitInformation::toolChain(const Kit *k, Core::Id language) +ToolChain *ToolChainKitAspect::toolChain(const Kit *k, Core::Id language) { return ToolChainManager::findToolChain(toolChainId(k, language)); } -QList<ToolChain *> ToolChainKitInformation::toolChains(const Kit *k) +QList<ToolChain *> ToolChainKitAspect::toolChains(const Kit *k) { QTC_ASSERT(k, return QList<ToolChain *>()); - const QVariantMap value = k->value(ToolChainKitInformation::id()).toMap(); + const QVariantMap value = k->value(ToolChainKitAspect::id()).toMap(); const QList<ToolChain *> tcList = Utils::transform(ToolChainManager::allLanguages().toList(), [&value](Core::Id l) -> ToolChain * { @@ -437,18 +613,18 @@ QList<ToolChain *> ToolChainKitInformation::toolChains(const Kit *k) return Utils::filtered(tcList, [](ToolChain *tc) { return tc; }); } -void ToolChainKitInformation::setToolChain(Kit *k, ToolChain *tc) +void ToolChainKitAspect::setToolChain(Kit *k, ToolChain *tc) { QTC_ASSERT(tc, return); QTC_ASSERT(k, return); - QVariantMap result = k->value(ToolChainKitInformation::id()).toMap(); + QVariantMap result = k->value(ToolChainKitAspect::id()).toMap(); result.insert(tc->language().toString(), tc->id()); k->setValue(id(), result); } /** - * @brief ToolChainKitInformation::setAllToolChainsToMatch + * @brief ToolChainKitAspect::setAllToolChainsToMatch * * Set up all toolchains to be similar to the one toolchain provided. Similar ideally means * that all toolchains use the "same" compiler from the same installation, but we will @@ -457,7 +633,7 @@ void ToolChainKitInformation::setToolChain(Kit *k, ToolChain *tc) * @param k The kit to set up * @param tc The toolchain to match other languages for. */ -void ToolChainKitInformation::setAllToolChainsToMatch(Kit *k, ToolChain *tc) +void ToolChainKitAspect::setAllToolChainsToMatch(Kit *k, ToolChain *tc) { QTC_ASSERT(tc, return); QTC_ASSERT(k, return); @@ -465,7 +641,7 @@ void ToolChainKitInformation::setAllToolChainsToMatch(Kit *k, ToolChain *tc) const QList<ToolChain *> allTcList = ToolChainManager::toolChains(); QTC_ASSERT(allTcList.contains(tc), return); - QVariantMap result = k->value(ToolChainKitInformation::id()).toMap(); + QVariantMap result = k->value(ToolChainKitAspect::id()).toMap(); result.insert(tc->language().toString(), tc->id()); for (Core::Id l : ToolChainManager::allLanguages()) { @@ -496,17 +672,17 @@ void ToolChainKitInformation::setAllToolChainsToMatch(Kit *k, ToolChain *tc) k->setValue(id(), result); } -void ToolChainKitInformation::clearToolChain(Kit *k, Core::Id language) +void ToolChainKitAspect::clearToolChain(Kit *k, Core::Id language) { QTC_ASSERT(language.isValid(), return); QTC_ASSERT(k, return); - QVariantMap result = k->value(ToolChainKitInformation::id()).toMap(); + QVariantMap result = k->value(ToolChainKitAspect::id()).toMap(); result.insert(language.toString(), QByteArray()); k->setValue(id(), result); } -Abi ToolChainKitInformation::targetAbi(const Kit *k) +Abi ToolChainKitAspect::targetAbi(const Kit *k) { QList<ToolChain *> tcList = toolChains(k); // Find the best possible ABI for all the tool chains... @@ -539,29 +715,29 @@ Abi ToolChainKitInformation::targetAbi(const Kit *k) return candidates.at(0); // Use basically a random Abi... } -QString ToolChainKitInformation::msgNoToolChainInTarget() +QString ToolChainKitAspect::msgNoToolChainInTarget() { return tr("No compiler set in kit."); } -void ToolChainKitInformation::kitsWereLoaded() +void ToolChainKitAspect::kitsWereLoaded() { foreach (Kit *k, KitManager::kits()) fix(k); connect(ToolChainManager::instance(), &ToolChainManager::toolChainRemoved, - this, &ToolChainKitInformation::toolChainRemoved); + this, &ToolChainKitAspect::toolChainRemoved); connect(ToolChainManager::instance(), &ToolChainManager::toolChainUpdated, - this, &ToolChainKitInformation::toolChainUpdated); + this, &ToolChainKitAspect::toolChainUpdated); } -void ToolChainKitInformation::toolChainUpdated(ToolChain *tc) +void ToolChainKitAspect::toolChainUpdated(ToolChain *tc) { for (Kit *k : KitManager::kits([tc](const Kit *k) { return toolChain(k, tc->language()) == tc; })) notifyAboutUpdate(k); } -void ToolChainKitInformation::toolChainRemoved(ToolChain *tc) +void ToolChainKitAspect::toolChainRemoved(ToolChain *tc) { Q_UNUSED(tc); foreach (Kit *k, KitManager::kits()) @@ -569,35 +745,83 @@ void ToolChainKitInformation::toolChainRemoved(ToolChain *tc) } // -------------------------------------------------------------------------- -// DeviceTypeKitInformation: +// DeviceTypeKitAspect: // -------------------------------------------------------------------------- +namespace Internal { +class DeviceTypeKitAspectWidget : public KitAspectWidget +{ + Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::DeviceTypeKitAspect) + +public: + DeviceTypeKitAspectWidget(Kit *workingCopy, const KitAspect *ki) + : KitAspectWidget(workingCopy, ki), m_comboBox(new QComboBox) + { + for (IDeviceFactory *factory : IDeviceFactory::allDeviceFactories()) + m_comboBox->addItem(factory->displayName(), factory->deviceType().toSetting()); + m_comboBox->setToolTip(ki->description()); + refresh(); + connect(m_comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), + this, &DeviceTypeKitAspectWidget::currentTypeChanged); + } -DeviceTypeKitInformation::DeviceTypeKitInformation() + ~DeviceTypeKitAspectWidget() override { delete m_comboBox; } + +private: + QWidget *mainWidget() const override { return m_comboBox; } + void makeReadOnly() override { m_comboBox->setEnabled(false); } + + void refresh() override + { + Core::Id devType = DeviceTypeKitAspect::deviceTypeId(m_kit); + if (!devType.isValid()) + m_comboBox->setCurrentIndex(-1); + for (int i = 0; i < m_comboBox->count(); ++i) { + if (m_comboBox->itemData(i) == devType.toSetting()) { + m_comboBox->setCurrentIndex(i); + break; + } + } + } + + void currentTypeChanged(int idx) + { + Core::Id type = idx < 0 ? Core::Id() : Core::Id::fromSetting(m_comboBox->itemData(idx)); + DeviceTypeKitAspect::setDeviceTypeId(m_kit, type); + } + + QComboBox *m_comboBox; +}; +} // namespace Internal + +DeviceTypeKitAspect::DeviceTypeKitAspect() { setObjectName(QLatin1String("DeviceTypeInformation")); - setId(DeviceTypeKitInformation::id()); + setId(DeviceTypeKitAspect::id()); + setDisplayName(tr("Device type")); + setDescription(tr("The type of device to run applications on.")); setPriority(33000); + makeEssential(); } -QVariant DeviceTypeKitInformation::defaultValue(const Kit *k) const +void DeviceTypeKitAspect::setup(Kit *k) { - Q_UNUSED(k); - return QByteArray(Constants::DESKTOP_DEVICE_TYPE); + if (k && !k->hasValue(id())) + k->setValue(id(), QByteArray(Constants::DESKTOP_DEVICE_TYPE)); } -QList<Task> DeviceTypeKitInformation::validate(const Kit *k) const +Tasks DeviceTypeKitAspect::validate(const Kit *k) const { Q_UNUSED(k); - return QList<Task>(); + return {}; } -KitConfigWidget *DeviceTypeKitInformation::createConfigWidget(Kit *k) const +KitAspectWidget *DeviceTypeKitAspect::createConfigWidget(Kit *k) const { QTC_ASSERT(k, return nullptr); - return new Internal::DeviceTypeInformationConfigWidget(k, this); + return new Internal::DeviceTypeKitAspectWidget(k, this); } -KitInformation::ItemList DeviceTypeKitInformation::toUserOutput(const Kit *k) const +KitAspect::ItemList DeviceTypeKitAspect::toUserOutput(const Kit *k) const { QTC_ASSERT(k, return {}); Core::Id type = deviceTypeId(k); @@ -606,60 +830,133 @@ KitInformation::ItemList DeviceTypeKitInformation::toUserOutput(const Kit *k) co if (IDeviceFactory *factory = IDeviceFactory::find(type)) typeDisplayName = factory->displayName(); } - return ItemList() << qMakePair(tr("Device type"), typeDisplayName); + return {{tr("Device type"), typeDisplayName}}; } -const Core::Id DeviceTypeKitInformation::id() +const Core::Id DeviceTypeKitAspect::id() { return "PE.Profile.DeviceType"; } -const Core::Id DeviceTypeKitInformation::deviceTypeId(const Kit *k) +const Core::Id DeviceTypeKitAspect::deviceTypeId(const Kit *k) { - return k ? Core::Id::fromSetting(k->value(DeviceTypeKitInformation::id())) : Core::Id(); + return k ? Core::Id::fromSetting(k->value(DeviceTypeKitAspect::id())) : Core::Id(); } -void DeviceTypeKitInformation::setDeviceTypeId(Kit *k, Core::Id type) +void DeviceTypeKitAspect::setDeviceTypeId(Kit *k, Core::Id type) { QTC_ASSERT(k, return); - k->setValue(DeviceTypeKitInformation::id(), type.toSetting()); + k->setValue(DeviceTypeKitAspect::id(), type.toSetting()); } -Kit::Predicate DeviceTypeKitInformation::deviceTypePredicate(Core::Id type) -{ - return [type](const Kit *kit) { return type.isValid() && deviceTypeId(kit) == type; }; -} - -QSet<Core::Id> DeviceTypeKitInformation::supportedPlatforms(const Kit *k) const +QSet<Core::Id> DeviceTypeKitAspect::supportedPlatforms(const Kit *k) const { return {deviceTypeId(k)}; } -QSet<Core::Id> DeviceTypeKitInformation::availableFeatures(const Kit *k) const +QSet<Core::Id> DeviceTypeKitAspect::availableFeatures(const Kit *k) const { - Core::Id id = DeviceTypeKitInformation::deviceTypeId(k); + Core::Id id = DeviceTypeKitAspect::deviceTypeId(k); if (id.isValid()) return {id.withPrefix("DeviceType.")}; return QSet<Core::Id>(); } // -------------------------------------------------------------------------- -// DeviceKitInformation: +// DeviceKitAspect: // -------------------------------------------------------------------------- +namespace Internal { +class DeviceKitAspectWidget : public KitAspectWidget +{ + Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::DeviceKitAspect) -DeviceKitInformation::DeviceKitInformation() +public: + DeviceKitAspectWidget(Kit *workingCopy, const KitAspect *ki) + : KitAspectWidget(workingCopy, ki), m_comboBox(new QComboBox), + m_model(new DeviceManagerModel(DeviceManager::instance())) + { + m_comboBox->setSizePolicy(QSizePolicy::Ignored, m_comboBox->sizePolicy().verticalPolicy()); + m_comboBox->setModel(m_model); + m_manageButton = new QPushButton(KitAspectWidget::msgManage()); + refresh(); + m_comboBox->setToolTip(ki->description()); + + connect(m_model, &QAbstractItemModel::modelAboutToBeReset, + this, &DeviceKitAspectWidget::modelAboutToReset); + connect(m_model, &QAbstractItemModel::modelReset, + this, &DeviceKitAspectWidget::modelReset); + connect(m_comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), + this, &DeviceKitAspectWidget::currentDeviceChanged); + connect(m_manageButton, &QAbstractButton::clicked, + this, &DeviceKitAspectWidget::manageDevices); + } + + ~DeviceKitAspectWidget() override + { + delete m_comboBox; + delete m_model; + delete m_manageButton; + } + +private: + QWidget *mainWidget() const override { return m_comboBox; } + QWidget *buttonWidget() const override { return m_manageButton; } + void makeReadOnly() override { m_comboBox->setEnabled(false); } + + void refresh() override + { + m_model->setTypeFilter(DeviceTypeKitAspect::deviceTypeId(m_kit)); + m_comboBox->setCurrentIndex(m_model->indexOf(DeviceKitAspect::device(m_kit))); + } + + void manageDevices() + { + Core::ICore::showOptionsDialog(Constants::DEVICE_SETTINGS_PAGE_ID, buttonWidget()); + } + + void modelAboutToReset() + { + m_selectedId = m_model->deviceId(m_comboBox->currentIndex()); + m_ignoreChange = true; + } + + void modelReset() + { + m_comboBox->setCurrentIndex(m_model->indexForId(m_selectedId)); + m_ignoreChange = false; + } + + void currentDeviceChanged() + { + if (m_ignoreChange) + return; + DeviceKitAspect::setDeviceId(m_kit, m_model->deviceId(m_comboBox->currentIndex())); + } + + bool m_isReadOnly = false; + bool m_ignoreChange = false; + QComboBox *m_comboBox; + QPushButton *m_manageButton; + DeviceManagerModel *m_model; + Core::Id m_selectedId; +}; +} // namespace Internal + +DeviceKitAspect::DeviceKitAspect() { setObjectName(QLatin1String("DeviceInformation")); - setId(DeviceKitInformation::id()); + setId(DeviceKitAspect::id()); + setDisplayName(tr("Device")); + setDescription(tr("The device to run the applications on.")); setPriority(32000); connect(KitManager::instance(), &KitManager::kitsLoaded, - this, &DeviceKitInformation::kitsWereLoaded); + this, &DeviceKitAspect::kitsWereLoaded); } -QVariant DeviceKitInformation::defaultValue(const Kit *k) const +QVariant DeviceKitAspect::defaultValue(const Kit *k) const { - Core::Id type = DeviceTypeKitInformation::deviceTypeId(k); + Core::Id type = DeviceTypeKitAspect::deviceTypeId(k); // Use default device if that is compatible: IDevice::ConstPtr dev = DeviceManager::instance()->defaultDevice(type); if (dev && dev->isCompatibleWith(k)) @@ -674,23 +971,23 @@ QVariant DeviceKitInformation::defaultValue(const Kit *k) const return QString(); } -QList<Task> DeviceKitInformation::validate(const Kit *k) const +Tasks DeviceKitAspect::validate(const Kit *k) const { - IDevice::ConstPtr dev = DeviceKitInformation::device(k); - QList<Task> result; + IDevice::ConstPtr dev = DeviceKitAspect::device(k); + Tasks result; if (dev.isNull()) result.append(Task(Task::Warning, tr("No device set."), - Utils::FileName(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))); + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))); else if (!dev->isCompatibleWith(k)) result.append(Task(Task::Error, tr("Device is incompatible with this kit."), - Utils::FileName(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))); + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))); return result; } -void DeviceKitInformation::fix(Kit *k) +void DeviceKitAspect::fix(Kit *k) { - IDevice::ConstPtr dev = DeviceKitInformation::device(k); + IDevice::ConstPtr dev = DeviceKitAspect::device(k); if (!dev.isNull() && !dev->isCompatibleWith(k)) { qWarning("Device is no longer compatible with kit \"%s\", removing it.", qPrintable(k->displayName())); @@ -698,109 +995,109 @@ void DeviceKitInformation::fix(Kit *k) } } -void DeviceKitInformation::setup(Kit *k) +void DeviceKitAspect::setup(Kit *k) { QTC_ASSERT(DeviceManager::instance()->isLoaded(), return); - IDevice::ConstPtr dev = DeviceKitInformation::device(k); + IDevice::ConstPtr dev = DeviceKitAspect::device(k); if (!dev.isNull() && dev->isCompatibleWith(k)) return; setDeviceId(k, Core::Id::fromSetting(defaultValue(k))); } -KitConfigWidget *DeviceKitInformation::createConfigWidget(Kit *k) const +KitAspectWidget *DeviceKitAspect::createConfigWidget(Kit *k) const { QTC_ASSERT(k, return nullptr); - return new Internal::DeviceInformationConfigWidget(k, this); + return new Internal::DeviceKitAspectWidget(k, this); } -QString DeviceKitInformation::displayNamePostfix(const Kit *k) const +QString DeviceKitAspect::displayNamePostfix(const Kit *k) const { IDevice::ConstPtr dev = device(k); return dev.isNull() ? QString() : dev->displayName(); } -KitInformation::ItemList DeviceKitInformation::toUserOutput(const Kit *k) const +KitAspect::ItemList DeviceKitAspect::toUserOutput(const Kit *k) const { IDevice::ConstPtr dev = device(k); - return ItemList() << qMakePair(tr("Device"), dev.isNull() ? tr("Unconfigured") : dev->displayName()); + return {{tr("Device"), dev.isNull() ? tr("Unconfigured") : dev->displayName()}}; } -void DeviceKitInformation::addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const +void DeviceKitAspect::addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const { QTC_ASSERT(kit, return); expander->registerVariable("Device:HostAddress", tr("Host address"), [kit]() -> QString { - const IDevice::ConstPtr device = DeviceKitInformation::device(kit); + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); return device ? device->sshParameters().host() : QString(); }); expander->registerVariable("Device:SshPort", tr("SSH port"), [kit]() -> QString { - const IDevice::ConstPtr device = DeviceKitInformation::device(kit); + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); return device ? QString::number(device->sshParameters().port()) : QString(); }); expander->registerVariable("Device:UserName", tr("User name"), [kit]() -> QString { - const IDevice::ConstPtr device = DeviceKitInformation::device(kit); + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); return device ? device->sshParameters().userName() : QString(); }); expander->registerVariable("Device:KeyFile", tr("Private key file"), [kit]() -> QString { - const IDevice::ConstPtr device = DeviceKitInformation::device(kit); + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); return device ? device->sshParameters().privateKeyFile : QString(); }); expander->registerVariable("Device:Name", tr("Device name"), [kit]() -> QString { - const IDevice::ConstPtr device = DeviceKitInformation::device(kit); + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); return device ? device->displayName() : QString(); }); } -Core::Id DeviceKitInformation::id() +Core::Id DeviceKitAspect::id() { return "PE.Profile.Device"; } -IDevice::ConstPtr DeviceKitInformation::device(const Kit *k) +IDevice::ConstPtr DeviceKitAspect::device(const Kit *k) { QTC_ASSERT(DeviceManager::instance()->isLoaded(), return IDevice::ConstPtr()); return DeviceManager::instance()->find(deviceId(k)); } -Core::Id DeviceKitInformation::deviceId(const Kit *k) +Core::Id DeviceKitAspect::deviceId(const Kit *k) { - return k ? Core::Id::fromSetting(k->value(DeviceKitInformation::id())) : Core::Id(); + return k ? Core::Id::fromSetting(k->value(DeviceKitAspect::id())) : Core::Id(); } -void DeviceKitInformation::setDevice(Kit *k, IDevice::ConstPtr dev) +void DeviceKitAspect::setDevice(Kit *k, IDevice::ConstPtr dev) { setDeviceId(k, dev ? dev->id() : Core::Id()); } -void DeviceKitInformation::setDeviceId(Kit *k, Core::Id id) +void DeviceKitAspect::setDeviceId(Kit *k, Core::Id id) { QTC_ASSERT(k, return); - k->setValue(DeviceKitInformation::id(), id.toSetting()); + k->setValue(DeviceKitAspect::id(), id.toSetting()); } -void DeviceKitInformation::kitsWereLoaded() +void DeviceKitAspect::kitsWereLoaded() { foreach (Kit *k, KitManager::kits()) fix(k); DeviceManager *dm = DeviceManager::instance(); - connect(dm, &DeviceManager::deviceListReplaced, this, &DeviceKitInformation::devicesChanged); - connect(dm, &DeviceManager::deviceAdded, this, &DeviceKitInformation::devicesChanged); - connect(dm, &DeviceManager::deviceRemoved, this, &DeviceKitInformation::devicesChanged); - connect(dm, &DeviceManager::deviceUpdated, this, &DeviceKitInformation::deviceUpdated); + connect(dm, &DeviceManager::deviceListReplaced, this, &DeviceKitAspect::devicesChanged); + connect(dm, &DeviceManager::deviceAdded, this, &DeviceKitAspect::devicesChanged); + connect(dm, &DeviceManager::deviceRemoved, this, &DeviceKitAspect::devicesChanged); + connect(dm, &DeviceManager::deviceUpdated, this, &DeviceKitAspect::deviceUpdated); connect(KitManager::instance(), &KitManager::kitUpdated, - this, &DeviceKitInformation::kitUpdated); + this, &DeviceKitAspect::kitUpdated); connect(KitManager::instance(), &KitManager::unmanagedKitUpdated, - this, &DeviceKitInformation::kitUpdated); + this, &DeviceKitAspect::kitUpdated); } -void DeviceKitInformation::deviceUpdated(Core::Id id) +void DeviceKitAspect::deviceUpdated(Core::Id id) { foreach (Kit *k, KitManager::kits()) { if (deviceId(k) == id) @@ -808,59 +1105,160 @@ void DeviceKitInformation::deviceUpdated(Core::Id id) } } -void DeviceKitInformation::kitUpdated(Kit *k) +void DeviceKitAspect::kitUpdated(Kit *k) { setup(k); // Set default device if necessary } -void DeviceKitInformation::devicesChanged() +void DeviceKitAspect::devicesChanged() { foreach (Kit *k, KitManager::kits()) setup(k); // Set default device if necessary } // -------------------------------------------------------------------------- -// EnvironmentKitInformation: +// EnvironmentKitAspect: // -------------------------------------------------------------------------- - -EnvironmentKitInformation::EnvironmentKitInformation() +namespace Internal { +class EnvironmentKitAspectWidget : public KitAspectWidget { - setObjectName(QLatin1String("EnvironmentKitInformation")); - setId(EnvironmentKitInformation::id()); - setPriority(29000); -} + Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::EnvironmentKitAspect) -QVariant EnvironmentKitInformation::defaultValue(const Kit *k) const +public: + EnvironmentKitAspectWidget(Kit *workingCopy, const KitAspect *ki) + : KitAspectWidget(workingCopy, ki), + m_summaryLabel(new QLabel), + m_manageButton(new QPushButton), + m_mainWidget(new QWidget) + { + auto *layout = new QVBoxLayout; + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(m_summaryLabel); + if (Utils::HostOsInfo::isWindowsHost()) + initMSVCOutputSwitch(layout); + m_mainWidget->setLayout(layout); + refresh(); + m_manageButton->setText(tr("Change...")); + connect(m_manageButton, &QAbstractButton::clicked, + this, &EnvironmentKitAspectWidget::editEnvironmentChanges); + } + +private: + QWidget *mainWidget() const override { return m_mainWidget; } + QWidget *buttonWidget() const override { return m_manageButton; } + void makeReadOnly() override { m_manageButton->setEnabled(false); } + + void refresh() override + { + const QList<Utils::EnvironmentItem> changes = currentEnvironment(); + QString shortSummary = Utils::EnvironmentItem::toStringList(changes).join(QLatin1String("; ")); + QFontMetrics fm(m_summaryLabel->font()); + shortSummary = fm.elidedText(shortSummary, Qt::ElideRight, m_summaryLabel->width()); + m_summaryLabel->setText(shortSummary.isEmpty() ? tr("No changes to apply.") : shortSummary); + } + + void editEnvironmentChanges() + { + bool ok; + Utils::MacroExpander *expander = m_kit->macroExpander(); + Utils::EnvironmentDialog::Polisher polisher = [expander](QWidget *w) { + Core::VariableChooser::addSupportForChildWidgets(w, expander); + }; + QList<Utils::EnvironmentItem> + changes = Utils::EnvironmentDialog::getEnvironmentItems(&ok, + m_summaryLabel, + currentEnvironment(), + QString(), + polisher); + if (!ok) + return; + + if (Utils::HostOsInfo::isWindowsHost()) { + const Utils::EnvironmentItem forceMSVCEnglishItem("VSLANG", "1033"); + if (m_vslangCheckbox->isChecked() && changes.indexOf(forceMSVCEnglishItem) < 0) + changes.append(forceMSVCEnglishItem); + } + + EnvironmentKitAspect::setEnvironmentChanges(m_kit, changes); + } + + QList<Utils::EnvironmentItem> currentEnvironment() const + { + QList<Utils::EnvironmentItem> changes = EnvironmentKitAspect::environmentChanges(m_kit); + + if (Utils::HostOsInfo::isWindowsHost()) { + const Utils::EnvironmentItem forceMSVCEnglishItem("VSLANG", "1033"); + if (changes.indexOf(forceMSVCEnglishItem) >= 0) { + m_vslangCheckbox->setCheckState(Qt::Checked); + changes.removeAll(forceMSVCEnglishItem); + } + } + + Utils::sort(changes, [](const Utils::EnvironmentItem &lhs, const Utils::EnvironmentItem &rhs) + { return QString::localeAwareCompare(lhs.name, rhs.name) < 0; }); + return changes; + } + + void initMSVCOutputSwitch(QVBoxLayout *layout) + { + m_vslangCheckbox = new QCheckBox(tr("Force UTF-8 MSVC compiler output")); + layout->addWidget(m_vslangCheckbox); + m_vslangCheckbox->setToolTip(tr("Either switches MSVC to English or keeps the language and " + "just forces UTF-8 output (may vary depending on the used MSVC " + "compiler).")); + connect(m_vslangCheckbox, &QCheckBox::toggled, this, [this](bool checked) { + QList<Utils::EnvironmentItem> changes + = EnvironmentKitAspect::environmentChanges(m_kit); + const Utils::EnvironmentItem forceMSVCEnglishItem("VSLANG", "1033"); + if (!checked && changes.indexOf(forceMSVCEnglishItem) >= 0) + changes.removeAll(forceMSVCEnglishItem); + if (checked && changes.indexOf(forceMSVCEnglishItem) < 0) + changes.append(forceMSVCEnglishItem); + EnvironmentKitAspect::setEnvironmentChanges(m_kit, changes); + }); + } + + QLabel *m_summaryLabel; + QPushButton *m_manageButton; + QCheckBox *m_vslangCheckbox; + QWidget *m_mainWidget; +}; +} // namespace Internal + +EnvironmentKitAspect::EnvironmentKitAspect() { - Q_UNUSED(k) - return QStringList(); + setObjectName(QLatin1String("EnvironmentKitAspect")); + setId(EnvironmentKitAspect::id()); + setDisplayName(tr("Environment")); + setDescription(tr("Additional build environment settings when using this kit.")); + setPriority(29000); } -QList<Task> EnvironmentKitInformation::validate(const Kit *k) const +Tasks EnvironmentKitAspect::validate(const Kit *k) const { - QList<Task> result; + Tasks result; QTC_ASSERT(k, return result); - const QVariant variant = k->value(EnvironmentKitInformation::id()); + const QVariant variant = k->value(EnvironmentKitAspect::id()); if (!variant.isNull() && !variant.canConvert(QVariant::List)) { result.append(Task(Task::Error, tr("The environment setting value is invalid."), - Utils::FileName(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))); + Utils::FilePath(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))); } return result; } -void EnvironmentKitInformation::fix(Kit *k) +void EnvironmentKitAspect::fix(Kit *k) { QTC_ASSERT(k, return); - const QVariant variant = k->value(EnvironmentKitInformation::id()); + const QVariant variant = k->value(EnvironmentKitAspect::id()); if (!variant.isNull() && !variant.canConvert(QVariant::List)) { qWarning("Kit \"%s\" has a wrong environment value set.", qPrintable(k->displayName())); setEnvironmentChanges(k, QList<Utils::EnvironmentItem>()); } } -void EnvironmentKitInformation::addToEnvironment(const Kit *k, Utils::Environment &env) const +void EnvironmentKitAspect::addToEnvironment(const Kit *k, Utils::Environment &env) const { const QStringList values = Utils::transform(Utils::EnvironmentItem::toStringList(environmentChanges(k)), @@ -868,34 +1266,34 @@ void EnvironmentKitInformation::addToEnvironment(const Kit *k, Utils::Environmen env.modify(Utils::EnvironmentItem::fromStringList(values)); } -KitConfigWidget *EnvironmentKitInformation::createConfigWidget(Kit *k) const +KitAspectWidget *EnvironmentKitAspect::createConfigWidget(Kit *k) const { QTC_ASSERT(k, return nullptr); - return new Internal::KitEnvironmentConfigWidget(k, this); + return new Internal::EnvironmentKitAspectWidget(k, this); } -KitInformation::ItemList EnvironmentKitInformation::toUserOutput(const Kit *k) const +KitAspect::ItemList EnvironmentKitAspect::toUserOutput(const Kit *k) const { return { qMakePair(tr("Environment"), Utils::EnvironmentItem::toStringList(environmentChanges(k)).join("<br>")) }; } -Core::Id EnvironmentKitInformation::id() +Core::Id EnvironmentKitAspect::id() { return "PE.Profile.Environment"; } -QList<Utils::EnvironmentItem> EnvironmentKitInformation::environmentChanges(const Kit *k) +QList<Utils::EnvironmentItem> EnvironmentKitAspect::environmentChanges(const Kit *k) { if (k) - return Utils::EnvironmentItem::fromStringList(k->value(EnvironmentKitInformation::id()).toStringList()); + return Utils::EnvironmentItem::fromStringList(k->value(EnvironmentKitAspect::id()).toStringList()); return QList<Utils::EnvironmentItem>(); } -void EnvironmentKitInformation::setEnvironmentChanges(Kit *k, const QList<Utils::EnvironmentItem> &changes) +void EnvironmentKitAspect::setEnvironmentChanges(Kit *k, const QList<Utils::EnvironmentItem> &changes) { if (k) - k->setValue(EnvironmentKitInformation::id(), Utils::EnvironmentItem::toStringList(changes)); + k->setValue(EnvironmentKitAspect::id(), Utils::EnvironmentItem::toStringList(changes)); } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitinformation.h b/src/plugins/projectexplorer/kitinformation.h index c32dbefa27..037b4b9394 100644 --- a/src/plugins/projectexplorer/kitinformation.h +++ b/src/plugins/projectexplorer/kitinformation.h @@ -37,52 +37,46 @@ namespace ProjectExplorer { -class KitConfigWidget; +class KitAspectWidget; // -------------------------------------------------------------------------- // SysRootInformation: // -------------------------------------------------------------------------- -class PROJECTEXPLORER_EXPORT SysRootKitInformation : public KitInformation +class PROJECTEXPLORER_EXPORT SysRootKitAspect : public KitAspect { Q_OBJECT public: - SysRootKitInformation(); - - QVariant defaultValue(const Kit *k) const override; - - QList<Task> validate(const Kit *k) const override; - - KitConfigWidget *createConfigWidget(Kit *k) const override; + SysRootKitAspect(); + Tasks validate(const Kit *k) const override; + KitAspectWidget *createConfigWidget(Kit *k) const override; ItemList toUserOutput(const Kit *k) const override; void addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const override; static Core::Id id(); - static Utils::FileName sysRoot(const Kit *k); - static void setSysRoot(Kit *k, const Utils::FileName &v); + static Utils::FilePath sysRoot(const Kit *k); + static void setSysRoot(Kit *k, const Utils::FilePath &v); }; // -------------------------------------------------------------------------- // ToolChainInformation: // -------------------------------------------------------------------------- -class PROJECTEXPLORER_EXPORT ToolChainKitInformation : public KitInformation +class PROJECTEXPLORER_EXPORT ToolChainKitAspect : public KitAspect { Q_OBJECT public: - ToolChainKitInformation(); + ToolChainKitAspect(); - QVariant defaultValue(const Kit *k) const override; - - QList<Task> validate(const Kit *k) const override; + Tasks validate(const Kit *k) const override; void upgrade(Kit *k) override; void fix(Kit *k) override; void setup(Kit *k) override; - KitConfigWidget *createConfigWidget(Kit *k) const override; + KitAspectWidget *createConfigWidget(Kit *k) const override; QString displayNamePostfix(const Kit *k) const override; @@ -114,27 +108,22 @@ private: // DeviceTypeInformation: // -------------------------------------------------------------------------- -class PROJECTEXPLORER_EXPORT DeviceTypeKitInformation : public KitInformation +class PROJECTEXPLORER_EXPORT DeviceTypeKitAspect : public KitAspect { Q_OBJECT public: - DeviceTypeKitInformation(); - - QVariant defaultValue(const Kit *k) const override; - - QList<Task> validate(const Kit *k) const override; - - KitConfigWidget *createConfigWidget(Kit *k) const override; + DeviceTypeKitAspect(); + void setup(Kit *k) override; + Tasks validate(const Kit *k) const override; + KitAspectWidget *createConfigWidget(Kit *k) const override; ItemList toUserOutput(const Kit *k) const override; static const Core::Id id(); static const Core::Id deviceTypeId(const Kit *k); static void setDeviceTypeId(Kit *k, Core::Id type); - static Kit::Predicate deviceTypePredicate(Core::Id type); - QSet<Core::Id> supportedPlatforms(const Kit *k) const override; QSet<Core::Id> availableFeatures(const Kit *k) const override; }; @@ -143,20 +132,18 @@ public: // DeviceInformation: // -------------------------------------------------------------------------- -class PROJECTEXPLORER_EXPORT DeviceKitInformation : public KitInformation +class PROJECTEXPLORER_EXPORT DeviceKitAspect : public KitAspect { Q_OBJECT public: - DeviceKitInformation(); + DeviceKitAspect(); - QVariant defaultValue(const Kit *k) const override; - - QList<Task> validate(const Kit *k) const override; + Tasks validate(const Kit *k) const override; void fix(Kit *k) override; void setup(Kit *k) override; - KitConfigWidget *createConfigWidget(Kit *k) const override; + KitAspectWidget *createConfigWidget(Kit *k) const override; QString displayNamePostfix(const Kit *k) const override; @@ -171,6 +158,8 @@ public: static void setDeviceId(Kit *k, Core::Id dataId); private: + QVariant defaultValue(const Kit *k) const; + void kitsWereLoaded(); void deviceUpdated(Core::Id dataId); void devicesChanged(); @@ -178,23 +167,21 @@ private: }; // -------------------------------------------------------------------------- -// EnvironmentKitInformation: +// EnvironmentKitAspect: // -------------------------------------------------------------------------- -class PROJECTEXPLORER_EXPORT EnvironmentKitInformation : public KitInformation +class PROJECTEXPLORER_EXPORT EnvironmentKitAspect : public KitAspect { Q_OBJECT public: - EnvironmentKitInformation(); - - QVariant defaultValue(const Kit *k) const override; + EnvironmentKitAspect(); - QList<Task> validate(const Kit *k) const override; + Tasks validate(const Kit *k) const override; void fix(Kit *k) override; void addToEnvironment(const Kit *k, Utils::Environment &env) const override; - KitConfigWidget *createConfigWidget(Kit *k) const override; + KitAspectWidget *createConfigWidget(Kit *k) const override; ItemList toUserOutput(const Kit *k) const override; diff --git a/src/plugins/projectexplorer/kitinformationconfigwidget.cpp b/src/plugins/projectexplorer/kitinformationconfigwidget.cpp deleted file mode 100644 index a366d84646..0000000000 --- a/src/plugins/projectexplorer/kitinformationconfigwidget.cpp +++ /dev/null @@ -1,529 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "kitinformationconfigwidget.h" - -#include "devicesupport/devicemanager.h" -#include "devicesupport/devicemanagermodel.h" -#include "devicesupport/idevicefactory.h" -#include "projectexplorerconstants.h" -#include "kit.h" -#include "kitinformation.h" -#include "toolchain.h" -#include "toolchainmanager.h" -#include "environmentwidget.h" - -#include <coreplugin/icore.h> -#include <coreplugin/variablechooser.h> - -#include <utils/algorithm.h> -#include <utils/fancylineedit.h> -#include <utils/environment.h> -#include <utils/qtcassert.h> -#include <utils/pathchooser.h> -#include <utils/environmentdialog.h> - -#include <QCheckBox> -#include <QComboBox> -#include <QDialog> -#include <QDialogButtonBox> -#include <QFontMetrics> -#include <QLabel> -#include <QPlainTextEdit> -#include <QPushButton> -#include <QVBoxLayout> - -using namespace Core; - -namespace ProjectExplorer { -namespace Internal { - -// -------------------------------------------------------------------------- -// SysRootInformationConfigWidget: -// -------------------------------------------------------------------------- - -SysRootInformationConfigWidget::SysRootInformationConfigWidget(Kit *k, const KitInformation *ki) : - KitConfigWidget(k, ki) -{ - m_chooser = new Utils::PathChooser; - m_chooser->setExpectedKind(Utils::PathChooser::ExistingDirectory); - m_chooser->setHistoryCompleter(QLatin1String("PE.SysRoot.History")); - m_chooser->setFileName(SysRootKitInformation::sysRoot(k)); - connect(m_chooser, &Utils::PathChooser::pathChanged, - this, &SysRootInformationConfigWidget::pathWasChanged); -} - -SysRootInformationConfigWidget::~SysRootInformationConfigWidget() -{ - delete m_chooser; -} - -QString SysRootInformationConfigWidget::displayName() const -{ - return tr("Sysroot"); -} - -QString SysRootInformationConfigWidget::toolTip() const -{ - return tr("The root directory of the system image to use.<br>" - "Leave empty when building for the desktop."); -} - -void SysRootInformationConfigWidget::setPalette(const QPalette &p) -{ - KitConfigWidget::setPalette(p); - m_chooser->setOkColor(p.color(QPalette::Active, QPalette::Text)); -} - -void SysRootInformationConfigWidget::refresh() -{ - if (!m_ignoreChange) - m_chooser->setFileName(SysRootKitInformation::sysRoot(m_kit)); -} - -void SysRootInformationConfigWidget::makeReadOnly() -{ - m_chooser->setReadOnly(true); -} - -QWidget *SysRootInformationConfigWidget::mainWidget() const -{ - return m_chooser->lineEdit(); -} - -QWidget *SysRootInformationConfigWidget::buttonWidget() const -{ - return m_chooser->buttonAtIndex(0); -} - -void SysRootInformationConfigWidget::pathWasChanged() -{ - m_ignoreChange = true; - SysRootKitInformation::setSysRoot(m_kit, m_chooser->fileName()); - m_ignoreChange = false; -} - -// -------------------------------------------------------------------------- -// ToolChainInformationConfigWidget: -// -------------------------------------------------------------------------- - -ToolChainInformationConfigWidget::ToolChainInformationConfigWidget(Kit *k, const KitInformation *ki) : - KitConfigWidget(k, ki) -{ - m_mainWidget = new QWidget; - m_mainWidget->setContentsMargins(0, 0, 0, 0); - - auto layout = new QGridLayout(m_mainWidget); - layout->setContentsMargins(0, 0, 0, 0); - layout->setColumnStretch(1, 2); - - int row = 0; - QList<Core::Id> languageList = ToolChainManager::allLanguages().toList(); - Utils::sort(languageList, [](Core::Id l1, Core::Id l2) { - return ToolChainManager::displayNameOfLanguageId(l1) < ToolChainManager::displayNameOfLanguageId(l2); - }); - - QTC_ASSERT(!languageList.isEmpty(), return); - - foreach (Core::Id l, languageList) { - layout->addWidget(new QLabel(ToolChainManager::displayNameOfLanguageId(l) + ':'), row, 0); - auto cb = new QComboBox; - cb->setSizePolicy(QSizePolicy::Ignored, cb->sizePolicy().verticalPolicy()); - cb->setToolTip(toolTip()); - - m_languageComboboxMap.insert(l, cb); - layout->addWidget(cb, row, 1); - ++row; - - connect(cb, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), - this, [this, l](int idx) { currentToolChainChanged(l, idx); }); - } - - refresh(); - - m_manageButton = new QPushButton(KitConfigWidget::msgManage()); - m_manageButton->setContentsMargins(0, 0, 0, 0); - connect(m_manageButton, &QAbstractButton::clicked, - this, &ToolChainInformationConfigWidget::manageToolChains); -} - -ToolChainInformationConfigWidget::~ToolChainInformationConfigWidget() -{ - delete m_mainWidget; - delete m_manageButton; -} - -QString ToolChainInformationConfigWidget::displayName() const -{ - return tr("Compiler"); -} - -QString ToolChainInformationConfigWidget::toolTip() const -{ - return tr("The compiler to use for building.<br>" - "Make sure the compiler will produce binaries compatible with the target device, " - "Qt version and other libraries used."); -} - -void ToolChainInformationConfigWidget::refresh() -{ - m_ignoreChanges = true; - - foreach (Core::Id l, m_languageComboboxMap.keys()) { - const QList<ToolChain *> ltcList - = ToolChainManager::toolChains(Utils::equal(&ToolChain::language, l)); - - QComboBox *cb = m_languageComboboxMap.value(l); - cb->clear(); - cb->addItem(tr("<No compiler>"), QByteArray()); - - foreach (ToolChain *tc, ltcList) - cb->addItem(tc->displayName(), tc->id()); - - cb->setEnabled(cb->count() > 1 && !m_isReadOnly); - const int index = indexOf(cb, ToolChainKitInformation::toolChain(m_kit, l)); - cb->setCurrentIndex(index); - } - m_ignoreChanges = false; -} - -void ToolChainInformationConfigWidget::makeReadOnly() -{ - m_isReadOnly = true; - foreach (Core::Id l, m_languageComboboxMap.keys()) { - m_languageComboboxMap.value(l)->setEnabled(false); - } -} - -QWidget *ToolChainInformationConfigWidget::mainWidget() const -{ - return m_mainWidget; -} - -QWidget *ToolChainInformationConfigWidget::buttonWidget() const -{ - return m_manageButton; -} - -void ToolChainInformationConfigWidget::manageToolChains() -{ - ICore::showOptionsDialog(Constants::TOOLCHAIN_SETTINGS_PAGE_ID, buttonWidget()); -} - -void ToolChainInformationConfigWidget::currentToolChainChanged(Id language, int idx) -{ - if (m_ignoreChanges || idx < 0) - return; - - const QByteArray id = m_languageComboboxMap.value(language)->itemData(idx).toByteArray(); - ToolChain *tc = ToolChainManager::findToolChain(id); - QTC_ASSERT(!tc || tc->language() == language, return); - if (tc) - ToolChainKitInformation::setToolChain(m_kit, tc); - else - ToolChainKitInformation::clearToolChain(m_kit, language); -} - -int ToolChainInformationConfigWidget::indexOf(QComboBox *cb, const ToolChain *tc) -{ - const QByteArray id = tc ? tc->id() : QByteArray(); - for (int i = 0; i < cb->count(); ++i) { - if (id == cb->itemData(i).toByteArray()) - return i; - } - return -1; -} - -// -------------------------------------------------------------------------- -// DeviceTypeInformationConfigWidget: -// -------------------------------------------------------------------------- - -DeviceTypeInformationConfigWidget::DeviceTypeInformationConfigWidget(Kit *workingCopy, const KitInformation *ki) : - KitConfigWidget(workingCopy, ki), m_comboBox(new QComboBox) -{ - for (IDeviceFactory *factory : IDeviceFactory::allDeviceFactories()) - m_comboBox->addItem(factory->displayName(), factory->deviceType().toSetting()); - - m_comboBox->setToolTip(toolTip()); - - refresh(); - connect(m_comboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), - this, &DeviceTypeInformationConfigWidget::currentTypeChanged); -} - -DeviceTypeInformationConfigWidget::~DeviceTypeInformationConfigWidget() -{ - delete m_comboBox; -} - -QWidget *DeviceTypeInformationConfigWidget::mainWidget() const -{ - return m_comboBox; -} - -QString DeviceTypeInformationConfigWidget::displayName() const -{ - return tr("Device type"); -} - -QString DeviceTypeInformationConfigWidget::toolTip() const -{ - return tr("The type of device to run applications on."); -} - -void DeviceTypeInformationConfigWidget::refresh() -{ - Id devType = DeviceTypeKitInformation::deviceTypeId(m_kit); - if (!devType.isValid()) - m_comboBox->setCurrentIndex(-1); - for (int i = 0; i < m_comboBox->count(); ++i) { - if (m_comboBox->itemData(i) == devType.toSetting()) { - m_comboBox->setCurrentIndex(i); - break; - } - } -} - -void DeviceTypeInformationConfigWidget::makeReadOnly() -{ - m_comboBox->setEnabled(false); -} - -void DeviceTypeInformationConfigWidget::currentTypeChanged(int idx) -{ - Id type = idx < 0 ? Id() : Id::fromSetting(m_comboBox->itemData(idx)); - DeviceTypeKitInformation::setDeviceTypeId(m_kit, type); -} - -// -------------------------------------------------------------------------- -// DeviceInformationConfigWidget: -// -------------------------------------------------------------------------- - -DeviceInformationConfigWidget::DeviceInformationConfigWidget(Kit *workingCopy, const KitInformation *ki) : - KitConfigWidget(workingCopy, ki), - m_comboBox(new QComboBox), - m_model(new DeviceManagerModel(DeviceManager::instance())) -{ - m_comboBox->setSizePolicy(QSizePolicy::Ignored, m_comboBox->sizePolicy().verticalPolicy()); - m_comboBox->setModel(m_model); - - m_manageButton = new QPushButton(KitConfigWidget::msgManage()); - - refresh(); - m_comboBox->setToolTip(toolTip()); - - connect(m_model, &QAbstractItemModel::modelAboutToBeReset, - this, &DeviceInformationConfigWidget::modelAboutToReset); - connect(m_model, &QAbstractItemModel::modelReset, - this, &DeviceInformationConfigWidget::modelReset); - connect(m_comboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), - this, &DeviceInformationConfigWidget::currentDeviceChanged); - connect(m_manageButton, &QAbstractButton::clicked, - this, &DeviceInformationConfigWidget::manageDevices); -} - -DeviceInformationConfigWidget::~DeviceInformationConfigWidget() -{ - delete m_comboBox; - delete m_model; - delete m_manageButton; -} - -QWidget *DeviceInformationConfigWidget::mainWidget() const -{ - return m_comboBox; -} - -QString DeviceInformationConfigWidget::displayName() const -{ - return tr("Device"); -} - -QString DeviceInformationConfigWidget::toolTip() const -{ - return tr("The device to run the applications on."); -} - -void DeviceInformationConfigWidget::refresh() -{ - m_model->setTypeFilter(DeviceTypeKitInformation::deviceTypeId(m_kit)); - m_comboBox->setCurrentIndex(m_model->indexOf(DeviceKitInformation::device(m_kit))); -} - -void DeviceInformationConfigWidget::makeReadOnly() -{ - m_comboBox->setEnabled(false); -} - -QWidget *DeviceInformationConfigWidget::buttonWidget() const -{ - return m_manageButton; -} - -void DeviceInformationConfigWidget::manageDevices() -{ - ICore::showOptionsDialog(Constants::DEVICE_SETTINGS_PAGE_ID, buttonWidget()); -} - -void DeviceInformationConfigWidget::modelAboutToReset() -{ - m_selectedId = m_model->deviceId(m_comboBox->currentIndex()); - m_ignoreChange = true; -} - -void DeviceInformationConfigWidget::modelReset() -{ - m_comboBox->setCurrentIndex(m_model->indexForId(m_selectedId)); - m_ignoreChange = false; -} - -void DeviceInformationConfigWidget::currentDeviceChanged() -{ - if (m_ignoreChange) - return; - DeviceKitInformation::setDeviceId(m_kit, m_model->deviceId(m_comboBox->currentIndex())); -} - -// -------------------------------------------------------------------- -// KitEnvironmentConfigWidget: -// -------------------------------------------------------------------- - -KitEnvironmentConfigWidget::KitEnvironmentConfigWidget(Kit *workingCopy, const KitInformation *ki) : - KitConfigWidget(workingCopy, ki), - m_summaryLabel(new QLabel), - m_manageButton(new QPushButton), - m_mainWidget(new QWidget) -{ - auto *layout = new QVBoxLayout; - layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(m_summaryLabel); - if (Utils::HostOsInfo::isWindowsHost()) - initMSVCOutputSwitch(layout); - - m_mainWidget->setLayout(layout); - - refresh(); - m_manageButton->setText(tr("Change...")); - connect(m_manageButton, &QAbstractButton::clicked, - this, &KitEnvironmentConfigWidget::editEnvironmentChanges); -} - -QWidget *KitEnvironmentConfigWidget::mainWidget() const -{ - return m_mainWidget; -} - -QString KitEnvironmentConfigWidget::displayName() const -{ - return tr("Environment"); -} - -QString KitEnvironmentConfigWidget::toolTip() const -{ - return tr("Additional build environment settings when using this kit."); -} - -void KitEnvironmentConfigWidget::refresh() -{ - const QList<Utils::EnvironmentItem> changes = currentEnvironment(); - QString shortSummary = Utils::EnvironmentItem::toStringList(changes).join(QLatin1String("; ")); - QFontMetrics fm(m_summaryLabel->font()); - shortSummary = fm.elidedText(shortSummary, Qt::ElideRight, m_summaryLabel->width()); - m_summaryLabel->setText(shortSummary.isEmpty() ? tr("No changes to apply.") : shortSummary); -} - -void KitEnvironmentConfigWidget::makeReadOnly() -{ - m_manageButton->setEnabled(false); -} - -QList<Utils::EnvironmentItem> KitEnvironmentConfigWidget::currentEnvironment() const -{ - QList<Utils::EnvironmentItem> changes = EnvironmentKitInformation::environmentChanges(m_kit); - - if (Utils::HostOsInfo::isWindowsHost()) { - const Utils::EnvironmentItem forceMSVCEnglishItem("VSLANG", "1033"); - if (changes.indexOf(forceMSVCEnglishItem) >= 0) { - m_vslangCheckbox->setCheckState(Qt::Checked); - changes.removeAll(forceMSVCEnglishItem); - } - } - - Utils::sort(changes, [](const Utils::EnvironmentItem &lhs, const Utils::EnvironmentItem &rhs) - { return QString::localeAwareCompare(lhs.name, rhs.name) < 0; }); - return changes; -} - -void KitEnvironmentConfigWidget::editEnvironmentChanges() -{ - bool ok; - Utils::MacroExpander *expander = m_kit->macroExpander(); - Utils::EnvironmentDialog::Polisher polisher = [expander](QWidget *w) { - Core::VariableChooser::addSupportForChildWidgets(w, expander); - }; - QList<Utils::EnvironmentItem> - changes = Utils::EnvironmentDialog::getEnvironmentItems(&ok, - m_summaryLabel, - currentEnvironment(), - QString(), - polisher); - if (!ok) - return; - - if (Utils::HostOsInfo::isWindowsHost()) { - const Utils::EnvironmentItem forceMSVCEnglishItem("VSLANG", "1033"); - if (m_vslangCheckbox->isChecked() && changes.indexOf(forceMSVCEnglishItem) < 0) - changes.append(forceMSVCEnglishItem); - } - - EnvironmentKitInformation::setEnvironmentChanges(m_kit, changes); -} - -QWidget *KitEnvironmentConfigWidget::buttonWidget() const -{ - return m_manageButton; -} - -void KitEnvironmentConfigWidget::initMSVCOutputSwitch(QVBoxLayout *layout) -{ - m_vslangCheckbox = new QCheckBox(tr("Force UTF-8 MSVC compiler output")); - layout->addWidget(m_vslangCheckbox); - m_vslangCheckbox->setToolTip(tr("Either switches MSVC to English or keeps the language and " - "just forces UTF-8 output (may vary depending on the used MSVC " - "compiler).")); - connect(m_vslangCheckbox, &QCheckBox::toggled, this, [this](bool checked) { - QList<Utils::EnvironmentItem> changes - = EnvironmentKitInformation::environmentChanges(m_kit); - const Utils::EnvironmentItem forceMSVCEnglishItem("VSLANG", "1033"); - if (!checked && changes.indexOf(forceMSVCEnglishItem) >= 0) - changes.removeAll(forceMSVCEnglishItem); - if (checked && changes.indexOf(forceMSVCEnglishItem) < 0) - changes.append(forceMSVCEnglishItem); - EnvironmentKitInformation::setEnvironmentChanges(m_kit, changes); - }); -} - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitinformationconfigwidget.h b/src/plugins/projectexplorer/kitinformationconfigwidget.h deleted file mode 100644 index 5fb6016d8c..0000000000 --- a/src/plugins/projectexplorer/kitinformationconfigwidget.h +++ /dev/null @@ -1,196 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "kitconfigwidget.h" -#include "toolchain.h" - -#include <coreplugin/id.h> - -#include <utils/environment.h> - -QT_BEGIN_NAMESPACE -class QCheckBox; -class QComboBox; -class QLabel; -class QPlainTextEdit; -class QPushButton; -class QVBoxLayout; -QT_END_NAMESPACE - -namespace Utils { class PathChooser; } - -namespace ProjectExplorer { - -class DeviceManagerModel; - -namespace Internal { - -// -------------------------------------------------------------------------- -// SysRootInformationConfigWidget: -// -------------------------------------------------------------------------- - -class SysRootInformationConfigWidget : public KitConfigWidget -{ - Q_OBJECT - -public: - SysRootInformationConfigWidget(Kit *k, const KitInformation *ki); - ~SysRootInformationConfigWidget() override; - - QString displayName() const override; - void refresh() override; - void makeReadOnly() override; - QWidget *buttonWidget() const override; - QWidget *mainWidget() const override; - QString toolTip() const override; - - void setPalette(const QPalette &p) override; - -private: - void pathWasChanged(); - - Utils::PathChooser *m_chooser; - bool m_ignoreChange = false; -}; - -// -------------------------------------------------------------------------- -// ToolChainInformationConfigWidget: -// -------------------------------------------------------------------------- - -class ToolChainInformationConfigWidget : public KitConfigWidget -{ - Q_OBJECT - -public: - ToolChainInformationConfigWidget(Kit *k, const KitInformation *ki); - ~ToolChainInformationConfigWidget() override; - - QString displayName() const override; - void refresh() override; - void makeReadOnly() override; - QWidget *mainWidget() const override; - QWidget *buttonWidget() const override; - QString toolTip() const override; - -private: - void manageToolChains(); - void currentToolChainChanged(Core::Id language, int idx); - - int indexOf(QComboBox *cb, const ToolChain *tc); - - QWidget *m_mainWidget = nullptr; - QPushButton *m_manageButton = nullptr; - QHash<Core::Id, QComboBox *> m_languageComboboxMap; - bool m_ignoreChanges = false; - bool m_isReadOnly = false; -}; - -// -------------------------------------------------------------------------- -// DeviceTypeInformationConfigWidget: -// -------------------------------------------------------------------------- - -class DeviceTypeInformationConfigWidget : public KitConfigWidget -{ - Q_OBJECT - -public: - DeviceTypeInformationConfigWidget(Kit *workingCopy, const KitInformation *ki); - ~DeviceTypeInformationConfigWidget() override; - - QWidget *mainWidget() const override; - QString displayName() const override; - QString toolTip() const override; - void refresh() override; - void makeReadOnly() override; - -private: - void currentTypeChanged(int idx); - - QComboBox *m_comboBox; -}; - -// -------------------------------------------------------------------------- -// DeviceInformationConfigWidget: -// -------------------------------------------------------------------------- - -class DeviceInformationConfigWidget : public KitConfigWidget -{ - Q_OBJECT - -public: - DeviceInformationConfigWidget(Kit *workingCopy, const KitInformation *ki); - ~DeviceInformationConfigWidget() override; - - QWidget *mainWidget() const override; - QWidget *buttonWidget() const override; - QString displayName() const override; - QString toolTip() const override; - void refresh() override; - void makeReadOnly() override; - -private: - void manageDevices(); - void modelAboutToReset(); - void modelReset(); - void currentDeviceChanged(); - - bool m_isReadOnly = false; - bool m_ignoreChange = false; - QComboBox *m_comboBox; - QPushButton *m_manageButton; - DeviceManagerModel *m_model; - Core::Id m_selectedId; -}; - -class KitEnvironmentConfigWidget : public KitConfigWidget -{ - Q_OBJECT - -public: - KitEnvironmentConfigWidget(Kit *workingCopy, const KitInformation *ki); - - QWidget *mainWidget() const override; - QWidget *buttonWidget() const override; - QString displayName() const override; - QString toolTip() const override; - void refresh() override; - void makeReadOnly() override; - -private: - void editEnvironmentChanges(); - QList<Utils::EnvironmentItem> currentEnvironment() const; - - void initMSVCOutputSwitch(QVBoxLayout *layout); - - QLabel *m_summaryLabel; - QPushButton *m_manageButton; - QCheckBox *m_vslangCheckbox; - QWidget *m_mainWidget; -}; - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp index 0b6e5a8b16..91d04b6f5d 100644 --- a/src/plugins/projectexplorer/kitmanager.cpp +++ b/src/plugins/projectexplorer/kitmanager.cpp @@ -25,12 +25,16 @@ #include "kitmanager.h" +#include "abi.h" #include "devicesupport/idevicefactory.h" #include "kit.h" #include "kitfeatureprovider.h" +#include "kitinformation.h" #include "kitmanagerconfigwidget.h" #include "project.h" +#include "projectexplorerconstants.h" #include "task.h" +#include "toolchainmanager.h" #include <coreplugin/icore.h> @@ -40,24 +44,37 @@ #include <utils/qtcassert.h> #include <utils/stringutils.h> +#include <QHash> #include <QSettings> +#include <QStyle> using namespace Core; using namespace Utils; using namespace ProjectExplorer::Internal; namespace ProjectExplorer { + +class KitList +{ +public: + Core::Id defaultKit; + std::vector<std::unique_ptr<Kit>> kits; +}; + +static KitList restoreKitsHelper(const Utils::FilePath &fileName); + namespace Internal { const char KIT_DATA_KEY[] = "Profile."; const char KIT_COUNT_KEY[] = "Profile.Count"; const char KIT_FILE_VERSION_KEY[] = "Version"; const char KIT_DEFAULT_KEY[] = "Profile.Default"; +const char KIT_IRRELEVANT_ASPECTS_KEY[] = "Kit.IrrelevantAspects"; const char KIT_FILENAME[] = "/profiles.xml"; -static FileName settingsFileName() +static FilePath settingsFileName() { - return FileName::fromString(ICore::userResourcePath() + KIT_FILENAME); + return FilePath::fromString(ICore::userResourcePath() + KIT_FILENAME); } // -------------------------------------------------------------------------- @@ -71,9 +88,37 @@ public: bool m_initialized = false; std::vector<std::unique_ptr<Kit>> m_kitList; std::unique_ptr<PersistentSettingsWriter> m_writer; + QSet<Id> m_irrelevantAspects; + + void addKitAspect(KitAspect *ki) + { + QTC_ASSERT(!m_aspectList.contains(ki), return); + m_aspectList.append(ki); + m_aspectListIsSorted = false; + } - // Sorted by priority, in descending order. - std::vector<std::unique_ptr<KitInformation>> m_informationList; + void removeKitAspect(KitAspect *ki) + { + int removed = m_aspectList.removeAll(ki); + QTC_CHECK(removed == 1); + } + + const QList<KitAspect *> kitAspects() + { + if (!m_aspectListIsSorted) { + Utils::sort(m_aspectList, [](const KitAspect *a, const KitAspect *b) { + return a->priority() > b->priority(); + }); + m_aspectListIsSorted = true; + } + return m_aspectList; + } + +private: + // Sorted by priority, in descending order... + QList<KitAspect *> m_aspectList; + // ... if this here is set: + bool m_aspectListIsSorted = true; }; } // namespace Internal @@ -87,11 +132,12 @@ static KitManager *m_instance = nullptr; KitManager *KitManager::instance() { + if (!m_instance) + m_instance = new KitManager; return m_instance; } -KitManager::KitManager(QObject *parent) - : QObject(parent) +KitManager::KitManager() { d = new KitManagerPrivate; QTC_CHECK(!m_instance); @@ -104,6 +150,14 @@ KitManager::KitManager(QObject *parent) connect(this, &KitManager::kitUpdated, this, &KitManager::kitsChanged); } +void KitManager::destroy() +{ + delete d; + d = nullptr; + delete m_instance; + m_instance = nullptr; +} + void KitManager::restoreKits() { QTC_ASSERT(!d->m_initialized, return ); @@ -114,7 +168,7 @@ void KitManager::restoreKits() Core::Id defaultUserKit; std::vector<std::unique_ptr<Kit>> kitsToCheck; { - KitList userKits = restoreKits(settingsFileName()); + KitList userKits = restoreKitsHelper(settingsFileName()); defaultUserKit = userKits.defaultKit; for (auto &k : userKits.kits) { @@ -129,8 +183,8 @@ void KitManager::restoreKits() // read all kits from SDK { - KitList system - = restoreKits(FileName::fromString(ICore::installerResourcePath() + KIT_FILENAME)); + KitList system = restoreKitsHelper + (FilePath::fromString(ICore::installerResourcePath() + KIT_FILENAME)); // SDK kits need to get updated with the user-provided extra settings: for (auto ¤t : system.kits) { @@ -153,11 +207,11 @@ void KitManager::restoreKits() Kit *ptr = i->get(); // Overwrite settings that the SDK sets to those values: - foreach (const KitInformation *ki, KitManager::kitInformation()) { + for (const KitAspect *aspect : KitManager::kitAspects()) { // Copy sticky settings over: - if (ptr->isSticky(ki->id())) { - ptr->setValue(ki->id(), toStore->value(ki->id())); - ptr->setSticky(ki->id(), true); + if (ptr->isSticky(aspect->id())) { + ptr->setValue(aspect->id(), toStore->value(aspect->id())); + ptr->setSticky(aspect->id(), true); } } toStore = std::move(*i); @@ -171,16 +225,69 @@ void KitManager::restoreKits() // Delete all loaded autodetected kits that were not rediscovered: kitsToCheck.clear(); - if (resultList.size() == 0) { - auto defaultKit = std::make_unique<Kit>(); // One kit using default values - defaultKit->setUnexpandedDisplayName(tr("Desktop")); - defaultKit->setSdkProvided(false); - defaultKit->setAutoDetected(false); + if (resultList.empty()) { + // No kits exist yet, so let's try to autoconfigure some from the toolchains we know. + // We consider only host toolchains, because for other ones we lack the knowledge how to + // map them to their respective device type. + static const auto isHostToolchain = [](const ToolChain *tc) { + static const Abi hostAbi = Abi::hostAbi(); + const Abi tcAbi = tc->targetAbi(); + return tcAbi.os() == hostAbi.os() && tcAbi.architecture() == hostAbi.architecture() + && (tcAbi.os() != Abi::LinuxOS || tcAbi.osFlavor() == hostAbi.osFlavor()); + }; + const QList<ToolChain *> allToolchains = ToolChainManager::toolChains(isHostToolchain); + QHash<Abi, QHash<Core::Id, ToolChain *>> uniqueToolchains; + + // On Linux systems, we usually detect a plethora of same-ish toolchains. The following + // algorithm gives precedence to icecc and ccache and otherwise simply chooses the one with + // the shortest path. This should also take care of ensuring matching C/C++ pairs. + // TODO: This should not need to be done here. Instead, it should be a convenience + // operation on some lower level, e.g. in the toolchain class(es). + // Also, we shouldn't detect so many doublets in the first place. + for (ToolChain * const tc : allToolchains) { + ToolChain *&bestTc = uniqueToolchains[tc->targetAbi()][tc->language()]; + if (!bestTc) { + bestTc = tc; + continue; + } + const QString bestFilePath = bestTc->compilerCommand().toString(); + const QString currentFilePath = tc->compilerCommand().toString(); + if (bestFilePath.contains("icecc")) + continue; + if (currentFilePath.contains("icecc")) { + bestTc = tc; + continue; + } - defaultKit->setup(); + if (bestFilePath.contains("ccache")) + continue; + if (currentFilePath.contains("ccache")) { + bestTc = tc; + continue; + } + if (bestFilePath.length() > currentFilePath.length()) + bestTc = tc; + } - completeKit(defaultKit.get()); // Store manual kits - resultList.emplace_back(std::move(defaultKit)); + int maxWeight = 0; + for (auto it = uniqueToolchains.cbegin(); it != uniqueToolchains.cend(); ++it) { + auto kit = std::make_unique<Kit>(); + kit->setSdkProvided(false); + kit->setAutoDetected(false); // TODO: Why false? What does autodetected mean here? + for (ToolChain * const tc : it.value()) + ToolChainKitAspect::setToolChain(kit.get(), tc); + kit->setUnexpandedDisplayName(tr("Desktop (%1)").arg(it.key().toString())); + kit->setup(); + if (kit->weight() < maxWeight) + continue; + if (kit->weight() > maxWeight) { + maxWeight = kit->weight(); + resultList.clear(); + } + resultList.emplace_back(std::move(kit)); + } + if (resultList.size() == 1) + resultList.front()->setUnexpandedDisplayName(tr("Desktop")); } Kit *k = Utils::findOrDefault(resultList, Utils::equal(&Kit::id, defaultUserKit)); @@ -191,19 +298,17 @@ void KitManager::restoreKits() d->m_writer = std::make_unique<PersistentSettingsWriter>(settingsFileName(), "QtCreatorProfiles"); d->m_initialized = true; - emit kitsLoaded(); - emit kitsChanged(); + emit m_instance->kitsLoaded(); + emit m_instance->kitsChanged(); } KitManager::~KitManager() { - delete d; - d = nullptr; - m_instance = nullptr; } void KitManager::saveKits() { + QTC_ASSERT(d, return); if (!d->m_writer) // ignore save requests while we are not initialized. return; @@ -221,6 +326,8 @@ void KitManager::saveKits() data.insert(QLatin1String(KIT_COUNT_KEY), count); data.insert(QLatin1String(KIT_DEFAULT_KEY), d->m_defaultKit ? QString::fromLatin1(d->m_defaultKit->id().name()) : QString()); + data.insert(KIT_IRRELEVANT_ASPECTS_KEY, + transform<QVariantList>(d->m_irrelevantAspects, &Id::toSetting)); d->m_writer->save(data, ICore::mainWindow()); } @@ -229,28 +336,27 @@ bool KitManager::isLoaded() return d->m_initialized; } -void KitManager::registerKitInformation(std::unique_ptr<KitInformation> &&ki) +void KitManager::registerKitAspect(KitAspect *ki) { - QTC_ASSERT(ki->id().isValid(), return ); - QTC_ASSERT(!Utils::contains(d->m_informationList, ki.get()), return ); - - auto it = std::lower_bound(std::begin(d->m_informationList), - std::end(d->m_informationList), - ki, - [](const std::unique_ptr<KitInformation> &a, - const std::unique_ptr<KitInformation> &b) { - return a->priority() > b->priority(); - }); - d->m_informationList.insert(it, std::move(ki)); + instance(); + QTC_ASSERT(d, return); + d->addKitAspect(ki); - foreach (Kit *k, kits()) { - if (!k->hasValue(ki->id())) - k->setValue(ki->id(), ki->defaultValue(k)); - else - ki->fix(k); - } + // Adding this aspect to possibly already existing kits is currently not + // needed here as kits are only created after all aspects are created + // in *Plugin::initialize(). + // Make sure we notice when this assumption breaks: + QTC_CHECK(d->m_kitList.empty()); +} - return; +void KitManager::deregisterKitAspect(KitAspect *ki) +{ + // Happens regularly for the aspects from the ProjectExplorerPlugin as these + // are destroyed after the manual call to KitManager::destroy() there, but as + // this here is just for sanity reasons that the KitManager does not access + // a destroyed aspect, a destroyed KitManager is not a problem. + if (d) + d->removeKitAspect(ki); } QSet<Id> KitManager::supportedPlatforms() @@ -291,7 +397,7 @@ QList<Kit *> KitManager::sortKits(const QList<Kit *> &kits) return Utils::transform(sortList, &QPair<QString, Kit *>::second); } -KitManager::KitList KitManager::restoreKits(const FileName &fileName) +static KitList restoreKitsHelper(const FilePath &fileName) { KitList result; @@ -336,6 +442,9 @@ KitManager::KitList KitManager::restoreKits(const FileName &fileName) if (Utils::contains(result.kits, [id](const std::unique_ptr<Kit> &k) { return k->id() == id; })) result.defaultKit = id; + const auto it = data.constFind(KIT_IRRELEVANT_ASPECTS_KEY); + if (it != data.constEnd()) + d->m_irrelevantAspects = transform<QSet<Id>>(it.value().toList(), &Id::fromSetting); return result; } @@ -366,20 +475,19 @@ Kit *KitManager::defaultKit() return d->m_defaultKit; } -QList<KitInformation *> KitManager::kitInformation() +const QList<KitAspect *> KitManager::kitAspects() { - return Utils::toRawPointer<QList>(d->m_informationList); + return d->kitAspects(); } -KitManagerConfigWidget *KitManager::createConfigWidget(Kit *k) +const QSet<Id> KitManager::irrelevantAspects() { - auto *result = new KitManagerConfigWidget(k); - foreach (KitInformation *ki, kitInformation()) - result->addConfigWidget(ki->createConfigWidget(result->workingCopy())); - - result->updateVisibility(); + return d->m_irrelevantAspects; +} - return result; +void KitManager::setIrrelevantAspects(const QSet<Id> &aspects) +{ + d->m_irrelevantAspects = aspects; } void KitManager::notifyAboutUpdate(Kit *k) @@ -393,18 +501,16 @@ void KitManager::notifyAboutUpdate(Kit *k) emit m_instance->unmanagedKitUpdated(k); } -bool KitManager::registerKit(std::unique_ptr<Kit> &&k) +Kit *KitManager::registerKit(const std::function<void (Kit *)> &init, Core::Id id) { - QTC_ASSERT(isLoaded(), return false); + QTC_ASSERT(isLoaded(), return nullptr); - if (!k) - return true; - - QTC_ASSERT(k->id().isValid(), return false); + auto k = std::make_unique<Kit>(id); + QTC_ASSERT(k->id().isValid(), return nullptr); Kit *kptr = k.get(); - if (Utils::contains(d->m_kitList, kptr)) - return false; + if (init) + init(kptr); // make sure we have all the information in our kits: completeKit(kptr); @@ -415,7 +521,7 @@ bool KitManager::registerKit(std::unique_ptr<Kit> &&k) setDefaultKit(kptr); emit m_instance->kitAdded(kptr); - return true; + return kptr; } void KitManager::deregisterKit(Kit *k) @@ -444,61 +550,106 @@ void KitManager::completeKit(Kit *k) { QTC_ASSERT(k, return); KitGuard g(k); - for (const std::unique_ptr<KitInformation> &ki : d->m_informationList) { + for (KitAspect *ki : d->kitAspects()) { ki->upgrade(k); if (!k->hasValue(ki->id())) - k->setValue(ki->id(), ki->defaultValue(k)); + ki->setup(k); else ki->fix(k); } } // -------------------------------------------------------------------- -// KitInformation: +// KitAspect: // -------------------------------------------------------------------- -void KitInformation::addToEnvironment(const Kit *k, Environment &env) const +KitAspect::KitAspect() +{ + KitManager::registerKitAspect(this); +} + +KitAspect::~KitAspect() +{ + KitManager::deregisterKitAspect(this); +} + +int KitAspect::weight(const Kit *k) const +{ + return k->value(id()).isValid() ? 1 : 0; +} + +void KitAspect::addToEnvironment(const Kit *k, Environment &env) const { Q_UNUSED(k); Q_UNUSED(env); } -IOutputParser *KitInformation::createOutputParser(const Kit *k) const +IOutputParser *KitAspect::createOutputParser(const Kit *k) const { Q_UNUSED(k); return nullptr; } -QString KitInformation::displayNamePostfix(const Kit *k) const +QString KitAspect::displayNamePostfix(const Kit *k) const { Q_UNUSED(k); return QString(); } -QSet<Id> KitInformation::supportedPlatforms(const Kit *k) const +QSet<Id> KitAspect::supportedPlatforms(const Kit *k) const { Q_UNUSED(k); return QSet<Id>(); } -QSet<Id> KitInformation::availableFeatures(const Kit *k) const +QSet<Id> KitAspect::availableFeatures(const Kit *k) const { Q_UNUSED(k); return QSet<Id>(); } -void KitInformation::addToMacroExpander(Kit *k, MacroExpander *expander) const +void KitAspect::addToMacroExpander(Kit *k, MacroExpander *expander) const { Q_UNUSED(k); Q_UNUSED(expander); } -void KitInformation::notifyAboutUpdate(Kit *k) +void KitAspect::notifyAboutUpdate(Kit *k) { if (k) k->kitUpdated(); } +KitAspectWidget::KitAspectWidget(Kit *kit, const KitAspect *ki) : m_kit(kit), + m_kitInformation(ki), m_isSticky(kit->isSticky(ki->id())) +{ } + +Core::Id KitAspectWidget::kitInformationId() const +{ + return m_kitInformation->id(); +} + +QString KitAspectWidget::msgManage() +{ + return tr("Manage..."); +} + +void KitAspectWidget::setPalette(const QPalette &p) +{ + if (mainWidget()) + mainWidget()->setPalette(p); + if (buttonWidget()) + buttonWidget()->setPalette(p); +} + +void KitAspectWidget::setStyle(QStyle *s) +{ + if (mainWidget()) + mainWidget()->setStyle(s); + if (buttonWidget()) + buttonWidget()->setStyle(s); +} + // -------------------------------------------------------------------- // KitFeatureProvider: // -------------------------------------------------------------------- diff --git a/src/plugins/projectexplorer/kitmanager.h b/src/plugins/projectexplorer/kitmanager.h index 189dc1c216..4cc80230c6 100644 --- a/src/plugins/projectexplorer/kitmanager.h +++ b/src/plugins/projectexplorer/kitmanager.h @@ -34,34 +34,38 @@ #include <QObject> #include <QPair> +#include <QSet> #include <functional> +QT_BEGIN_NAMESPACE +class QStyle; +QT_END_NAMESPACE + namespace Utils { class Environment; -class FileName; +class FilePath; class MacroExpander; } // namespace Utils namespace ProjectExplorer { class Task; class IOutputParser; -class KitConfigWidget; +class KitAspectWidget; class KitManager; namespace Internal { -class KitManagerConfigWidget; class KitModel; } // namespace Internal /** - * @brief The KitInformation class + * @brief The KitAspect class * * One piece of information stored in the kit. * - * This needs to get registered with the \a KitManager. + * They auto-register with the \a KitManager for their life time */ -class PROJECTEXPLORER_EXPORT KitInformation : public QObject +class PROJECTEXPLORER_EXPORT KitAspect : public QObject { Q_OBJECT @@ -71,11 +75,12 @@ public: Core::Id id() const { return m_id; } int priority() const { return m_priority; } - - virtual QVariant defaultValue(const Kit *) const = 0; + QString displayName() const { return m_displayName; } + QString description() const { return m_description; } + bool isEssential() const { return m_essential; } // called to find issues with the kit - virtual QList<Task> validate(const Kit *) const = 0; + virtual Tasks validate(const Kit *) const = 0; // called after restoring a kit, so upgrading of kit information settings can be done virtual void upgrade(Kit *) { return; } // called to fix issues with this kitinformation. Does not modify the rest of the kit. @@ -83,9 +88,11 @@ public: // called on initial setup of a kit. virtual void setup(Kit *) { return; } + virtual int weight(const Kit *k) const; + virtual ItemList toUserOutput(const Kit *) const = 0; - virtual KitConfigWidget *createConfigWidget(Kit *) const = 0; + virtual KitAspectWidget *createConfigWidget(Kit *) const = 0; virtual void addToEnvironment(const Kit *k, Utils::Environment &env) const; virtual IOutputParser *createOutputParser(const Kit *k) const; @@ -97,14 +104,59 @@ public: virtual void addToMacroExpander(ProjectExplorer::Kit *kit, Utils::MacroExpander *expander) const; + virtual bool isApplicableToKit(const Kit *) const { return true; } + protected: + KitAspect(); + ~KitAspect(); + void setId(Core::Id id) { m_id = id; } + void setDisplayName(const QString &name) { m_displayName = name; } + void setDescription(const QString &desc) { m_description = desc; } + void makeEssential() { m_essential = true; } void setPriority(int priority) { m_priority = priority; } void notifyAboutUpdate(Kit *k); private: + QString m_displayName; + QString m_description; Core::Id m_id; int m_priority = 0; // The higher the closer to the top. + bool m_essential = false; +}; + +class PROJECTEXPLORER_EXPORT KitAspectWidget : public QObject +{ + Q_OBJECT + +public: + KitAspectWidget(Kit *kit, const KitAspect *ki); + + Core::Id kitInformationId() const; + + virtual void makeReadOnly() = 0; + virtual void refresh() = 0; + bool visibleInKit() { return m_kitInformation->isApplicableToKit(m_kit); } + + virtual QWidget *mainWidget() const = 0; + virtual QWidget *buttonWidget() const { return nullptr; } + + bool isSticky() const { return m_isSticky; } + + static QString msgManage(); + + Kit *kit() const { return m_kit; } + + virtual void setPalette(const QPalette &p); + virtual void setStyle(QStyle *s); + +signals: + void dirty(); + +protected: + Kit *m_kit; + const KitAspect *m_kitInformation; + bool m_isSticky; }; class PROJECTEXPLORER_EXPORT KitManager : public QObject @@ -120,19 +172,14 @@ public: static Kit *kit(Core::Id id); static Kit *defaultKit(); - static QList<KitInformation *> kitInformation(); + static const QList<KitAspect *> kitAspects(); + static const QSet<Core::Id> irrelevantAspects(); + static void setIrrelevantAspects(const QSet<Core::Id> &aspects); - static Internal::KitManagerConfigWidget *createConfigWidget(Kit *k); - - static bool registerKit(std::unique_ptr<Kit> &&k); + static Kit *registerKit(const std::function<void(Kit *)> &init, Core::Id id = {}); static void deregisterKit(Kit *k); static void setDefaultKit(Kit *k); - template<typename KI, typename... Args> - static void registerKitInformation(Args&&... args) { - registerKitInformation(std::make_unique<KI>(std::forward<Args>(args)...)); - } - static QSet<Core::Id> supportedPlatforms(); static QSet<Core::Id> availableFeatures(Core::Id platformId); @@ -157,20 +204,16 @@ signals: void kitsLoaded(); private: - explicit KitManager(QObject *parent = nullptr); + KitManager(); + + static void destroy(); - static void registerKitInformation(std::unique_ptr<KitInformation> &&ki); + static void registerKitAspect(KitAspect *ki); + static void deregisterKitAspect(KitAspect *ki); // Make sure the this is only called after all - // KitInformation are registered! - void restoreKits(); - class KitList - { - public: - Core::Id defaultKit; - std::vector<std::unique_ptr<Kit>> kits; - }; - KitList restoreKits(const Utils::FileName &fileName); + // KitAspects are registered! + static void restoreKits(); static void notifyAboutUpdate(Kit *k); static void completeKit(Kit *k); @@ -178,7 +221,7 @@ private: friend class ProjectExplorerPlugin; // for constructor friend class Kit; friend class Internal::KitModel; - friend class KitInformation; // for notifyAbutUpdate + friend class KitAspect; // for notifyAboutUpdate and self-registration }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp b/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp index 473c8455e2..cdd508b078 100644 --- a/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp +++ b/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp @@ -24,18 +24,22 @@ ****************************************************************************/ #include "kitmanagerconfigwidget.h" -#include "projectexplorerconstants.h" +#include "devicesupport/idevicefactory.h" #include "kit.h" +#include "kitinformation.h" #include "kitmanager.h" +#include "projectexplorerconstants.h" #include "task.h" #include <coreplugin/variablechooser.h> #include <utils/algorithm.h> #include <utils/detailswidget.h> -#include <utils/qtcassert.h> #include <utils/macroexpander.h> +#include <utils/pathchooser.h> +#include <utils/qtcassert.h> +#include <utils/utilsicons.h> #include <QAction> #include <QRegularExpression> @@ -44,7 +48,9 @@ #include <QGridLayout> #include <QLabel> #include <QLineEdit> +#include <QMenu> #include <QPainter> +#include <QPushButton> #include <QToolButton> #include <QScrollArea> #include <QSizePolicy> @@ -92,11 +98,10 @@ KitManagerConfigWidget::KitManagerConfigWidget(Kit *k) : mainLayout->setMargin(1); mainLayout->addWidget(inner, 0, 0); - toolTip = tr("Kit name and icon."); - label = createLabel(tr("Name:"), toolTip); + label = createLabel(tr("Name:"), tr("Kit name and icon.")); m_layout->addWidget(label, 0, LabelColumn, alignment); - m_iconButton->setToolTip(toolTip); - auto setIconAction = new QAction(tr("Select Icon File"), this); + m_iconButton->setToolTip(tr("Kit icon.")); + auto setIconAction = new QAction(tr("Select Icon..."), this); m_iconButton->addAction(setIconAction); auto resetIconAction = new QAction(tr("Reset to Device Default Icon"), this); m_iconButton->addAction(resetIconAction); @@ -121,6 +126,15 @@ KitManagerConfigWidget::KitManagerConfigWidget(Kit *k) : auto chooser = new Core::VariableChooser(this); chooser->addSupportedWidget(m_nameEdit); chooser->addMacroExpanderProvider([this]() { return m_modifiedKit->macroExpander(); }); + + for (KitAspect *aspect : KitManager::kitAspects()) + addAspectToWorkingCopy(aspect); + + updateVisibility(); + + if (k && k->isAutoDetected()) + makeStickySubWidgetsReadOnly(); + setVisible(false); } KitManagerConfigWidget::~KitManagerConfigWidget() @@ -142,28 +156,29 @@ QString KitManagerConfigWidget::displayName() const return m_cachedDisplayName; } -QIcon KitManagerConfigWidget::icon() const +QIcon KitManagerConfigWidget::displayIcon() const { - return m_modifiedKit->icon(); + // Special case: Extra warning if there are no errors but name is not unique. + if (m_modifiedKit->isValid() && !m_hasUniqueName) { + static const QIcon warningIcon(Utils::Icons::WARNING.icon()); + return warningIcon; + } + + return m_modifiedKit->displayIcon(); } void KitManagerConfigWidget::apply() { - bool mustSetDefault = m_isDefaultKit; - bool mustRegister = false; - auto toRegister = std::make_unique<Kit>(); - if (!m_kit) { - mustRegister = true; - m_kit = toRegister.get(); + const auto copyIntoKit = [this](Kit *k) { k->copyFrom(m_modifiedKit.get()); }; + if (m_kit) { + copyIntoKit(m_kit); + } else { + m_isRegistering = true; + m_kit = KitManager::registerKit(copyIntoKit); + m_isRegistering = false; } - m_kit->copyFrom(m_modifiedKit.get()); //m_isDefaultKit is reset in discard() here. - if (mustRegister) - KitManager::registerKit(std::move(toRegister)); - - if (mustSetDefault) + if (m_isDefaultKit) KitManager::setDefaultKit(m_kit); - - m_isDefaultKit = mustSetDefault; emit dirty(); } @@ -191,42 +206,35 @@ bool KitManagerConfigWidget::isDirty() const || m_isDefaultKit != (KitManager::defaultKit() == m_kit); } -bool KitManagerConfigWidget::isValid() const -{ - return m_modifiedKit->isValid(); -} - -bool KitManagerConfigWidget::hasWarning() const -{ - return m_modifiedKit->hasWarning() || !m_hasUniqueName; -} - QString KitManagerConfigWidget::validityMessage() const { - QList<Task> tmp; + Tasks tmp; if (!m_hasUniqueName) { - tmp.append(Task(Task::Warning, tr("Display name is not unique."), Utils::FileName(), -1, + tmp.append(Task(Task::Warning, tr("Display name is not unique."), Utils::FilePath(), -1, ProjectExplorer::Constants::TASK_CATEGORY_COMPILE)); } return m_modifiedKit->toHtml(tmp); } -void KitManagerConfigWidget::addConfigWidget(KitConfigWidget *widget) +void KitManagerConfigWidget::addAspectToWorkingCopy(KitAspect *aspect) { + QTC_ASSERT(aspect, return); + KitAspectWidget *widget = aspect->createConfigWidget(workingCopy()); QTC_ASSERT(widget, return); QTC_ASSERT(!m_widgets.contains(widget), return); - const QString name = widget->displayName() + ':'; - QString toolTip = widget->toolTip(); + const QString name = aspect->displayName() + ':'; + QString toolTip = aspect->description(); auto action = new QAction(tr("Mark as Mutable"), nullptr); action->setCheckable(true); - action->setChecked(widget->isMutable()); + action->setChecked(workingCopy()->isMutable(aspect->id())); + action->setEnabled(!widget->isSticky()); widget->mainWidget()->addAction(action); widget->mainWidget()->setContextMenuPolicy(Qt::ActionsContextMenu); - connect(action, &QAction::toggled, this, [this, widget, action] { - widget->setMutable(action->isChecked()); + connect(action, &QAction::toggled, this, [this, aspect, action] { + workingCopy()->setMutable(aspect->id(), action->isChecked()); emit dirty(); }); @@ -249,8 +257,9 @@ void KitManagerConfigWidget::updateVisibility() { int count = m_widgets.count(); for (int i = 0; i < count; ++i) { - KitConfigWidget *widget = m_widgets.at(i); - bool visible = widget->visibleInKit(); + KitAspectWidget *widget = m_widgets.at(i); + const bool visible = widget->visibleInKit() + && !m_modifiedKit->irrelevantAspects().contains(widget->kitInformationId()); widget->mainWidget()->setVisible(visible); if (widget->buttonWidget()) widget->buttonWidget()->setVisible(visible); @@ -265,7 +274,7 @@ void KitManagerConfigWidget::setHasUniqueName(bool unique) void KitManagerConfigWidget::makeStickySubWidgetsReadOnly() { - foreach (KitConfigWidget *w, m_widgets) { + foreach (KitAspectWidget *w, m_widgets) { if (w->isSticky()) w->makeReadOnly(); } @@ -303,24 +312,49 @@ void KitManagerConfigWidget::removeKit() void KitManagerConfigWidget::setIcon() { - const QString path = QFileDialog::getOpenFileName(this, tr("Select Icon"), - m_modifiedKit->iconPath().toString(), - tr("Images (*.png *.xpm *.jpg)")); - if (path.isEmpty()) - return; - - const QIcon icon(path); - if (icon.isNull()) - return; - - m_iconButton->setIcon(icon); - m_modifiedKit->setIconPath(Utils::FileName::fromString(path)); - emit dirty(); + const Core::Id deviceType = DeviceTypeKitAspect::deviceTypeId(m_modifiedKit.get()); + QList<IDeviceFactory *> allDeviceFactories = IDeviceFactory::allDeviceFactories(); + if (deviceType.isValid()) { + const auto less = [deviceType](const IDeviceFactory *f1, const IDeviceFactory *f2) { + if (f1->deviceType() == deviceType) + return true; + if (f2->deviceType() == deviceType) + return false; + return f1->displayName() < f2->displayName(); + }; + Utils::sort(allDeviceFactories, less); + } + QMenu iconMenu; + for (const IDeviceFactory * const factory : qAsConst(allDeviceFactories)) { + if (factory->icon().isNull()) + continue; + iconMenu.addAction(factory->icon(), tr("Default for %1").arg(factory->displayName()), + [this, factory] { + m_iconButton->setIcon(factory->icon()); + m_modifiedKit->setDeviceTypeForIcon(factory->deviceType()); + emit dirty(); + }); + } + iconMenu.addSeparator(); + iconMenu.addAction(Utils::PathChooser::browseButtonLabel(), [this] { + const QString path = QFileDialog::getOpenFileName(this, tr("Select Icon"), + m_modifiedKit->iconPath().toString(), + tr("Images (*.png *.xpm *.jpg)")); + if (path.isEmpty()) + return; + const QIcon icon(path); + if (icon.isNull()) + return; + m_iconButton->setIcon(icon); + m_modifiedKit->setIconPath(Utils::FilePath::fromString(path)); + emit dirty(); + }); + iconMenu.exec(mapToGlobal(m_iconButton->pos())); } void KitManagerConfigWidget::resetIcon() { - m_modifiedKit->setIconPath(Utils::FileName()); + m_modifiedKit->setIconPath(Utils::FilePath()); emit dirty(); } @@ -348,7 +382,7 @@ void KitManagerConfigWidget::workingCopyWasUpdated(Kit *k) k->fix(); m_fixingKit = false; - foreach (KitConfigWidget *w, m_widgets) + foreach (KitAspectWidget *w, m_widgets) w->refresh(); m_cachedDisplayName.clear(); @@ -376,7 +410,7 @@ void KitManagerConfigWidget::kitWasUpdated(Kit *k) void KitManagerConfigWidget::showEvent(QShowEvent *event) { Q_UNUSED(event); - foreach (KitConfigWidget *widget, m_widgets) + foreach (KitAspectWidget *widget, m_widgets) widget->refresh(); } diff --git a/src/plugins/projectexplorer/kitmanagerconfigwidget.h b/src/plugins/projectexplorer/kitmanagerconfigwidget.h index 026a8e25b4..cf1b6b44d6 100644 --- a/src/plugins/projectexplorer/kitmanagerconfigwidget.h +++ b/src/plugins/projectexplorer/kitmanagerconfigwidget.h @@ -25,7 +25,7 @@ #pragma once -#include "kitconfigwidget.h" +#include "kitmanager.h" #include <QWidget> @@ -52,19 +52,18 @@ public: ~KitManagerConfigWidget() override; QString displayName() const; - QIcon icon() const; + QIcon displayIcon() const; void apply(); void discard(); bool isDirty() const; - bool isValid() const; - bool hasWarning() const; QString validityMessage() const; - void addConfigWidget(KitConfigWidget *widget); + void addAspectToWorkingCopy(KitAspect *aspect); void makeStickySubWidgetsReadOnly(); Kit *workingCopy() const; bool configures(Kit *k) const; + bool isRegistering() const { return m_isRegistering; } void setIsDefaultKit(bool d); bool isDefaultKit() const; void removeKit(); @@ -97,13 +96,14 @@ private: QToolButton *m_iconButton; QLineEdit *m_nameEdit; QLineEdit *m_fileSystemFriendlyNameLineEdit; - QList<KitConfigWidget *> m_widgets; + QList<KitAspectWidget *> m_widgets; QList<QLabel *> m_labels; Kit *m_kit; std::unique_ptr<Kit> m_modifiedKit; bool m_isDefaultKit = false; bool m_fixingKit = false; bool m_hasUniqueName = true; + bool m_isRegistering = false; QList<QAction *> m_actions; mutable QString m_cachedDisplayName; }; diff --git a/src/plugins/projectexplorer/kitmodel.cpp b/src/plugins/projectexplorer/kitmodel.cpp index dbc04d559f..5f157dcbb3 100644 --- a/src/plugins/projectexplorer/kitmodel.cpp +++ b/src/plugins/projectexplorer/kitmodel.cpp @@ -44,14 +44,21 @@ namespace Internal { class KitNode : public TreeItem { public: - KitNode(Kit *k) + KitNode(Kit *k, KitModel *m) { - widget = KitManager::createConfigWidget(k); - if (widget) { - if (k && k->isAutoDetected()) - widget->makeStickySubWidgetsReadOnly(); - widget->setVisible(false); - } + widget = new KitManagerConfigWidget(k); + + QObject::connect(widget, &KitManagerConfigWidget::dirty, m, [this] { update(); }); + + QObject::connect(widget, &KitManagerConfigWidget::isAutoDetectedChanged, m, [this, m] { + TreeItem *oldParent = parent(); + TreeItem *newParent = + m->rootItem()->childAt(widget->workingCopy()->isAutoDetected() ? 0 : 1); + if (oldParent && oldParent != newParent) { + m->takeItem(this); + newParent->appendChild(this); + } + }); } ~KitNode() override @@ -78,15 +85,7 @@ public: return baseName; } if (role == Qt::DecorationRole) { - if (!widget->isValid()) { - static const QIcon errorIcon(Utils::Icons::CRITICAL.icon()); - return errorIcon; - } - if (widget->hasWarning()) { - static const QIcon warningIcon(Utils::Icons::WARNING.icon()); - return warningIcon; - } - return widget->icon(); + return widget->displayIcon(); } if (role == Qt::ToolTipRole) { return widget->validityMessage(); @@ -164,35 +163,6 @@ KitManagerConfigWidget *KitModel::widget(const QModelIndex &index) return n ? n->widget : nullptr; } -void KitModel::isAutoDetectedChanged() -{ - auto w = qobject_cast<KitManagerConfigWidget *>(sender()); - int idx = -1; - idx = Utils::indexOf(*m_manualRoot, [w](TreeItem *node) { - return static_cast<KitNode *>(node)->widget == w; - }); - TreeItem *oldParent = nullptr; - TreeItem *newParent = w->workingCopy()->isAutoDetected() ? m_autoRoot : m_manualRoot; - if (idx != -1) { - oldParent = m_manualRoot; - } else { - idx = Utils::indexOf(*m_autoRoot, [w](TreeItem *node) { - return static_cast<KitNode *>(node)->widget == w; - }); - if (idx != -1) { - oldParent = m_autoRoot; - } - } - - if (oldParent && oldParent != newParent) { - beginMoveRows(indexForItem(oldParent), idx, idx, indexForItem(newParent), newParent->childCount()); - TreeItem *n = oldParent->childAt(idx); - takeItem(n); - newParent->appendChild(n); - endMoveRows(); - } -} - void KitModel::validateKitNames() { QHash<QString, int> nameHash; @@ -248,10 +218,12 @@ void KitModel::markForRemoval(Kit *k) delete node; else m_toRemoveList.append(node); + validateKitNames(); } Kit *KitModel::markForAddition(Kit *baseKit) { + const QString newName = newKitName(baseKit ? baseKit->unexpandedDisplayName() : QString()); KitNode *node = createNode(nullptr); m_manualRoot->appendChild(node); Kit *k = node->widget->workingCopy(); @@ -260,10 +232,10 @@ Kit *KitModel::markForAddition(Kit *baseKit) k->copyFrom(baseKit); k->setAutoDetected(false); // Make sure we have a manual kit! k->setSdkProvided(false); - k->setUnexpandedDisplayName(tr("Clone of %1").arg(k->unexpandedDisplayName())); } else { k->setup(); } + k->setUnexpandedDisplayName(newName); if (!m_defaultNode) setDefaultNode(node); @@ -271,6 +243,22 @@ Kit *KitModel::markForAddition(Kit *baseKit) return k; } +void KitModel::updateVisibility() +{ + forItemsAtLevel<2>([](const TreeItem *ti) { + static_cast<const KitNode *>(ti)->widget->updateVisibility(); + }); +} + +QString KitModel::newKitName(const QString &sourceName) const +{ + QList<Kit *> allKits; + forItemsAtLevel<2>([&allKits](const TreeItem *ti) { + allKits << static_cast<const KitNode *>(ti)->widget->workingCopy(); + }); + return Kit::newKitName(sourceName, allKits); +} + KitNode *KitModel::findWorkingCopy(Kit *k) const { return findItemAtLevel<2>([k](KitNode *n) { return n->widget->workingCopy() == k; }); @@ -278,15 +266,8 @@ KitNode *KitModel::findWorkingCopy(Kit *k) const KitNode *KitModel::createNode(Kit *k) { - auto node = new KitNode(k); + auto node = new KitNode(k, this); m_parentLayout->addWidget(node->widget); - connect(node->widget, &KitManagerConfigWidget::dirty, [this, node] { - if (m_autoRoot->indexOf(node) != -1 || m_manualRoot->indexOf(node) != -1) - node->update(); - }); - connect(node->widget, &KitManagerConfigWidget::isAutoDetectedChanged, - this, &KitModel::isAutoDetectedChanged); - return node; } @@ -307,7 +288,7 @@ void KitModel::addKit(Kit *k) { for (TreeItem *n : *m_manualRoot) { // Was added by us - if (static_cast<KitNode *>(n)->widget->configures(k)) + if (static_cast<KitNode *>(n)->widget->isRegistering()) return; } @@ -333,6 +314,7 @@ void KitModel::removeKit(Kit *k) if (m_defaultNode == n) m_defaultNode = nullptr; delete n; + validateKitNames(); return; } } diff --git a/src/plugins/projectexplorer/kitmodel.h b/src/plugins/projectexplorer/kitmodel.h index 8070eefe30..6e9dbed6f4 100644 --- a/src/plugins/projectexplorer/kitmodel.h +++ b/src/plugins/projectexplorer/kitmodel.h @@ -69,6 +69,10 @@ public: void markForRemoval(Kit *k); Kit *markForAddition(Kit *baseKit); + void updateVisibility(); + + QString newKitName(const QString &sourceName) const; + signals: void kitStateChanged(); @@ -78,7 +82,6 @@ private: void removeKit(ProjectExplorer::Kit *k); void changeDefaultKit(); void validateKitNames(); - void isAutoDetectedChanged(); KitNode *findWorkingCopy(Kit *k) const; KitNode *createNode(Kit *k); diff --git a/src/plugins/projectexplorer/kitoptionspage.cpp b/src/plugins/projectexplorer/kitoptionspage.cpp index 262d6c2706..d9ebe3f0e0 100644 --- a/src/plugins/projectexplorer/kitoptionspage.cpp +++ b/src/plugins/projectexplorer/kitoptionspage.cpp @@ -25,6 +25,7 @@ #include "kitoptionspage.h" +#include "filterkitaspectsdialog.h" #include "kitmodel.h" #include "kit.h" #include "projectexplorerconstants.h" @@ -32,6 +33,8 @@ #include "kitmanagerconfigwidget.h" #include "kitmanager.h" +#include <utils/qtcassert.h> + #include <QHBoxLayout> #include <QHeaderView> #include <QItemSelectionModel> @@ -67,10 +70,12 @@ public: QPushButton *m_cloneButton = nullptr; QPushButton *m_delButton = nullptr; QPushButton *m_makeDefaultButton = nullptr; + QPushButton *m_filterButton = nullptr; + QPushButton *m_defaultFilterButton = nullptr; KitModel *m_model = nullptr; QItemSelectionModel *m_selectionModel = nullptr; - QWidget *m_currentWidget = nullptr; + KitManagerConfigWidget *m_currentWidget = nullptr; }; KitOptionsPageWidget::KitOptionsPageWidget() @@ -85,6 +90,12 @@ KitOptionsPageWidget::KitOptionsPageWidget() m_cloneButton = new QPushButton(KitOptionsPage::tr("Clone"), this); m_delButton = new QPushButton(KitOptionsPage::tr("Remove"), this); m_makeDefaultButton = new QPushButton(KitOptionsPage::tr("Make Default"), this); + m_filterButton = new QPushButton(KitOptionsPage::tr("Settings Filter..."), this); + m_filterButton->setToolTip(KitOptionsPage::tr( + "Choose which settings to display for this kit.")); + m_defaultFilterButton = new QPushButton(KitOptionsPage::tr("Default Settings Filter..."), this); + m_defaultFilterButton->setToolTip(KitOptionsPage::tr( + "Choose which kit settings to display by default.")); auto buttonLayout = new QVBoxLayout; buttonLayout->setSpacing(6); @@ -93,6 +104,8 @@ KitOptionsPageWidget::KitOptionsPageWidget() buttonLayout->addWidget(m_cloneButton); buttonLayout->addWidget(m_delButton); buttonLayout->addWidget(m_makeDefaultButton); + buttonLayout->addWidget(m_filterButton); + buttonLayout->addWidget(m_defaultFilterButton); buttonLayout->addStretch(); auto horizontalLayout = new QHBoxLayout; @@ -131,14 +144,28 @@ KitOptionsPageWidget::KitOptionsPageWidget() this, &KitOptionsPageWidget::removeKit); connect(m_makeDefaultButton, &QAbstractButton::clicked, this, &KitOptionsPageWidget::makeDefaultKit); - + connect(m_filterButton, &QAbstractButton::clicked, this, [this] { + QTC_ASSERT(m_currentWidget, return); + FilterKitAspectsDialog dlg(m_currentWidget->workingCopy(), this); + if (dlg.exec() == QDialog::Accepted) { + m_currentWidget->workingCopy()->setIrrelevantAspects(dlg.irrelevantAspects()); + m_currentWidget->updateVisibility(); + } + }); + connect(m_defaultFilterButton, &QAbstractButton::clicked, this, [this] { + FilterKitAspectsDialog dlg(nullptr, this); + if (dlg.exec() == QDialog::Accepted) { + KitManager::setIrrelevantAspects(dlg.irrelevantAspects()); + m_model->updateVisibility(); + } + }); updateState(); } void KitOptionsPageWidget::kitSelectionChanged() { QModelIndex current = currentIndex(); - QWidget *newWidget = m_model->widget(current); + KitManagerConfigWidget * const newWidget = m_model->widget(current); if (newWidget == m_currentWidget) return; @@ -216,6 +243,7 @@ void KitOptionsPageWidget::updateState() m_cloneButton->setEnabled(canCopy); m_delButton->setEnabled(canDelete); m_makeDefaultButton->setEnabled(canMakeDefault); + m_filterButton->setEnabled(canCopy); } QModelIndex KitOptionsPageWidget::currentIndex() const diff --git a/src/plugins/projectexplorer/ldparser.cpp b/src/plugins/projectexplorer/ldparser.cpp index 6da77cee9c..9ce56bf7dd 100644 --- a/src/plugins/projectexplorer/ldparser.cpp +++ b/src/plugins/projectexplorer/ldparser.cpp @@ -68,7 +68,7 @@ void LdParser::stdError(const QString &line) if (lne.startsWith(QLatin1String("collect2:"))) { Task task = Task(Task::Error, lne /* description */, - Utils::FileName() /* filename */, + Utils::FilePath() /* filename */, -1 /* linenumber */, Constants::TASK_CATEGORY_COMPILE); emit addTask(task, 1); @@ -79,7 +79,7 @@ void LdParser::stdError(const QString &line) if (match.hasMatch()) { QString description = match.captured(2); Task task(Task::Warning, description, - Utils::FileName(), -1, + Utils::FilePath(), -1, Constants::TASK_CATEGORY_COMPILE); emit addTask(task, 1); return; @@ -95,7 +95,7 @@ void LdParser::stdError(const QString &line) } else if (description.startsWith(QLatin1String("fatal: "))) { description = description.mid(7); } - Task task(type, description, Utils::FileName() /* filename */, -1 /* line */, + Task task(type, description, Utils::FilePath() /* filename */, -1 /* line */, Constants::TASK_CATEGORY_COMPILE); emit addTask(task, 1); return; @@ -107,12 +107,12 @@ void LdParser::stdError(const QString &line) int lineno = match.captured(7).toInt(&ok); if (!ok) lineno = -1; - Utils::FileName filename = Utils::FileName::fromUserInput(match.captured(1)); + Utils::FilePath filename = Utils::FilePath::fromUserInput(match.captured(1)); const QString sourceFileName = match.captured(4); if (!sourceFileName.isEmpty() && !sourceFileName.startsWith(QLatin1String("(.text")) && !sourceFileName.startsWith(QLatin1String("(.data"))) { - filename = Utils::FileName::fromUserInput(sourceFileName); + filename = Utils::FilePath::fromUserInput(sourceFileName); } QString description = match.captured(8).trimmed(); Task::TaskType type = Task::Error; diff --git a/src/plugins/projectexplorer/linuxiccparser.cpp b/src/plugins/projectexplorer/linuxiccparser.cpp index 04dbb6c65f..6572f47d66 100644 --- a/src/plugins/projectexplorer/linuxiccparser.cpp +++ b/src/plugins/projectexplorer/linuxiccparser.cpp @@ -81,7 +81,7 @@ void LinuxIccParser::stdError(const QString &line) else if (category == QLatin1String("warning")) type = Task::Warning; m_temporary = Task(type, m_firstLine.cap(6).trimmed(), - Utils::FileName::fromUserInput(m_firstLine.cap(1)), + Utils::FilePath::fromUserInput(m_firstLine.cap(1)), m_firstLine.cap(2).toInt(), Constants::TASK_CATEGORY_COMPILE); @@ -141,25 +141,25 @@ void ProjectExplorerPlugin::testLinuxIccOutputParsers_data() QTest::addColumn<OutputParserTester::Channel>("inputChannel"); QTest::addColumn<QString>("childStdOutLines"); QTest::addColumn<QString>("childStdErrLines"); - QTest::addColumn<QList<Task> >("tasks"); + QTest::addColumn<Tasks >("tasks"); QTest::addColumn<QString>("outputLines"); QTest::newRow("pass-through stdout") << QString::fromLatin1("Sometext") << OutputParserTester::STDOUT << QString::fromLatin1("Sometext\n") << QString() - << QList<Task>() + << Tasks() << QString(); QTest::newRow("pass-through stderr") << QString::fromLatin1("Sometext") << OutputParserTester::STDERR << QString() << QString::fromLatin1("Sometext\n") - << QList<Task>() + << Tasks() << QString(); QTest::newRow("pch creation") << QString::fromLatin1("\".pch/Qt5Core.pchi.cpp\": creating precompiled header file \".pch/Qt5Core.pchi\"") << OutputParserTester::STDERR << QString() << QString() - << QList<Task>() + << Tasks() << QString(); QTest::newRow("undeclared function") @@ -169,10 +169,10 @@ void ProjectExplorerPlugin::testLinuxIccOutputParsers_data() "\n") << OutputParserTester::STDERR << QString() << QString::fromLatin1("\n") - << (QList<Task>() + << (Tasks() << Task(Task::Error, QLatin1String("identifier \"f\" is undefined\nf(0);"), - Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 13, + Utils::FilePath::fromUserInput(QLatin1String("main.cpp")), 13, Constants::TASK_CATEGORY_COMPILE)) << QString(); @@ -185,10 +185,10 @@ void ProjectExplorerPlugin::testLinuxIccOutputParsers_data() "\n") << OutputParserTester::STDERR << QString() << QString::fromLatin1("\n") - << (QList<Task>() + << (Tasks() << Task(Task::Error, QLatin1String("identifier \"f\" is undefined\nf(0);"), - Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 13, + Utils::FilePath::fromUserInput(QLatin1String("main.cpp")), 13, Constants::TASK_CATEGORY_COMPILE)) << QString(); @@ -200,10 +200,10 @@ void ProjectExplorerPlugin::testLinuxIccOutputParsers_data() "\n") << OutputParserTester::STDERR << QString() << QString::fromLatin1("\n") - << (QList<Task>() + << (Tasks() << Task(Task::Error, QLatin1String("function \"AClass::privatefunc\" (declared at line 4 of \"main.h\") is inaccessible\nb.privatefunc();"), - Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 53, + Utils::FilePath::fromUserInput(QLatin1String("main.cpp")), 53, Constants::TASK_CATEGORY_COMPILE)) << QString(); @@ -214,20 +214,20 @@ void ProjectExplorerPlugin::testLinuxIccOutputParsers_data() "\n") << OutputParserTester::STDERR << QString() << QString::fromLatin1("\n") - << (QList<Task>() + << (Tasks() << Task(Task::Warning, QLatin1String("use of \"=\" where \"==\" may have been intended\nwhile (a = true)"), - Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 41, + Utils::FilePath::fromUserInput(QLatin1String("main.cpp")), 41, Constants::TASK_CATEGORY_COMPILE)) << QString(); QTest::newRow("moc note") << QString::fromLatin1("/home/qtwebkithelpviewer.h:0: Note: No relevant classes found. No output generated.") << OutputParserTester::STDERR << QString() << QString() - << (QList<ProjectExplorer::Task>() + << (Tasks() << Task(Task::Unknown, QLatin1String("Note: No relevant classes found. No output generated."), - Utils::FileName::fromUserInput(QLatin1String("/home/qtwebkithelpviewer.h")), 0, + Utils::FilePath::fromUserInput(QLatin1String("/home/qtwebkithelpviewer.h")), 0, Constants::TASK_CATEGORY_COMPILE) ) << QString(); @@ -239,7 +239,7 @@ void ProjectExplorerPlugin::testLinuxIccOutputParsers() testbench.appendOutputParser(new LinuxIccParser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); - QFETCH(QList<Task>, tasks); + QFETCH(Tasks, tasks); QFETCH(QString, childStdOutLines); QFETCH(QString, childStdErrLines); QFETCH(QString, outputLines); diff --git a/src/plugins/projectexplorer/localenvironmentaspect.cpp b/src/plugins/projectexplorer/localenvironmentaspect.cpp index 2a6eee97c5..366b938ed4 100644 --- a/src/plugins/projectexplorer/localenvironmentaspect.cpp +++ b/src/plugins/projectexplorer/localenvironmentaspect.cpp @@ -32,54 +32,39 @@ #include <utils/qtcassert.h> -namespace ProjectExplorer { +using namespace Utils; -enum BaseEnvironmentBase { - CleanEnvironmentBase = 0, - SystemEnvironmentBase, - BuildEnvironmentBase -}; +namespace ProjectExplorer { -Utils::Environment LocalEnvironmentAspect::baseEnvironment() const +LocalEnvironmentAspect::LocalEnvironmentAspect(Target *target) { - int base = baseEnvironmentBase(); - Utils::Environment env; - if (base == static_cast<int>(BuildEnvironmentBase)) { - if (BuildConfiguration *bc = m_target->activeBuildConfiguration()) { + setIsLocal(true); + addSupportedBaseEnvironment(tr("Clean Environment"), {}); + + addSupportedBaseEnvironment(tr("System Environment"), [] { + return Environment::systemEnvironment(); + }); + + addPreferredBaseEnvironment(tr("Build Environment"), [target] { + Environment env; + if (BuildConfiguration *bc = target->activeBuildConfiguration()) { env = bc->environment(); } else { // Fallback for targets without buildconfigurations: - env = Utils::Environment::systemEnvironment(); - m_target->kit()->addToEnvironment(env); + env = Environment::systemEnvironment(); + target->kit()->addToEnvironment(env); } - } else if (base == static_cast<int>(SystemEnvironmentBase)) { - env = Utils::Environment::systemEnvironment(); - } - - if (m_baseEnvironmentModifier) - m_baseEnvironmentModifier(env); + return env; + }); - return env; + target->subscribeSignal(&BuildConfiguration::environmentChanged, + this, &LocalEnvironmentAspect::buildEnvironmentHasChanged); + connect(target, &Target::activeBuildConfigurationChanged, + this, &LocalEnvironmentAspect::buildEnvironmentHasChanged); } void LocalEnvironmentAspect::buildEnvironmentHasChanged() { - if (baseEnvironmentBase() == static_cast<int>(BuildEnvironmentBase)) - emit environmentChanged(); -} - -LocalEnvironmentAspect::LocalEnvironmentAspect(Target *target, - const BaseEnvironmentModifier &modifier) : - m_baseEnvironmentModifier(modifier), - m_target(target) -{ - addPreferredBaseEnvironment(BuildEnvironmentBase, tr("Build Environment")); - addSupportedBaseEnvironment(SystemEnvironmentBase, tr("System Environment")); - addSupportedBaseEnvironment(CleanEnvironmentBase, tr("Clean Environment")); - - m_target->subscribeSignal(&BuildConfiguration::environmentChanged, - this, &LocalEnvironmentAspect::buildEnvironmentHasChanged); - connect(m_target, &Target::activeBuildConfigurationChanged, - this, &LocalEnvironmentAspect::buildEnvironmentHasChanged); + emit environmentChanged(); } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/localenvironmentaspect.h b/src/plugins/projectexplorer/localenvironmentaspect.h index 967a985e8f..05653899b2 100644 --- a/src/plugins/projectexplorer/localenvironmentaspect.h +++ b/src/plugins/projectexplorer/localenvironmentaspect.h @@ -34,16 +34,9 @@ class PROJECTEXPLORER_EXPORT LocalEnvironmentAspect : public EnvironmentAspect Q_OBJECT public: - using BaseEnvironmentModifier = std::function<void(Utils::Environment &)>; - LocalEnvironmentAspect(Target *parent, const BaseEnvironmentModifier &modifier); - - Utils::Environment baseEnvironment() const override; + explicit LocalEnvironmentAspect(Target *parent); void buildEnvironmentHasChanged(); - -private: - BaseEnvironmentModifier m_baseEnvironmentModifier; - Target *m_target; }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/makestep.cpp b/src/plugins/projectexplorer/makestep.cpp index b909962ce9..fecaacc89c 100644 --- a/src/plugins/projectexplorer/makestep.cpp +++ b/src/plugins/projectexplorer/makestep.cpp @@ -47,6 +47,7 @@ #include <QThread> using namespace Core; +using namespace Utils; const char BUILD_TARGETS_SUFFIX[] = ".BuildTargets"; const char MAKE_ARGUMENTS_SUFFIX[] = ".MakeArguments"; @@ -78,7 +79,7 @@ bool MakeStep::init() if (!bc) emit addTask(Task::buildConfigurationMissingTask()); - const QString make = effectiveMakeCommand(); + const FilePath make = effectiveMakeCommand(); if (make.isEmpty()) emit addTask(makeCommandMissingTask()); @@ -89,7 +90,7 @@ bool MakeStep::init() ProcessParameters *pp = processParameters(); pp->setMacroExpander(bc->macroExpander()); - pp->setWorkingDirectory(bc->buildDirectory().toString()); + pp->setWorkingDirectory(bc->buildDirectory()); pp->setEnvironment(environment(bc)); pp->setCommand(make); pp->setArguments(allArguments()); @@ -104,7 +105,7 @@ bool MakeStep::init() IOutputParser *parser = target()->kit()->createOutputParser(); if (parser) appendOutputParser(parser); - outputParser()->setWorkingDirectory(pp->effectiveWorkingDirectory()); + outputParser()->setWorkingDirectory(pp->effectiveWorkingDirectory().toString()); return AbstractProcessStep::init(); } @@ -126,7 +127,7 @@ QString MakeStep::defaultDisplayName() static const QList<ToolChain *> preferredToolChains(const Kit *kit) { - QList<ToolChain *> tcs = ToolChainKitInformation::toolChains(kit); + QList<ToolChain *> tcs = ToolChainKitAspect::toolChains(kit); // prefer CXX, then C, then others Utils::sort(tcs, [](ToolChain *tcA, ToolChain *tcB) { if (tcA->language() == tcB->language()) @@ -142,18 +143,18 @@ static const QList<ToolChain *> preferredToolChains(const Kit *kit) return tcs; } -QString MakeStep::defaultMakeCommand() const +FilePath MakeStep::defaultMakeCommand() const { BuildConfiguration *bc = buildConfiguration(); if (!bc) - return QString(); + return {}; const Utils::Environment env = environment(bc); for (const ToolChain *tc : preferredToolChains(target()->kit())) { - const QString make = tc->makeCommand(env); + FilePath make = tc->makeCommand(env); if (!make.isEmpty()) return make; } - return QString(); + return {}; } QString MakeStep::msgNoMakeCommand() @@ -165,7 +166,7 @@ Task MakeStep::makeCommandMissingTask() { return Task(Task::Error, msgNoMakeCommand(), - Utils::FileName(), + Utils::FilePath(), -1, Constants::TASK_CATEGORY_BUILDSYSTEM); } @@ -174,9 +175,7 @@ bool MakeStep::isJobCountSupported() const { const QList<ToolChain *> tcs = preferredToolChains(target()->kit()); const ToolChain *tc = tcs.isEmpty() ? nullptr : tcs.constFirst(); - return tc - && (tc->targetAbi().os() != Abi::WindowsOS - || tc->targetAbi().osFlavor() == Abi::WindowsMSysFlavor); + return tc && tc->isJobCountSupported(); } int MakeStep::jobCount() const @@ -261,7 +260,7 @@ Utils::Environment MakeStep::environment(BuildConfiguration *bc) const return env; } -void MakeStep::setMakeCommand(const QString &command) +void MakeStep::setMakeCommand(const FilePath &command) { m_makeCommand = command; } @@ -272,7 +271,7 @@ QVariantMap MakeStep::toMap() const map.insert(id().withSuffix(BUILD_TARGETS_SUFFIX).toString(), m_buildTargets); map.insert(id().withSuffix(MAKE_ARGUMENTS_SUFFIX).toString(), m_makeArguments); - map.insert(id().withSuffix(MAKE_COMMAND_SUFFIX).toString(), m_makeCommand); + map.insert(id().withSuffix(MAKE_COMMAND_SUFFIX).toString(), m_makeCommand.toString()); map.insert(id().withSuffix(CLEAN_SUFFIX).toString(), m_clean); const QString jobCountKey = id().withSuffix(JOBCOUNT_SUFFIX).toString(); if (m_userJobCount != defaultJobCount()) @@ -287,7 +286,8 @@ bool MakeStep::fromMap(const QVariantMap &map) { m_buildTargets = map.value(id().withSuffix(BUILD_TARGETS_SUFFIX).toString()).toStringList(); m_makeArguments = map.value(id().withSuffix(MAKE_ARGUMENTS_SUFFIX).toString()).toString(); - m_makeCommand = map.value(id().withSuffix(MAKE_COMMAND_SUFFIX).toString()).toString(); + m_makeCommand = FilePath::fromString( + map.value(id().withSuffix(MAKE_COMMAND_SUFFIX).toString()).toString()); m_clean = map.value(id().withSuffix(CLEAN_SUFFIX).toString()).toBool(); m_overrideMakeflags = map.value(id().withSuffix(OVERRIDE_MAKEFLAGS_SUFFIX).toString(), false).toBool(); m_userJobCount = map.value(id().withSuffix(JOBCOUNT_SUFFIX).toString(), defaultJobCount()).toInt(); @@ -325,12 +325,12 @@ void MakeStep::setUserArguments(const QString &args) m_makeArguments = args; } -QString MakeStep::makeCommand() const +FilePath MakeStep::makeCommand() const { return m_makeCommand; } -QString MakeStep::effectiveMakeCommand() const +FilePath MakeStep::effectiveMakeCommand() const { if (!m_makeCommand.isEmpty()) return m_makeCommand; @@ -387,7 +387,7 @@ MakeStepConfigWidget::MakeStepConfigWidget(MakeStep *makeStep) m_ui->makeLineEdit->setExpectedKind(Utils::PathChooser::ExistingCommand); m_ui->makeLineEdit->setBaseDirectory(Utils::PathChooser::homePath()); m_ui->makeLineEdit->setHistoryCompleter("PE.MakeCommand.History"); - m_ui->makeLineEdit->setPath(m_makeStep->makeCommand()); + m_ui->makeLineEdit->setPath(m_makeStep->makeCommand().toString()); m_ui->makeArgumentsLineEdit->setText(m_makeStep->userArguments()); m_ui->nonOverrideWarning->setToolTip("<html><body><p>" + tr("<code>MAKEFLAGS</code> specifies parallel jobs. Check \"%1\" to override.") @@ -460,7 +460,7 @@ void MakeStepConfigWidget::updateDetails() { BuildConfiguration *bc = m_makeStep->buildConfiguration(); - const QString defaultMake = m_makeStep->defaultMakeCommand(); + const QString defaultMake = m_makeStep->defaultMakeCommand().toString(); if (defaultMake.isEmpty()) m_ui->makeLabel->setText(tr("Make:")); else @@ -485,14 +485,14 @@ void MakeStepConfigWidget::updateDetails() ProcessParameters param; param.setMacroExpander(bc->macroExpander()); - param.setWorkingDirectory(bc->buildDirectory().toString()); + param.setWorkingDirectory(bc->buildDirectory()); param.setCommand(m_makeStep->effectiveMakeCommand()); - param.setArguments(m_makeStep->allArguments()); param.setEnvironment(m_makeStep->environment(bc)); if (param.commandMissing()) - setSummaryText(tr("<b>Make:</b> %1 not found in the environment.").arg(param.command())); // Override display text + setSummaryText(tr("<b>Make:</b> %1 not found in the environment.") + .arg(param.command().toString())); // Override display text else setSummaryText(param.summaryInWorkdir(displayName())); } @@ -505,7 +505,7 @@ void MakeStepConfigWidget::itemChanged(QListWidgetItem *item) void MakeStepConfigWidget::makeLineEditTextEdited() { - m_makeStep->setMakeCommand(m_ui->makeLineEdit->rawPath()); + m_makeStep->setMakeCommand(FilePath::fromString(m_ui->makeLineEdit->rawPath())); updateDetails(); } diff --git a/src/plugins/projectexplorer/makestep.h b/src/plugins/projectexplorer/makestep.h index 036b55e254..e44b52851e 100644 --- a/src/plugins/projectexplorer/makestep.h +++ b/src/plugins/projectexplorer/makestep.h @@ -28,6 +28,8 @@ #include "abstractprocessstep.h" #include "projectexplorer_global.h" +#include <utils/fileutils.h> + QT_FORWARD_DECLARE_CLASS(QListWidgetItem); namespace Utils { class Environment; } @@ -56,16 +58,16 @@ public: QString allArguments() const; QString userArguments() const; void setUserArguments(const QString &args); - QString makeCommand() const; - void setMakeCommand(const QString &command); - QString effectiveMakeCommand() const; + Utils::FilePath makeCommand() const; + void setMakeCommand(const Utils::FilePath &command); + Utils::FilePath effectiveMakeCommand() const; void setClean(bool clean); bool isClean() const; static QString defaultDisplayName(); - QString defaultMakeCommand() const; + Utils::FilePath defaultMakeCommand() const; static QString msgNoMakeCommand(); static Task makeCommandMissingTask(); @@ -80,16 +82,18 @@ public: Utils::Environment environment(BuildConfiguration *bc) const; +protected: + bool fromMap(const QVariantMap &map) override; + private: QVariantMap toMap() const override; - bool fromMap(const QVariantMap &map) override; static int defaultJobCount(); QStringList jobArguments() const; QStringList m_buildTargets; QStringList m_availableTargets; QString m_makeArguments; - QString m_makeCommand; + Utils::FilePath m_makeCommand; int m_userJobCount = 4; bool m_overrideMakeflags = false; bool m_clean = false; diff --git a/src/plugins/projectexplorer/miniprojecttargetselector.cpp b/src/plugins/projectexplorer/miniprojecttargetselector.cpp index 7d0573b0ac..aa01c0f9b6 100644 --- a/src/plugins/projectexplorer/miniprojecttargetselector.cpp +++ b/src/plugins/projectexplorer/miniprojecttargetselector.cpp @@ -25,10 +25,8 @@ #include "buildconfiguration.h" #include "deployconfiguration.h" -#include "kitconfigwidget.h" #include "kit.h" #include "kitmanager.h" -#include "kitmanager.h" #include "miniprojecttargetselector.h" #include "projectexplorer.h" #include "projectexplorericons.h" @@ -298,7 +296,7 @@ void ProjectListWidget::addProject(Project *project) setCurrentItem(item); QFontMetrics fn(font()); - int width = fn.width(displayName) + padding(); + int width = fn.horizontalAdvance(displayName) + padding(); if (width > optimalWidth()) setOptimalWidth(width); @@ -333,7 +331,7 @@ void ProjectListWidget::removeProject(Project *project) // recheck optimal width int width = 0; for (int i = 0; i < count(); ++i) - width = qMax(fn.width(item(i)->text()) + padding(), width); + width = qMax(fn.horizontalAdvance(item(i)->text()) + padding(), width); setOptimalWidth(width); m_ignoreIndexChange = false; @@ -377,7 +375,7 @@ void ProjectListWidget::projectDisplayNameChanged(Project *project) QFontMetrics fn(font()); int width = 0; for (int i = 0; i < count(); ++i) - width = qMax(fn.width(item(i)->text()) + padding(), width); + width = qMax(fn.horizontalAdvance(item(i)->text()) + padding(), width); setOptimalWidth(width); m_ignoreIndexChange = false; @@ -424,7 +422,7 @@ void GenericListWidget::setProjectConfigurations(const QList<ProjectConfiguratio int width = 0; foreach (ProjectConfiguration *pc, list) { addProjectConfiguration(pc); - width = qMax(width, fn.width(pc->displayName()) + padding()); + width = qMax(width, fn.horizontalAdvance(pc->displayName()) + padding()); } setOptimalWidth(width); setActiveProjectConfiguration(active); @@ -463,7 +461,7 @@ void GenericListWidget::addProjectConfiguration(ProjectConfiguration *pc) connect(pc, &ProjectConfiguration::toolTipChanged, this, &GenericListWidget::toolTipChanged); QFontMetrics fn(font()); - int width = fn.width(pc->displayName()) + padding(); + int width = fn.horizontalAdvance(pc->displayName()) + padding(); if (width > optimalWidth()) setOptimalWidth(width); @@ -481,7 +479,7 @@ void GenericListWidget::removeProjectConfiguration(ProjectConfiguration *pc) int width = 0; for (int i = 0; i < count(); ++i) { auto *p = item(i)->data(Qt::UserRole).value<ProjectConfiguration *>(); - width = qMax(width, fn.width(p->displayName()) + padding()); + width = qMax(width, fn.horizontalAdvance(p->displayName()) + padding()); } setOptimalWidth(width); @@ -534,7 +532,7 @@ void GenericListWidget::displayNameChanged() int width = 0; for (int i = 0; i < count(); ++i) { auto *p = item(i)->data(Qt::UserRole).value<ProjectConfiguration *>(); - width = qMax(width, fn.width(p->displayName()) + padding()); + width = qMax(width, fn.horizontalAdvance(p->displayName()) + padding()); } setOptimalWidth(width); @@ -579,7 +577,7 @@ KitAreaWidget::~KitAreaWidget() void KitAreaWidget::setKit(Kit *k) { - foreach (KitConfigWidget *w, m_widgets) + foreach (KitAspectWidget *w, m_widgets) delete(w); m_widgets.clear(); @@ -591,11 +589,11 @@ void KitAreaWidget::setKit(Kit *k) m_labels.clear(); int row = 0; - foreach (KitInformation *ki, KitManager::kitInformation()) { - if (k && k->isMutable(ki->id())) { - KitConfigWidget *widget = ki->createConfigWidget(k); + for (KitAspect *aspect : KitManager::kitAspects()) { + if (k && k->isMutable(aspect->id())) { + KitAspectWidget *widget = aspect->createConfigWidget(k); m_widgets << widget; - QLabel *label = new QLabel(widget->displayName()); + QLabel *label = new QLabel(aspect->displayName()); m_labels << label; widget->setStyle(QStyleFactory::create(QLatin1String("fusion"))); @@ -619,10 +617,10 @@ void KitAreaWidget::updateKit(Kit *k) return; bool addedMutables = false; - QList<Core::Id> knownIdList = Utils::transform(m_widgets, &KitConfigWidget::kitInformationId); + QList<Core::Id> knownIdList = Utils::transform(m_widgets, &KitAspectWidget::kitInformationId); - foreach (KitInformation *ki, KitManager::kitInformation()) { - Core::Id currentId = ki->id(); + for (KitAspect *aspect : KitManager::kitAspects()) { + const Core::Id currentId = aspect->id(); if (m_kit->isMutable(currentId) && !knownIdList.removeOne(currentId)) { addedMutables = true; break; @@ -635,7 +633,7 @@ void KitAreaWidget::updateKit(Kit *k) setKit(m_kit); } else { // Refresh all widgets if the number of mutable settings did not change - foreach (KitConfigWidget *w, m_widgets) + foreach (KitAspectWidget *w, m_widgets) w->refresh(); } } diff --git a/src/plugins/projectexplorer/miniprojecttargetselector.h b/src/plugins/projectexplorer/miniprojecttargetselector.h index 0c2e51a4f4..032f69f7eb 100644 --- a/src/plugins/projectexplorer/miniprojecttargetselector.h +++ b/src/plugins/projectexplorer/miniprojecttargetselector.h @@ -37,7 +37,7 @@ QT_END_NAMESPACE namespace ProjectExplorer { class Kit; -class KitConfigWidget; +class KitAspectWidget; class Project; class Target; class BuildConfiguration; @@ -101,7 +101,7 @@ private: QGridLayout *m_layout; Kit *m_kit = nullptr; - QList<KitConfigWidget *> m_widgets; + QList<KitAspectWidget *> m_widgets; QList<QLabel *> m_labels; }; diff --git a/src/plugins/projectexplorer/msvcparser.cpp b/src/plugins/projectexplorer/msvcparser.cpp index b99880252a..2d3095c168 100644 --- a/src/plugins/projectexplorer/msvcparser.cpp +++ b/src/plugins/projectexplorer/msvcparser.cpp @@ -35,11 +35,11 @@ using namespace Utils; // As of MSVC 2015: "foo.cpp(42) :" -> "foo.cpp(42):" static const char FILE_POS_PATTERN[] = "^(?:\\d+>)?(cl|LINK|.+[^ ]) ?: "; -static QPair<FileName, int> parseFileName(const QString &input) +static QPair<FilePath, int> parseFileName(const QString &input) { QString fileName = input; if (fileName.startsWith("LINK") || fileName.startsWith("cl")) - return qMakePair(FileName(), -1); + return qMakePair(FilePath(), -1); // Extract linenumber (if it is there): int linenumber = -1; @@ -59,7 +59,7 @@ static QPair<FileName, int> parseFileName(const QString &input) } } const QString normalized = FileUtils::normalizePathName(fileName); - return qMakePair(FileName::fromUserInput(normalized), linenumber); + return qMakePair(FilePath::fromUserInput(normalized), linenumber); } using namespace ProjectExplorer; @@ -78,7 +78,7 @@ static bool handleNmakeJomMessage(const QString &line, Task *task) *task = Task(Task::Error, line.mid(matchLength).trimmed(), /* description */ - FileName(), /* fileName */ + FilePath(), /* fileName */ -1, /* linenumber */ Constants::TASK_CATEGORY_COMPILE); return true; @@ -146,7 +146,7 @@ void MsvcParser::stdOutput(const QString &line) if (!match.captured(1).isEmpty()) description.chop(1); // Remove trailing quote m_lastTask = Task(Task::Unknown, description, - FileName::fromUserInput(match.captured(2)), /* fileName */ + FilePath::fromUserInput(match.captured(2)), /* fileName */ match.captured(3).toInt(), /* linenumber */ Constants::TASK_CATEGORY_COMPILE); m_lines = 1; @@ -178,7 +178,7 @@ bool MsvcParser::processCompileLine(const QString &line) QRegularExpressionMatch match = m_compileRegExp.match(line); if (match.hasMatch()) { - QPair<FileName, int> position = parseFileName(match.captured(1)); + QPair<FilePath, int> position = parseFileName(match.captured(1)); m_lastTask = Task(taskType(match.captured(2)), match.captured(3) + match.captured(4).trimmed(), // description position.first, position.second, @@ -261,7 +261,7 @@ void ClangClParser::stdError(const QString &lineIn) QRegularExpressionMatch match = m_compileRegExp.match(line); if (match.hasMatch()) { doFlush(); - const QPair<FileName, int> position = parseFileName(match.captured(1)); + const QPair<FilePath, int> position = parseFileName(match.captured(1)); m_lastTask = Task(taskType(match.captured(2)), match.captured(3).trimmed(), position.first, position.second, Constants::TASK_CATEGORY_COMPILE); @@ -307,28 +307,28 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() QTest::addColumn<OutputParserTester::Channel>("inputChannel"); QTest::addColumn<QString>("childStdOutLines"); QTest::addColumn<QString>("childStdErrLines"); - QTest::addColumn<QList<Task> >("tasks"); + QTest::addColumn<Tasks >("tasks"); QTest::addColumn<QString>("outputLines"); QTest::newRow("pass-through stdout") << "Sometext" << OutputParserTester::STDOUT << "Sometext\n" << "" - << QList<Task>() + << Tasks() << ""; QTest::newRow("pass-through stderr") << "Sometext" << OutputParserTester::STDERR << "" << "Sometext\n" - << QList<Task>() + << Tasks() << ""; QTest::newRow("labeled error") << "qmlstandalone\\main.cpp(54) : error C4716: 'findUnresolvedModule' : must return a value" << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Error, "C4716: 'findUnresolvedModule' : must return a value", - FileName::fromUserInput("qmlstandalone\\main.cpp"), 54, + FilePath::fromUserInput("qmlstandalone\\main.cpp"), 54, Constants::TASK_CATEGORY_COMPILE)) << ""; @@ -336,10 +336,10 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() << "qmlstandalone\\main.cpp(54): error C4716: 'findUnresolvedModule' : must return a value" << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Error, "C4716: 'findUnresolvedModule' : must return a value", - FileName::fromUserInput("qmlstandalone\\main.cpp"), 54, + FilePath::fromUserInput("qmlstandalone\\main.cpp"), 54, Constants::TASK_CATEGORY_COMPILE)) << ""; @@ -347,10 +347,10 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() << "1>qmlstandalone\\main.cpp(54) : error C4716: 'findUnresolvedModule' : must return a value" << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Error, "C4716: 'findUnresolvedModule' : must return a value", - FileName::fromUserInput("qmlstandalone\\main.cpp"), 54, + FilePath::fromUserInput("qmlstandalone\\main.cpp"), 54, Constants::TASK_CATEGORY_COMPILE)) << ""; @@ -358,10 +358,10 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() << "x:\\src\\plugins\\projectexplorer\\msvcparser.cpp(69) : warning C4100: 'something' : unreferenced formal parameter" << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Warning, "C4100: 'something' : unreferenced formal parameter", - FileName::fromUserInput("x:\\src\\plugins\\projectexplorer\\msvcparser.cpp"), 69, + FilePath::fromUserInput("x:\\src\\plugins\\projectexplorer\\msvcparser.cpp"), 69, Constants::TASK_CATEGORY_COMPILE)) << ""; @@ -370,10 +370,10 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() << "1>x:\\src\\plugins\\projectexplorer\\msvcparser.cpp(69) : warning C4100: 'something' : unreferenced formal parameter" << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Warning, "C4100: 'something' : unreferenced formal parameter", - FileName::fromUserInput("x:\\src\\plugins\\projectexplorer\\msvcparser.cpp"), 69, + FilePath::fromUserInput("x:\\src\\plugins\\projectexplorer\\msvcparser.cpp"), 69, Constants::TASK_CATEGORY_COMPILE)) << ""; @@ -382,14 +382,14 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() " x:\\src\\plugins\\texteditor\\completionsupport.h(39) : see declaration of 'TextEditor::CompletionItem'" << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Warning, "C4099: 'TextEditor::CompletionItem' : type name first seen using 'struct' now seen using 'class'", - FileName::fromUserInput("x:\\src\\plugins\\texteditor\\icompletioncollector.h"), 50, + FilePath::fromUserInput("x:\\src\\plugins\\texteditor\\icompletioncollector.h"), 50, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Unknown, "see declaration of 'TextEditor::CompletionItem'", - FileName::fromUserInput("x:\\src\\plugins\\texteditor\\completionsupport.h"), 39, + FilePath::fromUserInput("x:\\src\\plugins\\texteditor\\completionsupport.h"), 39, Constants::TASK_CATEGORY_COMPILE)) << ""; @@ -398,14 +398,14 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() " x:\\src\\plugins\\texteditor\\completionsupport.h(39) : see declaration of 'TextEditor::CompletionItem'" << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Warning, "C4099: 'TextEditor::CompletionItem' : type name first seen using 'struct' now seen using 'class'", - FileName::fromUserInput("x:\\src\\plugins\\texteditor\\icompletioncollector.h"), 50, + FilePath::fromUserInput("x:\\src\\plugins\\texteditor\\icompletioncollector.h"), 50, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Unknown, "see declaration of 'TextEditor::CompletionItem'", - FileName::fromUserInput("x:\\src\\plugins\\texteditor\\completionsupport.h"), 39, + FilePath::fromUserInput("x:\\src\\plugins\\texteditor\\completionsupport.h"), 39, Constants::TASK_CATEGORY_COMPILE)) << ""; @@ -413,10 +413,10 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() << "LINK : fatal error LNK1146: no argument specified with option '/LIBPATH:'" << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Error, "LNK1146: no argument specified with option '/LIBPATH:'", - FileName(), -1, + FilePath(), -1, Constants::TASK_CATEGORY_COMPILE)) << ""; @@ -425,10 +425,10 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() << "cl : Command line warning D9002 : ignoring unknown option '-fopenmp'" << OutputParserTester::STDERR << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Warning, "D9002 : ignoring unknown option '-fopenmp'", - FileName(), -1, + FilePath(), -1, Constants::TASK_CATEGORY_COMPILE)) << ""; QTest::newRow("complex error") @@ -440,7 +440,7 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() " No constructor could take the source type, or constructor overload resolution was ambiguous" << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Error, "C2440: 'initializing' : cannot convert from 'int' to 'std::_Tree<_Traits>::iterator'\n" "with\n" @@ -448,27 +448,27 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() " _Traits=std::_Tmap_traits<int,double,std::less<int>,std::allocator<std::pair<const int,double>>,false>\n" "]\n" "No constructor could take the source type, or constructor overload resolution was ambiguous", - FileName::fromUserInput("..\\untitled\\main.cpp"), 19, + FilePath::fromUserInput("..\\untitled\\main.cpp"), 19, Constants::TASK_CATEGORY_COMPILE)) << ""; QTest::newRow("Linker error 1") << "main.obj : error LNK2019: unresolved external symbol \"public: void __thiscall Data::doit(void)\" (?doit@Data@@QAEXXZ) referenced in function _main" << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Error, "LNK2019: unresolved external symbol \"public: void __thiscall Data::doit(void)\" (?doit@Data@@QAEXXZ) referenced in function _main", - FileName::fromUserInput("main.obj"), -1, + FilePath::fromUserInput("main.obj"), -1, Constants::TASK_CATEGORY_COMPILE)) << ""; QTest::newRow("Linker error 2") << "debug\\Experimentation.exe : fatal error LNK1120: 1 unresolved externals" << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Error, "LNK1120: 1 unresolved externals", - FileName::fromUserInput("debug\\Experimentation.exe"), -1, + FilePath::fromUserInput("debug\\Experimentation.exe"), -1, Constants::TASK_CATEGORY_COMPILE)) << ""; @@ -476,20 +476,20 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() << "Error: dependent '..\\..\\..\\..\\creator-2.5\\src\\plugins\\coreplugin\\ifile.h' does not exist." << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Error, "dependent '..\\..\\..\\..\\creator-2.5\\src\\plugins\\coreplugin\\ifile.h' does not exist.", - FileName(), -1, + FilePath(), -1, Constants::TASK_CATEGORY_COMPILE)) << ""; QTest::newRow("jom error") << "Error: dependent 'main.cpp' does not exist." << OutputParserTester::STDERR << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Error, "dependent 'main.cpp' does not exist.", - FileName(), -1, + FilePath(), -1, Constants::TASK_CATEGORY_COMPILE)) << ""; @@ -504,14 +504,14 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() " ]" << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Warning, "C4996: 'std::_Copy_impl': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'", - FileName::fromUserInput("c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE\\xutility"), 2227, + FilePath::fromUserInput("c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE\\xutility"), 2227, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Unknown, "see declaration of 'std::_Copy_impl'", - FileName::fromUserInput("c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE\\xutility"), 2212, + FilePath::fromUserInput("c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE\\xutility"), 2212, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Unknown, "see reference to function template instantiation '_OutIt std::copy<const unsigned char*,unsigned short*>(_InIt,_InIt,_OutIt)' being compiled\n" @@ -520,7 +520,7 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() " _OutIt=unsigned short *,\n" " _InIt=const unsigned char *\n" "]", - FileName::fromUserInput("symbolgroupvalue.cpp"), 2314, + FilePath::fromUserInput("symbolgroupvalue.cpp"), 2314, Constants::TASK_CATEGORY_COMPILE)) << ""; @@ -530,25 +530,25 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() " or 'D:\\Project\\types.h(71) : Types::UINT64'" << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Error, "C2872: 'UINT64' : ambiguous symbol", - FileName::fromUserInput("D:\\Project\\file.h"), 98, + FilePath::fromUserInput("D:\\Project\\file.h"), 98, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Unknown, "could be unsigned __int64 UINT64", - FileName::fromUserInput("C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\include\\basetsd.h"), 83, + FilePath::fromUserInput("C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\include\\basetsd.h"), 83, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Unknown, "or Types::UINT64", - FileName::fromUserInput("D:\\Project\\types.h"), 71, + FilePath::fromUserInput("D:\\Project\\types.h"), 71, Constants::TASK_CATEGORY_COMPILE)) << ""; QTest::newRow("ignore moc note") << "/home/qtwebkithelpviewer.h:0: Note: No relevant classes found. No output generated." << OutputParserTester::STDERR << "" << "/home/qtwebkithelpviewer.h:0: Note: No relevant classes found. No output generated.\n" - << (QList<ProjectExplorer::Task>()) + << (Tasks()) << ""; QTest::newRow("error with note") @@ -556,14 +556,14 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() "main.cpp(6): note: see declaration of 'func'" << OutputParserTester::STDOUT << "" << "" - << (QList<Task>() + << (Tasks() << Task(Task::Error, "C2733: 'func': second C linkage of overloaded function not allowed", - FileName::fromUserInput("main.cpp"), 7, + FilePath::fromUserInput("main.cpp"), 7, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Unknown, "see declaration of 'func'", - FileName::fromUserInput("main.cpp"), 6, + FilePath::fromUserInput("main.cpp"), 6, Constants::TASK_CATEGORY_COMPILE)) << ""; @@ -571,10 +571,10 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() << QString::fromUtf8("cl: командная строка warning D9025: переопределение \"/MDd\" на \"/MTd\"") << OutputParserTester::STDERR << "" << "" - << (QList<ProjectExplorer::Task>() + << (Tasks() << Task(Task::Warning, QString::fromUtf8("D9025: переопределение \"/MDd\" на \"/MTd\""), - FileName(), -1, Constants::TASK_CATEGORY_COMPILE)) + FilePath(), -1, Constants::TASK_CATEGORY_COMPILE)) << ""; } @@ -584,7 +584,7 @@ void ProjectExplorerPlugin::testMsvcOutputParsers() testbench.appendOutputParser(new MsvcParser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); - QFETCH(QList<Task>, tasks); + QFETCH(Tasks, tasks); QFETCH(QString, childStdOutLines); QFETCH(QString, childStdErrLines); QFETCH(QString, outputLines); @@ -600,7 +600,7 @@ void ProjectExplorerPlugin::testClangClOutputParsers_data() QTest::addColumn<OutputParserTester::Channel>("inputChannel"); QTest::addColumn<QString>("childStdOutLines"); QTest::addColumn<QString>("childStdErrLines"); - QTest::addColumn<QList<Task> >("tasks"); + QTest::addColumn<Tasks >("tasks"); QTest::addColumn<QString>("outputLines"); const QString warning1 = "private field 'm_version' is not used [-Wunused-private-field]\n" @@ -643,21 +643,21 @@ void ProjectExplorerPlugin::testClangClOutputParsers_data() << input << OutputParserTester::STDERR << "" << expectedStderr - << (QList<Task>() + << (Tasks() << Task(Task::Warning, warning1.trimmed(), - FileName::fromUserInput("./qwindowseglcontext.h"), 282, + FilePath::fromUserInput("./qwindowseglcontext.h"), 282, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Warning, warning2.trimmed(), - FileName::fromUserInput(".\\qwindowsclipboard.cpp"), 60, + FilePath::fromUserInput(".\\qwindowsclipboard.cpp"), 60, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Warning, warning3.trimmed(), - FileName::fromUserInput(".\\qwindowsclipboard.cpp"), 61, + FilePath::fromUserInput(".\\qwindowsclipboard.cpp"), 61, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Error, expectedError1, - FileName::fromUserInput(".\\qwindowsgdinativeinterface.cpp"), 48, + FilePath::fromUserInput(".\\qwindowsgdinativeinterface.cpp"), 48, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Error, error2.trimmed(), - FileName::fromUserInput(".\\qwindowsgdinativeinterface.cpp"), 51, + FilePath::fromUserInput(".\\qwindowsgdinativeinterface.cpp"), 51, Constants::TASK_CATEGORY_COMPILE)) << ""; } @@ -668,7 +668,7 @@ void ProjectExplorerPlugin::testClangClOutputParsers() testbench.appendOutputParser(new ClangClParser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); - QFETCH(QList<Task>, tasks); + QFETCH(Tasks, tasks); QFETCH(QString, childStdOutLines); QFETCH(QString, childStdErrLines); QFETCH(QString, outputLines); diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 9c86a70686..24b528154d 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -60,10 +60,13 @@ #include <QComboBox> #include <QFormLayout> +using namespace Utils; + #define KEY_ROOT "ProjectExplorer.MsvcToolChain." static const char varsBatKeyC[] = KEY_ROOT "VarsBat"; static const char varsBatArgKeyC[] = KEY_ROOT "VarsBatArg"; static const char supportedAbiKeyC[] = KEY_ROOT "SupportedAbi"; +static const char supportedAbisKeyC[] = KEY_ROOT "SupportedAbis"; static const char environModsKeyC[] = KEY_ROOT "environmentModifications"; enum { debug = 0 }; @@ -206,7 +209,7 @@ static Utils::optional<VisualStudioInstallation> installationFromPathAndVersion( installation.vsName = versionString; installation.vcVarsPath = vcVarsPath; installation.vcVarsAll = vcVarsAllPath; - return std::move(installation); + return installation; } // Detect build tools introduced with MSVC2017 @@ -349,33 +352,50 @@ static QVector<VisualStudioInstallation> detectVisualStudio() return detectVisualStudioFromRegistry(); } +static unsigned char wordWidthForPlatform(MsvcToolChain::Platform platform) +{ + switch (platform) { + case ProjectExplorer::Internal::MsvcToolChain::x86: + case ProjectExplorer::Internal::MsvcToolChain::arm: + case ProjectExplorer::Internal::MsvcToolChain::x86_arm: + case ProjectExplorer::Internal::MsvcToolChain::amd64_arm: + case ProjectExplorer::Internal::MsvcToolChain::amd64_x86: + return 32; + case ProjectExplorer::Internal::MsvcToolChain::amd64: + case ProjectExplorer::Internal::MsvcToolChain::x86_amd64: + case ProjectExplorer::Internal::MsvcToolChain::ia64: + case ProjectExplorer::Internal::MsvcToolChain::x86_ia64: + return 64; + } + + return 0; +} + +static Abi::Architecture archForPlatform(MsvcToolChain::Platform platform) +{ + switch (platform) { + case ProjectExplorer::Internal::MsvcToolChain::x86: + case ProjectExplorer::Internal::MsvcToolChain::amd64: + case ProjectExplorer::Internal::MsvcToolChain::x86_amd64: + case ProjectExplorer::Internal::MsvcToolChain::amd64_x86: + return Abi::X86Architecture; + case ProjectExplorer::Internal::MsvcToolChain::arm: + case ProjectExplorer::Internal::MsvcToolChain::x86_arm: + case ProjectExplorer::Internal::MsvcToolChain::amd64_arm: + return Abi::ArmArchitecture; + case ProjectExplorer::Internal::MsvcToolChain::ia64: + case ProjectExplorer::Internal::MsvcToolChain::x86_ia64: + return Abi::ItaniumArchitecture; + } + + return Abi::UnknownArchitecture; +} + static Abi findAbiOfMsvc(MsvcToolChain::Type type, MsvcToolChain::Platform platform, const QString &version) { - Abi::Architecture arch = Abi::X86Architecture; Abi::OSFlavor flavor = Abi::UnknownFlavor; - int wordWidth = 64; - - switch (platform) { - case MsvcToolChain::x86: - case MsvcToolChain::amd64_x86: - wordWidth = 32; - break; - case MsvcToolChain::ia64: - case MsvcToolChain::x86_ia64: - arch = Abi::ItaniumArchitecture; - break; - case MsvcToolChain::amd64: - case MsvcToolChain::x86_amd64: - break; - case MsvcToolChain::arm: - case MsvcToolChain::x86_arm: - case MsvcToolChain::amd64_arm: - arch = Abi::ArmArchitecture; - wordWidth = 32; - break; - }; QString msvcVersionString = version; if (type == MsvcToolChain::WindowsSDK) { @@ -400,7 +420,8 @@ static Abi findAbiOfMsvc(MsvcToolChain::Type type, flavor = Abi::WindowsMsvc2008Flavor; else flavor = Abi::WindowsMsvc2005Flavor; - const Abi result = Abi(arch, Abi::WindowsOS, flavor, Abi::PEFormat, wordWidth); + const Abi result = Abi(archForPlatform(platform), Abi::WindowsOS, flavor, Abi::PEFormat, + wordWidthForPlatform(platform)); if (!result.isValid()) qWarning("Unable to completely determine the ABI of MSVC version %s (%s).", qPrintable(version), @@ -592,7 +613,7 @@ Macros MsvcToolChain::msvcPredefinedMacros(const QStringList &cxxflags, cpp.setEnvironment(env.toStringList()); cpp.setWorkingDirectory(Utils::TemporaryDirectory::masterDirectoryPath()); QStringList arguments; - const Utils::FileName binary = env.searchInPath(QLatin1String("cl.exe")); + const Utils::FilePath binary = env.searchInPath(QLatin1String("cl.exe")); if (binary.isEmpty()) { qWarning("%s: The compiler binary cl.exe could not be found in the path.", Q_FUNC_INFO); return predefinedMacros; @@ -748,10 +769,46 @@ void MsvcToolChain::updateEnvironmentModifications(QList<Utils::EnvironmentItem> Utils::EnvironmentItem::sort(&modifications); if (modifications != m_environmentModifications) { m_environmentModifications = modifications; + rescanForCompiler(); toolChainUpdated(); } } +void MsvcToolChain::detectInstalledAbis() +{ + static QMap<QString, Abis> abiCache; + const QString vcVarsBase + = QDir::fromNativeSeparators(m_vcvarsBat).left(m_vcvarsBat.lastIndexOf('/')); + if (abiCache.contains(vcVarsBase)) { + m_supportedAbis = abiCache.value(vcVarsBase); + return; + } + + QTC_ASSERT(m_supportedAbis.isEmpty(), return); + const Abi baseAbi = targetAbi(); + for (MsvcPlatform platform : platforms) { + bool toolchainInstalled = false; + QString perhapsVcVarsPath = vcVarsBase + QLatin1Char('/') + QLatin1String(platform.bat); + const Platform p = platform.platform; + if (QFileInfo(perhapsVcVarsPath).isFile()) { + toolchainInstalled = true; + } else { + // MSVC 2015 and below had various versions of vcvars scripts in subfolders. Try these + // as fallbacks. + perhapsVcVarsPath = vcVarsBase + platform.prefix + QLatin1Char('/') + + QLatin1String(platform.bat); + toolchainInstalled = QFileInfo(perhapsVcVarsPath).isFile(); + } + if (hostSupportsPlatform(platform.platform) && toolchainInstalled) { + Abi newAbi(archForPlatform(p), baseAbi.os(), baseAbi.osFlavor(), baseAbi.binaryFormat(), + wordWidthForPlatform(p)); + if (!m_supportedAbis.contains(newAbi)) + m_supportedAbis.append(newAbi); + } + } + abiCache.insert(vcVarsBase, m_supportedAbis); +} + Utils::Environment MsvcToolChain::readEnvironmentSetting(const Utils::Environment &env) const { Utils::Environment resultEnv = env; @@ -780,67 +837,34 @@ Utils::Environment MsvcToolChain::readEnvironmentSetting(const Utils::Environmen MsvcToolChain::MsvcToolChain(const QString &name, const Abi &abi, const QString &varsBat, - const QString &varsBatArg, - Core::Id l, - Detection d) - : MsvcToolChain(Constants::MSVC_TOOLCHAIN_TYPEID, name, abi, varsBat, varsBatArg, l, d) + const QString &varsBatArg) + : MsvcToolChain(Constants::MSVC_TOOLCHAIN_TYPEID, name, abi, varsBat, varsBatArg) {} -MsvcToolChain::MsvcToolChain(const MsvcToolChain &other) - : ToolChain(other) - , m_headerPathsMutex(new QMutex) - , m_environmentModifications(other.m_environmentModifications) - , m_debuggerCommand(other.m_debuggerCommand) - , m_predefinedMacrosCache(other.m_predefinedMacrosCache) - , m_lastEnvironment(other.m_lastEnvironment) - , m_resultEnvironment(other.m_resultEnvironment) - , m_abi(other.m_abi) - , m_vcvarsBat(other.m_vcvarsBat) - , m_varsBatArg(other.m_varsBatArg) -{ - if (other.m_envModWatcher.isRunning()) { - initEnvModWatcher(other.m_envModWatcher.future()); - } else if (m_environmentModifications.isEmpty() && other.m_envModWatcher.future().isFinished() - && !other.m_envModWatcher.future().isCanceled()) { - const GenerateEnvResult &result = m_envModWatcher.result(); - if (result.error) { - const QString &errorMessage = *result.error; - if (!errorMessage.isEmpty()) - TaskHub::addTask(Task::Error, errorMessage, Constants::TASK_CATEGORY_COMPILE); - } else { - updateEnvironmentModifications(result.environmentItems); - } - } - - setDisplayName(other.displayName()); -} - static void addToAvailableMsvcToolchains(const MsvcToolChain *toolchain) { if (toolchain->typeId() != Constants::MSVC_TOOLCHAIN_TYPEID) return; - g_availableMsvcToolchains.push_back(toolchain); + if (!g_availableMsvcToolchains.contains(toolchain)) + g_availableMsvcToolchains.push_back(toolchain); } MsvcToolChain::MsvcToolChain(Core::Id typeId, const QString &name, const Abi &abi, const QString &varsBat, - const QString &varsBatArg, - Core::Id l, - Detection d) - : ToolChain(typeId, d) + const QString &varsBatArg) + : ToolChain(typeId) , m_headerPathsMutex(new QMutex) - , m_predefinedMacrosCache(std::make_shared<Cache<MacroInspectionReport, 64>>()) , m_lastEnvironment(Utils::Environment::systemEnvironment()) , m_abi(abi) , m_vcvarsBat(varsBat) , m_varsBatArg(varsBatArg) { + detectInstalledAbis(); addToAvailableMsvcToolchains(this); - setLanguage(l); initEnvModWatcher(Utils::runAsync(envModThreadPool(), &MsvcToolChain::environmentModifications, varsBat, @@ -852,9 +876,7 @@ MsvcToolChain::MsvcToolChain(Core::Id typeId, } MsvcToolChain::MsvcToolChain(Core::Id typeId) - : ToolChain(typeId, ManualDetection) - , m_predefinedMacrosCache(std::make_shared<Cache<MacroInspectionReport, 64>>()) - , m_lastEnvironment(Utils::Environment::systemEnvironment()) + : ToolChain(typeId) {} void MsvcToolChain::inferWarningsForLevel(int warningLevel, WarningFlags &flags) @@ -876,11 +898,6 @@ void MsvcToolChain::inferWarningsForLevel(int warningLevel, WarningFlags &flags) flags |= WarningFlags::UnusedParams; } -void MsvcToolChain::toolChainUpdated() -{ - m_predefinedMacrosCache->invalidate(); -} - MsvcToolChain::MsvcToolChain() : MsvcToolChain(Constants::MSVC_TOOLCHAIN_TYPEID) {} @@ -896,6 +913,19 @@ Abi MsvcToolChain::targetAbi() const return m_abi; } +Abis MsvcToolChain::supportedAbis() const +{ + return m_supportedAbis; +} + +void MsvcToolChain::setTargetAbi(const Abi &abi) +{ + if (m_abi == abi) + return; + + m_abi = abi; +} + bool MsvcToolChain::isValid() const { if (m_vcvarsBat.isEmpty()) @@ -915,53 +945,52 @@ QString MsvcToolChain::typeDisplayName() const return MsvcToolChainFactory::tr("MSVC"); } -Utils::FileNameList MsvcToolChain::suggestedMkspecList() const +QStringList MsvcToolChain::suggestedMkspecList() const { - Utils::FileNameList result; - result << Utils::FileName::fromLatin1("win32-msvc"); // Common MSVC mkspec introduced in 5.8.1 + QStringList result = {"win32-msvc"}; // Common MSVC mkspec introduced in 5.8.1 switch (m_abi.osFlavor()) { case Abi::WindowsMsvc2005Flavor: - result << Utils::FileName::fromLatin1("win32-msvc2005"); + result << "win32-msvc2005"; break; case Abi::WindowsMsvc2008Flavor: - result << Utils::FileName::fromLatin1("win32-msvc2008"); + result << "win32-msvc2008"; break; case Abi::WindowsMsvc2010Flavor: - result << Utils::FileName::fromLatin1("win32-msvc2010"); + result << "win32-msvc2010"; break; case Abi::WindowsMsvc2012Flavor: - result << Utils::FileName::fromLatin1("win32-msvc2012") - << Utils::FileName::fromLatin1("win32-msvc2010"); + result << "win32-msvc2012" + << "win32-msvc2010"; break; case Abi::WindowsMsvc2013Flavor: - result << Utils::FileName::fromLatin1("win32-msvc2013") - << Utils::FileName::fromLatin1("winphone-arm-msvc2013") - << Utils::FileName::fromLatin1("winphone-x86-msvc2013") - << Utils::FileName::fromLatin1("winrt-arm-msvc2013") - << Utils::FileName::fromLatin1("winrt-x86-msvc2013") - << Utils::FileName::fromLatin1("winrt-x64-msvc2013") - << Utils::FileName::fromLatin1("win32-msvc2012") - << Utils::FileName::fromLatin1("win32-msvc2010"); + result << "win32-msvc2013" + << "winphone-arm-msvc2013" + << "winphone-x86-msvc2013" + << "winrt-arm-msvc2013" + << "winrt-x86-msvc2013" + << "winrt-x64-msvc2013" + << "win32-msvc2012" + << "win32-msvc2010"; break; case Abi::WindowsMsvc2015Flavor: - result << Utils::FileName::fromLatin1("win32-msvc2015") - << Utils::FileName::fromLatin1("winphone-arm-msvc2015") - << Utils::FileName::fromLatin1("winphone-x86-msvc2015") - << Utils::FileName::fromLatin1("winrt-arm-msvc2015") - << Utils::FileName::fromLatin1("winrt-x86-msvc2015") - << Utils::FileName::fromLatin1("winrt-x64-msvc2015"); + result << "win32-msvc2015" + << "winphone-arm-msvc2015" + << "winphone-x86-msvc2015" + << "winrt-arm-msvc2015" + << "winrt-x86-msvc2015" + << "winrt-x64-msvc2015"; break; case Abi::WindowsMsvc2017Flavor: - result << Utils::FileName::fromLatin1("win32-msvc2017") - << Utils::FileName::fromLatin1("winrt-arm-msvc2017") - << Utils::FileName::fromLatin1("winrt-x86-msvc2017") - << Utils::FileName::fromLatin1("winrt-x64-msvc2017"); + result << "win32-msvc2017" + << "winrt-arm-msvc2017" + << "winrt-x86-msvc2017" + << "winrt-x64-msvc2017"; break; case Abi::WindowsMsvc2019Flavor: - result << Utils::FileName::fromLatin1("win32-msvc2019") - << Utils::FileName::fromLatin1("winrt-arm-msvc2019") - << Utils::FileName::fromLatin1("winrt-x86-msvc2019") - << Utils::FileName::fromLatin1("winrt-x64-msvc2019"); + result << "win32-msvc2019" + << "winrt-arm-msvc2019" + << "winrt-x86-msvc2019" + << "winrt-x64-msvc2019"; break; default: result.clear(); @@ -977,6 +1006,7 @@ QVariantMap MsvcToolChain::toMap() const if (!m_varsBatArg.isEmpty()) data.insert(QLatin1String(varsBatArgKeyC), m_varsBatArg); data.insert(QLatin1String(supportedAbiKeyC), m_abi.toString()); + data.insert(supportedAbisKeyC, Utils::transform<QStringList>(m_supportedAbis, &Abi::toString)); Utils::EnvironmentItem::sort(&m_environmentModifications); data.insert(QLatin1String(environModsKeyC), Utils::EnvironmentItem::toVariantList(m_environmentModifications)); @@ -989,34 +1019,40 @@ bool MsvcToolChain::fromMap(const QVariantMap &data) return false; m_vcvarsBat = QDir::fromNativeSeparators(data.value(QLatin1String(varsBatKeyC)).toString()); m_varsBatArg = data.value(QLatin1String(varsBatArgKeyC)).toString(); - addToAvailableMsvcToolchains(this); const QString abiString = data.value(QLatin1String(supportedAbiKeyC)).toString(); m_abi = Abi::fromString(abiString); + const QStringList abiList = data.value(supportedAbisKeyC).toStringList(); + m_supportedAbis.clear(); + for (const QString &a : abiList) { + Abi abi = Abi::fromString(a); + if (!abi.isValid()) + continue; + m_supportedAbis.append(abi); + } m_environmentModifications = Utils::EnvironmentItem::itemsFromVariantList( data.value(QLatin1String(environModsKeyC)).toList()); + rescanForCompiler(); initEnvModWatcher(Utils::runAsync(envModThreadPool(), &MsvcToolChain::environmentModifications, m_vcvarsBat, m_varsBatArg)); - return !m_vcvarsBat.isEmpty() && m_abi.isValid(); -} + // supported Abis were not stored in the map in previous versions of the settings. Re-detect + if (m_supportedAbis.isEmpty()) + detectInstalledAbis(); -std::unique_ptr<ToolChainConfigWidget> MsvcToolChain::createConfigurationWidget() -{ - return std::make_unique<MsvcToolChainConfigWidget>(this); -} + const bool valid = !m_vcvarsBat.isEmpty() && m_abi.isValid() && !m_supportedAbis.isEmpty(); + if (valid) + addToAvailableMsvcToolchains(this); -bool MsvcToolChain::canClone() const -{ - return true; + return valid; } -ToolChain *MsvcToolChain::clone() const +std::unique_ptr<ToolChainConfigWidget> MsvcToolChain::createConfigurationWidget() { - return new MsvcToolChain(*this); + return std::make_unique<MsvcToolChainConfigWidget>(this); } bool static hasFlagEffectOnMacros(const QString &flag) @@ -1037,7 +1073,7 @@ ToolChain::MacroInspectionRunner MsvcToolChain::createMacroInspectionRunner() co { Utils::Environment env(m_lastEnvironment); addToEnvironment(env); - std::shared_ptr<Cache<MacroInspectionReport, 64>> macroCache = m_predefinedMacrosCache; + MacrosCache macroCache = predefinedMacrosCache(); const Core::Id lang = language(); // This runner must be thread-safe! @@ -1131,7 +1167,7 @@ ToolChain::BuiltInHeaderPathsRunner MsvcToolChain::createBuiltInHeaderPathsRunne Utils::Environment env(m_lastEnvironment); addToEnvironment(env); - return [this, env](const QStringList &, const QString &) { + return [this, env](const QStringList &, const QString &, const QString &) { QMutexLocker locker(m_headerPathsMutex); if (m_headerPaths.isEmpty()) { foreach (const QString &path, @@ -1144,9 +1180,9 @@ ToolChain::BuiltInHeaderPathsRunner MsvcToolChain::createBuiltInHeaderPathsRunne } HeaderPaths MsvcToolChain::builtInHeaderPaths(const QStringList &cxxflags, - const Utils::FileName &sysRoot) const + const Utils::FilePath &sysRoot) const { - return createBuiltInHeaderPathsRunner()(cxxflags, sysRoot.toString()); + return createBuiltInHeaderPathsRunner()(cxxflags, sysRoot.toString(), ""); } void MsvcToolChain::addToEnvironment(Utils::Environment &env) const @@ -1174,44 +1210,49 @@ static QString wrappedMakeCommand(const QString &command) return wrapperPath; } -QString MsvcToolChain::makeCommand(const Utils::Environment &environment) const +FilePath MsvcToolChain::makeCommand(const Environment &environment) const { bool useJom = ProjectExplorerPlugin::projectExplorerSettings().useJom; const QString jom("jom.exe"); const QString nmake("nmake.exe"); - Utils::FileName tmp; + Utils::FilePath tmp; - QString command; + FilePath command; if (useJom) { tmp = environment.searchInPath(jom, - {Utils::FileName::fromString( + {Utils::FilePath::fromString( QCoreApplication::applicationDirPath())}); if (!tmp.isEmpty()) - command = tmp.toString(); + command = tmp; } if (command.isEmpty()) { tmp = environment.searchInPath(nmake); if (!tmp.isEmpty()) - command = tmp.toString(); + command = tmp; } if (command.isEmpty()) - command = useJom ? jom : nmake; + command = FilePath::fromString(useJom ? jom : nmake); if (environment.hasKey("VSLANG")) - return wrappedMakeCommand(command); + return FilePath::fromString(wrappedMakeCommand(command.toString())); return command; } -Utils::FileName MsvcToolChain::compilerCommand() const +Utils::FilePath MsvcToolChain::compilerCommand() const +{ + return m_compilerCommand; +} + +void MsvcToolChain::rescanForCompiler() { Utils::Environment env = Utils::Environment::systemEnvironment(); addToEnvironment(env); - Utils::FileName clexe - = env.searchInPath(QLatin1String("cl.exe"), {}, [](const Utils::FileName &name) { + m_compilerCommand + = env.searchInPath(QLatin1String("cl.exe"), {}, [](const Utils::FilePath &name) { QDir dir(QDir::cleanPath(name.toFileInfo().absolutePath() + QStringLiteral("/.."))); do { if (QFile::exists(dir.absoluteFilePath(QStringLiteral("vcvarsall.bat"))) @@ -1220,7 +1261,6 @@ Utils::FileName MsvcToolChain::compilerCommand() const } while (dir.cdUp() && !dir.isRoot()); return false; }); - return clexe; } IOutputParser *MsvcToolChain::outputParser() const @@ -1228,6 +1268,20 @@ IOutputParser *MsvcToolChain::outputParser() const return new MsvcParser; } +void MsvcToolChain::changeVcVarsCall(const QString &varsBat, const QString &varsBatArg) +{ + m_vcvarsBat = varsBat; + m_varsBatArg = varsBatArg; + + if (!varsBat.isEmpty()) { + detectInstalledAbis(); + initEnvModWatcher(Utils::runAsync(envModThreadPool(), + &ClangClToolChain::environmentModifications, + m_vcvarsBat, + m_varsBatArg)); + } +} + // -------------------------------------------------------------------------- // MsvcBasedToolChainConfigWidget: Creates a simple GUI without error label // to display name and varsBat. Derived classes should add the error label and @@ -1269,9 +1323,148 @@ void MsvcBasedToolChainConfigWidget::setFromMsvcToolChain() MsvcToolChainConfigWidget::MsvcToolChainConfigWidget(ToolChain *tc) : MsvcBasedToolChainConfigWidget(tc) + , m_varsBatPathCombo(new QComboBox(this)) + , m_varsBatArchCombo(new QComboBox(this)) + , m_varsBatArgumentsEdit(new QLineEdit(this)) + , m_abiWidget(new AbiWidget) { + m_mainLayout->removeRow(m_mainLayout->rowCount() - 1); + + QHBoxLayout *hLayout = new QHBoxLayout(); + m_varsBatPathCombo->setObjectName("varsBatCombo"); + m_varsBatPathCombo->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + m_varsBatPathCombo->setEditable(true); + for (const MsvcToolChain *tmpTc : g_availableMsvcToolchains) { + const QString nativeVcVars = QDir::toNativeSeparators(tmpTc->varsBat()); + if (!tmpTc->varsBat().isEmpty() + && m_varsBatPathCombo->findText(nativeVcVars) == -1) { + m_varsBatPathCombo->addItem(nativeVcVars); + } + } + const bool isAmd64 + = Utils::HostOsInfo::hostArchitecture() == Utils::HostOsInfo::HostArchitectureAMD64; + // TODO: Add missing values to MsvcToolChain::Platform + m_varsBatArchCombo->addItem(tr("<empty>"), isAmd64 ? MsvcToolChain::amd64 : MsvcToolChain::x86); + m_varsBatArchCombo->addItem("x86", MsvcToolChain::x86); + m_varsBatArchCombo->addItem("amd64", MsvcToolChain::amd64); + m_varsBatArchCombo->addItem("arm", MsvcToolChain::arm); + m_varsBatArchCombo->addItem("x86_amd64", MsvcToolChain::x86_amd64); + m_varsBatArchCombo->addItem("x86_arm", MsvcToolChain::x86_arm); +// m_varsBatArchCombo->addItem("x86_arm64", MsvcToolChain::x86_arm64); + m_varsBatArchCombo->addItem("amd64_x86", MsvcToolChain::amd64_x86); + m_varsBatArchCombo->addItem("amd64_arm", MsvcToolChain::amd64_arm); +// m_varsBatArchCombo->addItem("amd64_arm64", MsvcToolChain::amd64_arm64); + m_varsBatArchCombo->addItem("ia64", MsvcToolChain::ia64); + m_varsBatArchCombo->addItem("x86_ia64", MsvcToolChain::x86_ia64); + m_varsBatArgumentsEdit->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + m_varsBatArgumentsEdit->setToolTip(tr("Additional arguments for the vcvarsall.bat call")); + hLayout->addWidget(m_varsBatPathCombo); + hLayout->addWidget(m_varsBatArchCombo); + hLayout->addWidget(m_varsBatArgumentsEdit); + m_mainLayout->addRow(tr("Initialization:"), hLayout); + m_mainLayout->addRow(tr("&ABI:"), m_abiWidget); addErrorLabel(); setFromMsvcToolChain(); + + connect(m_varsBatPathCombo, &QComboBox::currentTextChanged, + this, &MsvcToolChainConfigWidget::handleVcVarsChange); + connect(m_varsBatArchCombo, &QComboBox::currentTextChanged, + this, &MsvcToolChainConfigWidget::handleVcVarsArchChange); + connect(m_varsBatArgumentsEdit, &QLineEdit::textChanged, + this, &ToolChainConfigWidget::dirty); + connect(m_abiWidget, &AbiWidget::abiChanged, this, &ToolChainConfigWidget::dirty); +} + +void MsvcToolChainConfigWidget::applyImpl() +{ + auto *tc = static_cast<MsvcToolChain *>(toolChain()); + QTC_ASSERT(tc, return ); + tc->setTargetAbi(m_abiWidget->currentAbi()); + const QString vcVars = QDir::fromNativeSeparators(m_varsBatPathCombo->currentText()); + tc->changeVcVarsCall(vcVars, vcVarsArguments()); + setFromMsvcToolChain(); +} + +void MsvcToolChainConfigWidget::discardImpl() +{ + setFromMsvcToolChain(); +} + +bool MsvcToolChainConfigWidget::isDirtyImpl() const +{ + auto msvcToolChain = static_cast<MsvcToolChain *>(toolChain()); + + return msvcToolChain->varsBat() != QDir::fromNativeSeparators(m_varsBatPathCombo->currentText()) + || msvcToolChain->varsBatArg() != vcVarsArguments() + || msvcToolChain->targetAbi() != m_abiWidget->currentAbi(); +} + +void MsvcToolChainConfigWidget::makeReadOnlyImpl() +{ + m_varsBatPathCombo->setEnabled(false); + m_varsBatArchCombo->setEnabled(false); + m_varsBatArgumentsEdit->setEnabled(false); + m_abiWidget->setEnabled(false); +} + +void MsvcToolChainConfigWidget::setFromMsvcToolChain() +{ + const auto *tc = static_cast<const MsvcToolChain *>(toolChain()); + QTC_ASSERT(tc, return ); + m_nameDisplayLabel->setText(tc->displayName()); + QString args = tc->varsBatArg(); + QStringList argList = args.split(' '); + for (int i = 0; i < argList.count(); ++i) { + if (m_varsBatArchCombo->findText(argList.at(i).trimmed()) != -1) { + const QString arch = argList.takeAt(i); + m_varsBatArchCombo->setCurrentText(arch); + args = argList.join(QLatin1Char(' ')); + break; + } + } + m_varsBatPathCombo->setCurrentText(QDir::toNativeSeparators(tc->varsBat())); + m_varsBatArgumentsEdit->setText(args); + m_abiWidget->setAbis(tc->supportedAbis(), tc->targetAbi()); +} + +void MsvcToolChainConfigWidget::handleVcVarsChange(const QString &vcVars) +{ + const QString normalizedVcVars = QDir::fromNativeSeparators(vcVars); + const auto *currentTc = static_cast<const MsvcToolChain *>(toolChain()); + QTC_ASSERT(currentTc, return ); + const MsvcToolChain::Platform platform = m_varsBatArchCombo->currentData().value<MsvcToolChain::Platform>(); + const Abi::Architecture arch = archForPlatform(platform); + const unsigned char wordWidth = wordWidthForPlatform(platform); + + for (const MsvcToolChain *tc : g_availableMsvcToolchains) { + if (tc->varsBat() == normalizedVcVars && tc->targetAbi().wordWidth() == wordWidth + && tc->targetAbi().architecture() == arch) { + m_abiWidget->setAbis(tc->supportedAbis(), tc->targetAbi()); + break; + } + } + emit dirty(); +} + +void MsvcToolChainConfigWidget::handleVcVarsArchChange(const QString &) +{ + Abi currentAbi = m_abiWidget->currentAbi(); + const MsvcToolChain::Platform platform = m_varsBatArchCombo->currentData().value<MsvcToolChain::Platform>(); + Abi newAbi(archForPlatform(platform), currentAbi.os(), currentAbi.osFlavor(), + currentAbi.binaryFormat(), wordWidthForPlatform(platform)); + if (currentAbi != newAbi) + m_abiWidget->setAbis(m_abiWidget->supportedAbis(), newAbi); + emit dirty(); +} + +QString MsvcToolChainConfigWidget::vcVarsArguments() const +{ + QString varsBatArg + = m_varsBatArchCombo->currentText() == tr("<empty>") + ? "" : m_varsBatArchCombo->currentText(); + if (!m_varsBatArgumentsEdit->text().isEmpty()) + varsBatArg += QLatin1Char(' ') + m_varsBatArgumentsEdit->text(); + return varsBatArg; } // -------------------------------------------------------------------------- @@ -1284,6 +1477,7 @@ ClangClToolChainConfigWidget::ClangClToolChainConfigWidget(ToolChain *tc) : { m_mainLayout->removeRow(m_mainLayout->rowCount() - 1); + m_varsBatDisplayCombo->setObjectName("varsBatCombo"); m_varsBatDisplayCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); m_mainLayout->addRow(tr("Initialization:"), m_varsBatDisplayCombo); @@ -1326,7 +1520,7 @@ void ClangClToolChainConfigWidget::setFromClangClToolChain() if (clangClToolChain->isAutoDetected()) m_llvmDirLabel->setText(QDir::toNativeSeparators(clangClToolChain->clangPath())); else - m_compilerCommand->setFileName(Utils::FileName::fromString(clangClToolChain->clangPath())); + m_compilerCommand->setFileName(Utils::FilePath::fromString(clangClToolChain->clangPath())); } static const MsvcToolChain *findMsvcToolChain(unsigned char wordWidth, Abi::OSFlavor flavor) @@ -1418,7 +1612,9 @@ static QList<ToolChain *> detectClangClToolChainInPath(const QString &clangClPat clangClPath); })); if (!tc) { - tc = new ClangClToolChain(name, clangClPath, language, ToolChain::AutoDetection); + tc = new ClangClToolChain(name, clangClPath); + tc->setDetection(ToolChain::AutoDetection); + tc->setLanguage(language); tc->resetMsvcToolChain(toolChain); } res << tc; @@ -1433,7 +1629,7 @@ static QString compilerFromPath(const QString &path) void ClangClToolChainConfigWidget::applyImpl() { - Utils::FileName clangClPath = m_compilerCommand->fileName(); + Utils::FilePath clangClPath = m_compilerCommand->fileName(); auto clangClToolChain = static_cast<ClangClToolChain *>(toolChain()); clangClToolChain->setClangPath(clangClPath.toString()); @@ -1479,10 +1675,8 @@ void ClangClToolChainConfigWidget::makeReadOnlyImpl() // -------------------------------------------------------------------------- ClangClToolChain::ClangClToolChain(const QString &name, - const QString &clangPath, - Core::Id language, - Detection d) - : MsvcToolChain(Constants::CLANG_CL_TOOLCHAIN_TYPEID, name, Abi(), "", "", language, d) + const QString &clangPath) + : MsvcToolChain(Constants::CLANG_CL_TOOLCHAIN_TYPEID, name, Abi(), "", "") , m_clangPath(clangPath) {} @@ -1503,9 +1697,9 @@ void ClangClToolChain::addToEnvironment(Utils::Environment &env) const env.prependOrSetPath(path.canonicalPath()); } -Utils::FileName ClangClToolChain::compilerCommand() const +Utils::FilePath ClangClToolChain::compilerCommand() const { - return Utils::FileName::fromString(m_clangPath); + return Utils::FilePath::fromString(m_clangPath); } QString ClangClToolChain::typeDisplayName() const @@ -1513,11 +1707,10 @@ QString ClangClToolChain::typeDisplayName() const return QCoreApplication::translate("ProjectExplorer::ClangToolChainFactory", "Clang"); } -QList<Utils::FileName> ClangClToolChain::suggestedMkspecList() const +QStringList ClangClToolChain::suggestedMkspecList() const { - const QString mkspec = QLatin1String("win32-clang-") + Abi::toString(targetAbi().osFlavor()); - return QList<Utils::FileName>{Utils::FileName::fromString(mkspec), - Utils::FileName::fromString("win32-clang-msvc")}; + const QString mkspec = "win32-clang-" + Abi::toString(targetAbi().osFlavor()); + return {mkspec, "win32-clang-msvc"}; } IOutputParser *ClangClToolChain::outputParser() const @@ -1525,11 +1718,6 @@ IOutputParser *ClangClToolChain::outputParser() const return new ClangClParser; } -ToolChain *ClangClToolChain::clone() const -{ - return new ClangClToolChain(*this); -} - static inline QString llvmDirKey() { return QStringLiteral("ProjectExplorer.ClangClToolChain.LlvmDir"); @@ -1563,18 +1751,12 @@ void ClangClToolChain::resetMsvcToolChain(const MsvcToolChain *base) { if (!base) { m_abi = Abi(); - m_vcvarsBat.clear(); - setVarsBatArg(""); + changeVcVarsCall(""); return; } - m_abi = base->targetAbi(); - m_vcvarsBat = base->varsBat(); - setVarsBatArg(base->varsBatArg()); - initEnvModWatcher(Utils::runAsync(envModThreadPool(), - &ClangClToolChain::environmentModifications, - m_vcvarsBat, - base->varsBatArg())); + m_abi = base->targetAbi(); + changeVcVarsCall(base->varsBat(), base->varsBatArg()); } bool ClangClToolChain::operator==(const ToolChain &other) const @@ -1618,12 +1800,6 @@ Utils::LanguageVersion ClangClToolChain::msvcLanguageVersion(const QStringList & return MsvcToolChain::msvcLanguageVersion(cxxflags, language, macros); } -void ClangClToolChain::toolChainUpdated() -{ - MsvcToolChain::toolChainUpdated(); - ToolChain::toolChainUpdated(); -} - ClangClToolChain::BuiltInHeaderPathsRunner ClangClToolChain::createBuiltInHeaderPathsRunner() const { { @@ -1641,11 +1817,9 @@ ClangClToolChain::BuiltInHeaderPathsRunner ClangClToolChain::createBuiltInHeader MsvcToolChainFactory::MsvcToolChainFactory() { setDisplayName(tr("MSVC")); -} - -QSet<Core::Id> MsvcToolChainFactory::supportedLanguages() const -{ - return {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}; + setSupportedToolChainType(Constants::MSVC_TOOLCHAIN_TYPEID); + setSupportedLanguages({Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}); + setToolchainConstructor([] { return new MsvcToolChain; }); } QString MsvcToolChainFactory::vcVarsBatFor(const QString &basePath, @@ -1668,8 +1842,7 @@ static QList<ToolChain *> findOrCreateToolChain(const QList<ToolChain *> &alread const QString &name, const Abi &abi, const QString &varsBat, - const QString &varsBatArg, - ToolChain::Detection d = ToolChain::ManualDetection) + const QString &varsBatArg) { QList<ToolChain *> res; for (auto language : {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}) { @@ -1683,8 +1856,10 @@ static QList<ToolChain *> findOrCreateToolChain(const QList<ToolChain *> &alread auto mtc = static_cast<MsvcToolChain *>(tc); return mtc->varsBat() == varsBat && mtc->varsBatArg() == varsBatArg; }); - if (!tc) - tc = new MsvcToolChain(name, abi, varsBat, varsBatArg, language, d); + if (!tc) { + tc = new MsvcToolChain(name, abi, varsBat, varsBatArg); + tc->setLanguage(language); + } res << tc; } return res; @@ -1719,12 +1894,13 @@ static void detectCppBuildTools2015(QList<ToolChain *> *list) e.format, e.wordSize); for (auto language : {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}) { - list->append(new MsvcToolChain(name + QLatin1String(e.postFix), - abi, - vcVarsBat, - QLatin1String(e.varsBatArg), - language, - ToolChain::AutoDetection)); + auto tc = new MsvcToolChain(name + QLatin1String(e.postFix), + abi, + vcVarsBat, + QLatin1String(e.varsBatArg)); + tc->setDetection(ToolChain::AutoDetection); + tc->setLanguage(language); + list->append(tc); } } } @@ -1770,8 +1946,7 @@ QList<ToolChain *> MsvcToolChainFactory::autoDetect(const QList<ToolChain *> &al platform.first, sdkKey), fi.absoluteFilePath(), - "/" + platform.second, - ToolChain::AutoDetection)); + "/" + platform.second)); } // Make sure the default is front. if (folder == defaultSdkPath) @@ -1806,23 +1981,28 @@ QList<ToolChain *> MsvcToolChainFactory::autoDetect(const QList<ToolChain *> &al generateDisplayName(i.vsName, MsvcToolChain::VS, platform), findAbiOfMsvc(MsvcToolChain::VS, platform, i.vsName), i.vcVarsAll, - platformName(platform), - ToolChain::AutoDetection)); + platformName(platform))); } } } detectCppBuildTools2015(&results); + for (ToolChain *tc : results) + tc->setDetection(ToolChain::AutoDetection); + return results; } ClangClToolChainFactory::ClangClToolChainFactory() { setDisplayName(tr("clang-cl")); + setSupportedLanguages({Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}); + setSupportedToolChainType(Constants::CLANG_CL_TOOLCHAIN_TYPEID); + setToolchainConstructor([] { return new ClangClToolChain; }); } -bool ClangClToolChainFactory::canCreate() +bool ClangClToolChainFactory::canCreate() const { return !g_availableMsvcToolchains.isEmpty(); } @@ -1842,9 +2022,9 @@ QList<ToolChain *> ClangClToolChainFactory::autoDetect(const QList<ToolChain *> QString qtCreatorsClang = Core::ICore::clangExecutable(CLANG_BINDIR); if (!qtCreatorsClang.isEmpty()) { - qtCreatorsClang = Utils::FileName::fromString(qtCreatorsClang) + qtCreatorsClang = Utils::FilePath::fromString(qtCreatorsClang) .parentDir() - .appendPath("clang-cl.exe") + .pathAppended("clang-cl.exe") .toString(); results.append(detectClangClToolChainInPath(qtCreatorsClang, alreadyKnown, "", true)); known.append(results); @@ -1861,16 +2041,16 @@ QList<ToolChain *> ClangClToolChainFactory::autoDetect(const QList<ToolChain *> } const Utils::Environment systemEnvironment = Utils::Environment::systemEnvironment(); - const Utils::FileName clangClPath = systemEnvironment.searchInPath("clang-cl"); + const Utils::FilePath clangClPath = systemEnvironment.searchInPath("clang-cl"); if (!clangClPath.isEmpty()) results.append(detectClangClToolChainInPath(clangClPath.toString(), known, "")); return results; } -ToolChain *ClangClToolChainFactory::create(Core::Id l) +ToolChain *ClangClToolChainFactory::create() { - return new ClangClToolChain("clang-cl", "", l, ToolChain::ManualDetection); + return new ClangClToolChain("clang-cl", ""); } bool MsvcToolChain::operator==(const ToolChain &other) const @@ -1926,7 +2106,7 @@ Utils::optional<QString> MsvcToolChain::generateEnvironmentSettings(const Utils: runEnv.unset(QLatin1String("ORIGINALPATH")); run.setEnvironment(runEnv.toStringList()); run.setTimeoutS(30); - Utils::FileName cmdPath = Utils::FileName::fromUserInput( + Utils::FilePath cmdPath = Utils::FilePath::fromUserInput( QString::fromLocal8Bit(qgetenv("COMSPEC"))); if (cmdPath.isEmpty()) cmdPath = env.searchInPath(QLatin1String("cmd.exe")); @@ -1984,36 +2164,15 @@ Utils::optional<QString> MsvcToolChain::generateEnvironmentSettings(const Utils: return Utils::nullopt; } -bool MsvcToolChainFactory::canRestore(const QVariantMap &data) -{ - const Core::Id id = typeIdFromMap(data); - return id == Constants::MSVC_TOOLCHAIN_TYPEID; -} - -template<class ToolChainType> -ToolChainType *readFromMap(const QVariantMap &data) +bool MsvcToolChainFactory::canCreate() const { - auto result = new ToolChainType; - if (result->fromMap(data)) - return result; - delete result; - return nullptr; -} - -ToolChain *MsvcToolChainFactory::restore(const QVariantMap &data) -{ - return readFromMap<MsvcToolChain>(data); -} - -bool ClangClToolChainFactory::canRestore(const QVariantMap &data) -{ - const Core::Id id = typeIdFromMap(data); - return id == Constants::CLANG_CL_TOOLCHAIN_TYPEID; + return !g_availableMsvcToolchains.isEmpty(); } -ToolChain *ClangClToolChainFactory::restore(const QVariantMap &data) +ToolChain *MsvcToolChainFactory::create() { - return readFromMap<ClangClToolChain>(data); + return new MsvcToolChain("Microsoft Visual C++ Compiler", + Abi::hostAbi(), g_availableMsvcToolchains.first()->varsBat(), ""); } MsvcToolChain::WarningFlagAdder::WarningFlagAdder(const QString &flag, WarningFlags &flags) @@ -2056,3 +2215,5 @@ bool MsvcToolChain::WarningFlagAdder::triggered() const } // namespace Internal } // namespace ProjectExplorer + +Q_DECLARE_METATYPE(ProjectExplorer::Internal::MsvcToolChain::Platform) diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h index 10caaadfee..bc0adb00ba 100644 --- a/src/plugins/projectexplorer/msvctoolchain.h +++ b/src/plugins/projectexplorer/msvctoolchain.h @@ -26,8 +26,8 @@ #pragma once #include "abi.h" +#include "abiwidget.h" #include "toolchain.h" -#include "toolchaincache.h" #include "toolchainconfigwidget.h" #include <QFutureWatcher> @@ -59,20 +59,19 @@ public: explicit MsvcToolChain(const QString &name, const Abi &abi, const QString &varsBat, - const QString &varsBatArg, - Core::Id l, - Detection d = ManualDetection); - MsvcToolChain(const MsvcToolChain &other); + const QString &varsBatArg); MsvcToolChain(); ~MsvcToolChain() override; Abi targetAbi() const override; + Abis supportedAbis() const override; + void setTargetAbi(const Abi &abi); bool isValid() const override; QString originalTargetTriple() const override; - Utils::FileNameList suggestedMkspecList() const override; + QStringList suggestedMkspecList() const override; QString typeDisplayName() const override; @@ -81,34 +80,33 @@ public: std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override; - bool canClone() const override; - ToolChain *clone() const override; - MacroInspectionRunner createMacroInspectionRunner() const override; Macros predefinedMacros(const QStringList &cxxflags) const override; Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; WarningFlags warningFlags(const QStringList &cflags) const override; BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner() const override; HeaderPaths builtInHeaderPaths(const QStringList &cxxflags, - const Utils::FileName &sysRoot) const override; + const Utils::FilePath &sysRoot) const override; void addToEnvironment(Utils::Environment &env) const override; - QString makeCommand(const Utils::Environment &environment) const override; - Utils::FileName compilerCommand() const override; + Utils::FilePath makeCommand(const Utils::Environment &environment) const override; + Utils::FilePath compilerCommand() const override; IOutputParser *outputParser() const override; QString varsBatArg() const { return m_varsBatArg; } QString varsBat() const { return m_vcvarsBat; } void setVarsBatArg(const QString &varsBA) { m_varsBatArg = varsBA; } + void changeVcVarsCall(const QString &varsBat, const QString &varsBatArgs = QString()); bool operator==(const ToolChain &) const override; + bool isJobCountSupported() const override { return false; } + static void cancelMsvcToolChainDetection(); static Utils::optional<QString> generateEnvironmentSettings(const Utils::Environment &env, const QString &batchFile, const QString &batchArgs, QMap<QString, QString> &envPairs); - protected: class WarningFlagAdder { @@ -128,13 +126,10 @@ protected: const QString &name, const Abi &abi, const QString &varsBat, - const QString &varsBatArg, - Core::Id l, - Detection d); + const QString &varsBatArg); explicit MsvcToolChain(Core::Id typeId); static void inferWarningsForLevel(int warningLevel, WarningFlags &flags); - void toolChainUpdated() override; Utils::Environment readEnvironmentSetting(const Utils::Environment &env) const; // Function must be thread-safe! @@ -160,37 +155,37 @@ protected: private: void updateEnvironmentModifications(QList<Utils::EnvironmentItem> modifications); + void rescanForCompiler(); + void detectInstalledAbis(); mutable QList<Utils::EnvironmentItem> m_environmentModifications; mutable QFutureWatcher<GenerateEnvResult> m_envModWatcher; - Utils::FileName m_debuggerCommand; - - mutable std::shared_ptr<Cache<MacroInspectionReport, 64>> m_predefinedMacrosCache; - mutable Utils::Environment m_lastEnvironment; // Last checked 'incoming' environment. mutable Utils::Environment m_resultEnvironment; // Resulting environment for VC + Utils::FilePath m_compilerCommand; + protected: Abi m_abi; + Abis m_supportedAbis; QString m_vcvarsBat; QString m_varsBatArg; // Argument }; -class ClangClToolChain : public MsvcToolChain +class PROJECTEXPLORER_EXPORT ClangClToolChain : public MsvcToolChain { public: - ClangClToolChain(const QString &name, const QString &llvmDir, Core::Id language, Detection d); + ClangClToolChain(const QString &name, const QString &llvmDir); ClangClToolChain(); bool isValid() const override; QString typeDisplayName() const override; - QList<Utils::FileName> suggestedMkspecList() const override; + QStringList suggestedMkspecList() const override; void addToEnvironment(Utils::Environment &env) const override; - Utils::FileName compilerCommand() const override; + Utils::FilePath compilerCommand() const override; IOutputParser *outputParser() const override; - ToolChain *clone() const override; QVariantMap toMap() const override; bool fromMap(const QVariantMap &data) override; std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override; @@ -210,9 +205,6 @@ public: bool operator==(const ToolChain &) const override; private: - void toolChainUpdated() override; - -private: QString m_clangPath; }; @@ -226,19 +218,18 @@ class MsvcToolChainFactory : public ToolChainFactory public: MsvcToolChainFactory(); - QSet<Core::Id> supportedLanguages() const override; QList<ToolChain *> autoDetect(const QList<ToolChain *> &alreadyKnown) override; - bool canRestore(const QVariantMap &data) override; - ToolChain *restore(const QVariantMap &data) override; + bool canCreate() const override; + ToolChain *create() override; static QString vcVarsBatFor(const QString &basePath, MsvcToolChain::Platform platform, const QVersionNumber &v); }; -class ClangClToolChainFactory : public MsvcToolChainFactory +class ClangClToolChainFactory : public ToolChainFactory { Q_OBJECT @@ -247,11 +238,8 @@ public: QList<ToolChain *> autoDetect(const QList<ToolChain *> &alreadyKnown) override; - bool canRestore(const QVariantMap &data) override; - ToolChain *restore(const QVariantMap &data) override; - - bool canCreate() override; - ToolChain *create(Core::Id l) override; + bool canCreate() const override; + ToolChain *create() override; }; // -------------------------------------------------------------------------- @@ -288,6 +276,24 @@ class MsvcToolChainConfigWidget : public MsvcBasedToolChainConfigWidget public: explicit MsvcToolChainConfigWidget(ToolChain *); + +private: + void applyImpl() override; + void discardImpl() override; + bool isDirtyImpl() const override; + void makeReadOnlyImpl() override; + + void setFromMsvcToolChain(); + + void handleVcVarsChange(const QString &vcVars); + void handleVcVarsArchChange(const QString &arch); + + QString vcVarsArguments() const; + + QComboBox *m_varsBatPathCombo; + QComboBox *m_varsBatArchCombo; + QLineEdit *m_varsBatArgumentsEdit; + AbiWidget *m_abiWidget; }; // -------------------------------------------------------------------------- @@ -304,6 +310,7 @@ public: protected: void applyImpl() override; void discardImpl() override; + bool isDirtyImpl() const override { return false; } void makeReadOnlyImpl() override; private: diff --git a/src/plugins/projectexplorer/namedwidget.h b/src/plugins/projectexplorer/namedwidget.h index 26c9550f7e..870941c73b 100644 --- a/src/plugins/projectexplorer/namedwidget.h +++ b/src/plugins/projectexplorer/namedwidget.h @@ -39,13 +39,11 @@ public: explicit NamedWidget(QWidget *parent = nullptr); QString displayName() const; + void setDisplayName(const QString &displayName); signals: void displayNameChanged(const QString &); -protected: - void setDisplayName(const QString &displayName); - private: QString m_displayName; }; diff --git a/src/plugins/projectexplorer/osparser.cpp b/src/plugins/projectexplorer/osparser.cpp index 9190f45d73..ecbef759ef 100644 --- a/src/plugins/projectexplorer/osparser.cpp +++ b/src/plugins/projectexplorer/osparser.cpp @@ -41,7 +41,7 @@ void OsParser::stdError(const QString &line) if (Utils::HostOsInfo::isLinuxHost()) { const QString trimmed = line.trimmed(); if (trimmed.contains(QLatin1String(": error while loading shared libraries:"))) { - emit addTask(Task(Task::Error, trimmed, Utils::FileName(), -1, + emit addTask(Task(Task::Error, trimmed, Utils::FilePath(), -1, Constants::TASK_CATEGORY_COMPILE)); } } @@ -55,7 +55,7 @@ void OsParser::stdOutput(const QString &line) if (trimmed == QLatin1String("The process cannot access the file because it is being used by another process.")) { emit addTask(Task(Task::Error, tr("The process cannot access the file because it is being used by another process.\n" "Please close all running instances of your application before starting a build."), - Utils::FileName(), -1, Constants::TASK_CATEGORY_COMPILE)); + Utils::FilePath(), -1, Constants::TASK_CATEGORY_COMPILE)); m_hasFatalError = true; } } diff --git a/src/plugins/projectexplorer/outputparser_test.cpp b/src/plugins/projectexplorer/outputparser_test.cpp index cb2a9772c3..765a087650 100644 --- a/src/plugins/projectexplorer/outputparser_test.cpp +++ b/src/plugins/projectexplorer/outputparser_test.cpp @@ -32,7 +32,7 @@ namespace ProjectExplorer { -static inline QByteArray msgFileComparisonFail(const Utils::FileName &f1, const Utils::FileName &f2) +static inline QByteArray msgFileComparisonFail(const Utils::FilePath &f1, const Utils::FilePath &f2) { const QString result = '"' + f1.toUserOutput() + "\" != \"" + f2.toUserOutput() + '"'; return result.toLocal8Bit(); @@ -43,7 +43,7 @@ OutputParserTester::OutputParserTester() = default; // test functions: void OutputParserTester::testParsing(const QString &lines, Channel inputChannel, - QList<Task> tasks, + Tasks tasks, const QString &childStdOutLines, const QString &childStdErrLines, const QString &outputLines) diff --git a/src/plugins/projectexplorer/outputparser_test.h b/src/plugins/projectexplorer/outputparser_test.h index 7836331632..43920d0ebf 100644 --- a/src/plugins/projectexplorer/outputparser_test.h +++ b/src/plugins/projectexplorer/outputparser_test.h @@ -50,7 +50,7 @@ public: // test functions: void testParsing(const QString &lines, Channel inputChannel, - QList<Task> tasks, + Tasks tasks, const QString &childStdOutLines, const QString &childStdErrLines, const QString &outputLines); @@ -76,7 +76,7 @@ private: QString m_receivedStdErrChildLine; QString m_receivedStdOutChildLine; - QList<Task> m_receivedTasks; + Tasks m_receivedTasks; QString m_receivedOutput; friend class TestTerminator; diff --git a/src/plugins/projectexplorer/parseissuesdialog.cpp b/src/plugins/projectexplorer/parseissuesdialog.cpp new file mode 100644 index 0000000000..7b0d3758e6 --- /dev/null +++ b/src/plugins/projectexplorer/parseissuesdialog.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "parseissuesdialog.h" + +#include "ioutputparser.h" +#include "kitinformation.h" +#include "kitchooser.h" +#include "kitmanager.h" +#include "projectexplorerconstants.h" +#include "taskhub.h" + +#include <coreplugin/progressmanager/progressmanager.h> +#include <utils/runextensions.h> + +#include <QButtonGroup> +#include <QCheckBox> +#include <QDialogButtonBox> +#include <QFile> +#include <QFileDialog> +#include <QGroupBox> +#include <QLabel> +#include <QMessageBox> +#include <QPlainTextEdit> +#include <QPushButton> +#include <QVBoxLayout> + +#include <memory> + +namespace ProjectExplorer { +namespace Internal { + +class ParseIssuesDialog::Private +{ +public: + QPlainTextEdit compileOutputEdit; + QCheckBox stderrCheckBox; + QCheckBox clearTasksCheckBox; + KitChooser kitChooser; +}; + +ParseIssuesDialog::ParseIssuesDialog(QWidget *parent) : QDialog(parent), d(new Private) +{ + setWindowTitle(tr("Parse Build Output")); + + d->stderrCheckBox.setText(tr("Output went to stderr")); + d->stderrCheckBox.setChecked(true); + + d->clearTasksCheckBox.setText(tr("Clear existing tasks")); + d->clearTasksCheckBox.setChecked(true); + + const auto loadFileButton = new QPushButton(tr("Load from File...")); + connect(loadFileButton, &QPushButton::clicked, this, [this] { + const QString filePath = QFileDialog::getOpenFileName(this, tr("Choose File")); + if (filePath.isEmpty()) + return; + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly)) { + QMessageBox::critical(this, tr("Could Not Open File"), + tr("Could not open file: \"%1\": %2") + .arg(filePath, file.errorString())); + return; + } + d->compileOutputEdit.setPlainText(QString::fromLocal8Bit(file.readAll())); + }); + + d->kitChooser.populate(); + if (!d->kitChooser.hasStartupKit()) { + for (const Kit * const k : KitManager::kits()) { + if (DeviceTypeKitAspect::deviceTypeId(k) == Constants::DESKTOP_DEVICE_TYPE) { + d->kitChooser.setCurrentKitId(k->id()); + break; + } + } + } + + const auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + buttonBox->button(QDialogButtonBox::Ok)->setEnabled(d->kitChooser.currentKit()); + + const auto layout = new QVBoxLayout(this); + const auto outputGroupBox = new QGroupBox(tr("Build Output")); + layout->addWidget(outputGroupBox); + const auto outputLayout = new QHBoxLayout(outputGroupBox); + outputLayout->addWidget(&d->compileOutputEdit); + const auto buttonsWidget = new QWidget; + const auto outputButtonsLayout = new QVBoxLayout(buttonsWidget); + outputLayout->addWidget(buttonsWidget); + outputButtonsLayout->addWidget(loadFileButton); + outputButtonsLayout->addWidget(&d->stderrCheckBox); + outputButtonsLayout->addStretch(1); + + // TODO: Only very few parsers are available from a Kit (basically just the Toolchain one). + // If we introduced factories for IOutputParsers, we could offer the user + // to combine arbitrary parsers here. + const auto parserGroupBox = new QGroupBox(tr("Parsing Options")); + layout->addWidget(parserGroupBox); + const auto parserLayout = new QVBoxLayout(parserGroupBox); + const auto kitChooserWidget = new QWidget; + const auto kitChooserLayout = new QHBoxLayout(kitChooserWidget); + kitChooserLayout->setContentsMargins(0, 0, 0, 0); + kitChooserLayout->addWidget(new QLabel(tr("Use parsers from kit:"))); + kitChooserLayout->addWidget(&d->kitChooser); + parserLayout->addWidget(kitChooserWidget); + parserLayout->addWidget(&d->clearTasksCheckBox); + + layout->addWidget(buttonBox); +} + +ParseIssuesDialog::~ParseIssuesDialog() +{ + delete d; +} + +static void parse(QFutureInterface<void> &future, const QString &output, + const std::unique_ptr<IOutputParser> &parser, bool isStderr) +{ + const QStringList lines = output.split('\n'); + future.setProgressRange(0, lines.count()); + const auto parserFunc = isStderr ? &IOutputParser::stdError : &IOutputParser::stdOutput; + for (const QString &line : lines) { + (parser.get()->*parserFunc)(line); + future.setProgressValue(future.progressValue() + 1); + if (future.isCanceled()) + return; + } +} + +void ParseIssuesDialog::accept() +{ + std::unique_ptr<IOutputParser> parser(d->kitChooser.currentKit()->createOutputParser()); + if (!parser) { + QMessageBox::critical(this, tr("Cannot Parse"), tr("Cannot parse: The chosen kit does " + "not provide an output parser.")); + return; + } + if (d->clearTasksCheckBox.isChecked()) + TaskHub::clearTasks(); + connect(parser.get(), &IOutputParser::addTask, [](const Task &t) { TaskHub::addTask(t); }); + const QFuture<void> f = Utils::runAsync(&parse, d->compileOutputEdit.toPlainText(), + std::move(parser), d->stderrCheckBox.isChecked()); + Core::ProgressManager::addTask(f, tr("Parsing build output"), + "ProgressExplorer.ParseExternalBuildOutput"); + QDialog::accept(); +} + +} // namespace Internal +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/parseissuesdialog.h b/src/plugins/projectexplorer/parseissuesdialog.h new file mode 100644 index 0000000000..555727c3b9 --- /dev/null +++ b/src/plugins/projectexplorer/parseissuesdialog.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QDialog> + +namespace ProjectExplorer { +namespace Internal { + +class ParseIssuesDialog : public QDialog +{ + Q_OBJECT +public: + ParseIssuesDialog(QWidget *parent = nullptr); + ~ParseIssuesDialog() override; + +private: + void accept() override; + + class Private; + Private * const d; +}; + +} // namespace Internal +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/processparameters.cpp b/src/plugins/projectexplorer/processparameters.cpp index db56c57cdb..f535cc120e 100644 --- a/src/plugins/projectexplorer/processparameters.cpp +++ b/src/plugins/projectexplorer/processparameters.cpp @@ -44,7 +44,9 @@ \sa ProjectExplorer::AbstractProcessStep */ -using namespace ProjectExplorer; +using namespace Utils; + +namespace ProjectExplorer { ProcessParameters::ProcessParameters() : m_macroExpander(nullptr), @@ -52,11 +54,19 @@ ProcessParameters::ProcessParameters() : { } +void ProcessParameters::setCommandLine(const CommandLine &cmdLine) +{ + m_command = cmdLine.executable(); + m_arguments = cmdLine.arguments(); + m_effectiveCommand.clear(); + m_effectiveArguments.clear(); +} + /*! Sets the executable to run. */ -void ProcessParameters::setCommand(const QString &cmd) +void ProcessParameters::setCommand(const Utils::FilePath &cmd) { m_command = cmd; m_effectiveCommand.clear(); @@ -78,7 +88,7 @@ void ProcessParameters::setArguments(const QString &arguments) Should be called from init(). */ -void ProcessParameters::setWorkingDirectory(const QString &workingDirectory) +void ProcessParameters::setWorkingDirectory(const FilePath &workingDirectory) { m_workingDirectory = workingDirectory; m_effectiveWorkingDirectory.clear(); @@ -103,13 +113,14 @@ void ProcessParameters::setWorkingDirectory(const QString &workingDirectory) Gets the fully expanded working directory. */ -QString ProcessParameters::effectiveWorkingDirectory() const +FilePath ProcessParameters::effectiveWorkingDirectory() const { if (m_effectiveWorkingDirectory.isEmpty()) { - QString wds = m_workingDirectory; + QString wds = m_workingDirectory.toString(); if (m_macroExpander) wds = m_macroExpander->expand(wds); - m_effectiveWorkingDirectory = QDir::cleanPath(m_environment.expandVariables(wds)); + m_effectiveWorkingDirectory + = FilePath::fromString(QDir::cleanPath(m_environment.expandVariables(wds))); } return m_effectiveWorkingDirectory; } @@ -118,14 +129,15 @@ QString ProcessParameters::effectiveWorkingDirectory() const Gets the fully expanded command name to run. */ -QString ProcessParameters::effectiveCommand() const +FilePath ProcessParameters::effectiveCommand() const { if (m_effectiveCommand.isEmpty()) { - QString cmd = m_command; + FilePath cmd = m_command; if (m_macroExpander) cmd = m_macroExpander->expand(cmd); m_effectiveCommand = - m_environment.searchInPath(cmd, {Utils::FileName::fromString(effectiveWorkingDirectory())}).toString(); + m_environment.searchInPath(cmd.toString(), + {effectiveWorkingDirectory()}); m_commandMissing = m_effectiveCommand.isEmpty(); if (m_commandMissing) m_effectiveCommand = cmd; @@ -155,16 +167,16 @@ QString ProcessParameters::effectiveArguments() const QString ProcessParameters::prettyCommand() const { - QString cmd = m_command; + QString cmd = m_command.toString(); if (m_macroExpander) cmd = m_macroExpander->expand(cmd); - return Utils::FileName::fromString(cmd).fileName(); + return Utils::FilePath::fromString(cmd).fileName(); } QString ProcessParameters::prettyArguments() const { QString margs = effectiveArguments(); - QString workDir = effectiveWorkingDirectory(); + QString workDir = effectiveWorkingDirectory().toString(); Utils::QtcProcess::SplitError err; Utils::QtcProcess::Arguments args = Utils::QtcProcess::prepareArgs(margs, &err, Utils::HostOsInfo::hostOs(), &m_environment, &workDir); @@ -187,7 +199,7 @@ QString ProcessParameters::summaryInWorkdir(const QString &displayName) const .arg(displayName, Utils::QtcProcess::quoteArg(prettyCommand()), prettyArguments(), - QDir::toNativeSeparators(effectiveWorkingDirectory())); + QDir::toNativeSeparators(effectiveWorkingDirectory().toString())); } void ProcessParameters::resolveAll() @@ -196,3 +208,5 @@ void ProcessParameters::resolveAll() effectiveArguments(); effectiveWorkingDirectory(); } + +} // ProcessExplorer diff --git a/src/plugins/projectexplorer/processparameters.h b/src/plugins/projectexplorer/processparameters.h index fd43e03bf7..c87ede7d5b 100644 --- a/src/plugins/projectexplorer/processparameters.h +++ b/src/plugins/projectexplorer/processparameters.h @@ -28,8 +28,12 @@ #include "projectexplorer_export.h" #include <utils/environment.h> +#include <utils/fileutils.h> -namespace Utils { class MacroExpander; } +namespace Utils { +class CommandLine; +class MacroExpander; +} // Utils namespace ProjectExplorer { @@ -39,14 +43,16 @@ class PROJECTEXPLORER_EXPORT ProcessParameters public: ProcessParameters(); - void setCommand(const QString &cmd); - QString command() const { return m_command; } + void setCommandLine(const Utils::CommandLine &cmdLine); + + void setCommand(const Utils::FilePath &cmd); + Utils::FilePath command() const { return m_command; } void setArguments(const QString &arguments); QString arguments() const { return m_arguments; } - void setWorkingDirectory(const QString &workingDirectory); - QString workingDirectory() const { return m_workingDirectory; } + void setWorkingDirectory(const Utils::FilePath &workingDirectory); + Utils::FilePath workingDirectory() const { return m_workingDirectory; } void setEnvironment(const Utils::Environment &env) { m_environment = env; } Utils::Environment environment() const { return m_environment; } @@ -55,9 +61,9 @@ public: Utils::MacroExpander *macroExpander() const { return m_macroExpander; } /// Get the fully expanded working directory: - QString effectiveWorkingDirectory() const; + Utils::FilePath effectiveWorkingDirectory() const; /// Get the fully expanded command name to run: - QString effectiveCommand() const; + Utils::FilePath effectiveCommand() const; /// Get the fully expanded arguments to use: QString effectiveArguments() const; @@ -71,14 +77,14 @@ public: void resolveAll(); private: - QString m_workingDirectory; - QString m_command; + Utils::FilePath m_workingDirectory; + Utils::FilePath m_command; QString m_arguments; Utils::Environment m_environment; Utils::MacroExpander *m_macroExpander; - mutable QString m_effectiveWorkingDirectory; - mutable QString m_effectiveCommand; + mutable Utils::FilePath m_effectiveWorkingDirectory; + mutable Utils::FilePath m_effectiveCommand; mutable QString m_effectiveArguments; mutable bool m_commandMissing; }; diff --git a/src/plugins/projectexplorer/processstep.cpp b/src/plugins/projectexplorer/processstep.cpp index c6c6057f4b..231cb831bb 100644 --- a/src/plugins/projectexplorer/processstep.cpp +++ b/src/plugins/projectexplorer/processstep.cpp @@ -24,114 +24,106 @@ ****************************************************************************/ #include "processstep.h" -#include "buildstep.h" #include "buildconfiguration.h" +#include "buildstep.h" +#include "kit.h" #include "processparameters.h" #include "projectexplorerconstants.h" #include "target.h" -#include "kit.h" #include <coreplugin/variablechooser.h> #include <utils/macroexpander.h> -#include <QDebug> +#include <QFormLayout> -using namespace ProjectExplorer; -using namespace ProjectExplorer::Internal; +namespace ProjectExplorer { -namespace { const char PROCESS_STEP_ID[] = "ProjectExplorer.ProcessStep"; const char PROCESS_COMMAND_KEY[] = "ProjectExplorer.ProcessStep.Command"; const char PROCESS_WORKINGDIRECTORY_KEY[] = "ProjectExplorer.ProcessStep.WorkingDirectory"; const char PROCESS_ARGUMENTS_KEY[] = "ProjectExplorer.ProcessStep.Arguments"; -} ProcessStep::ProcessStep(BuildStepList *bsl) : AbstractProcessStep(bsl, PROCESS_STEP_ID) { //: Default ProcessStep display name setDefaultDisplayName(tr("Custom Process Step")); - if (m_workingDirectory.isEmpty()) - m_workingDirectory = Constants::DEFAULT_WORKING_DIR; + + m_command = addAspect<BaseStringAspect>(); + m_command->setSettingsKey(PROCESS_COMMAND_KEY); + m_command->setDisplayStyle(BaseStringAspect::PathChooserDisplay); + m_command->setLabelText(tr("Command:")); + m_command->setExpectedKind(Utils::PathChooser::Command); + m_command->setHistoryCompleter("PE.ProcessStepCommand.History"); + + m_arguments = addAspect<BaseStringAspect>(); + m_arguments->setSettingsKey(PROCESS_ARGUMENTS_KEY); + m_arguments->setDisplayStyle(BaseStringAspect::LineEditDisplay); + m_arguments->setLabelText(tr("Arguments:")); + + m_workingDirectory = addAspect<BaseStringAspect>(); + m_workingDirectory->setSettingsKey(PROCESS_WORKINGDIRECTORY_KEY); + m_workingDirectory->setValue(Constants::DEFAULT_WORKING_DIR); + m_workingDirectory->setDisplayStyle(BaseStringAspect::PathChooserDisplay); + m_workingDirectory->setLabelText(tr("Working directory:")); + m_workingDirectory->setExpectedKind(Utils::PathChooser::Directory); } bool ProcessStep::init() { - BuildConfiguration *bc = buildConfiguration(); - ProcessParameters *pp = processParameters(); - pp->setMacroExpander(bc ? bc->macroExpander() : Utils::globalMacroExpander()); - pp->setEnvironment(bc ? bc->environment() : Utils::Environment::systemEnvironment()); - pp->setWorkingDirectory(workingDirectory()); - pp->setCommand(m_command); - pp->setArguments(m_arguments); - pp->resolveAll(); - + setupProcessParameters(processParameters()); setOutputParser(target()->kit()->createOutputParser()); return AbstractProcessStep::init(); } -void ProcessStep::doRun() +void ProcessStep::setupProcessParameters(ProcessParameters *pp) { - AbstractProcessStep::doRun(); -} + BuildConfiguration *bc = buildConfiguration(); -BuildStepConfigWidget *ProcessStep::createConfigWidget() -{ - return new ProcessStepConfigWidget(this); -} + QString command = m_command->value(); + QString arguments = m_arguments->value(); + QString workingDirectory = m_workingDirectory->value(); + if (workingDirectory.isEmpty()) { + if (bc) + workingDirectory = Constants::DEFAULT_WORKING_DIR; + else + workingDirectory = Constants::DEFAULT_WORKING_DIR_ALTERNATE; + } -QString ProcessStep::command() const -{ - return m_command; + pp->setMacroExpander(bc ? bc->macroExpander() : Utils::globalMacroExpander()); + pp->setEnvironment(bc ? bc->environment() : Utils::Environment::systemEnvironment()); + pp->setWorkingDirectory(Utils::FilePath::fromString(workingDirectory)); + pp->setCommand(Utils::FilePath::fromString(command)); + pp->setArguments(arguments); + pp->resolveAll(); } -QString ProcessStep::arguments() const +BuildStepConfigWidget *ProcessStep::createConfigWidget() { - return m_arguments; -} + auto widget = AbstractProcessStep::createConfigWidget(); -QString ProcessStep::workingDirectory() const -{ - return m_workingDirectory; -} + Core::VariableChooser::addSupportForChildWidgets(widget, macroExpander()); -void ProcessStep::setCommand(const QString &command) -{ - m_command = command; -} + auto updateDetails = [this, widget] { + QString display = displayName(); + if (display.isEmpty()) + display = tr("Custom Process Step"); + ProcessParameters param; + setupProcessParameters(¶m); + widget->setSummaryText(param.summary(display)); + }; -void ProcessStep::setArguments(const QString &arguments) -{ - m_arguments = arguments; -} + updateDetails(); -void ProcessStep::setWorkingDirectory(const QString &workingDirectory) -{ - if (workingDirectory.isEmpty()) - if (buildConfiguration()) - m_workingDirectory = Constants::DEFAULT_WORKING_DIR; - else - m_workingDirectory = Constants::DEFAULT_WORKING_DIR_ALTERNATE; - else - m_workingDirectory = workingDirectory; -} + connect(m_command, &ProjectConfigurationAspect::changed, + widget, updateDetails); + connect(m_workingDirectory, &ProjectConfigurationAspect::changed, + widget, updateDetails); + connect(m_arguments, &ProjectConfigurationAspect::changed, + widget, updateDetails); -QVariantMap ProcessStep::toMap() const -{ - QVariantMap map(AbstractProcessStep::toMap()); - map.insert(PROCESS_COMMAND_KEY, command()); - map.insert(PROCESS_ARGUMENTS_KEY, arguments()); - map.insert(PROCESS_WORKINGDIRECTORY_KEY, workingDirectory()); - return map; -} - -bool ProcessStep::fromMap(const QVariantMap &map) -{ - setCommand(map.value(PROCESS_COMMAND_KEY).toString()); - setArguments(map.value(PROCESS_ARGUMENTS_KEY).toString()); - setWorkingDirectory(map.value(PROCESS_WORKINGDIRECTORY_KEY).toString()); - return AbstractProcessStep::fromMap(map); + return widget; } //******* @@ -144,80 +136,4 @@ ProcessStepFactory::ProcessStepFactory() setDisplayName(ProcessStep::tr("Custom Process Step", "item in combobox")); } -//******* -// ProcessStepConfigWidget -//******* - -ProcessStepConfigWidget::ProcessStepConfigWidget(ProcessStep *step) - : BuildStepConfigWidget(step), m_step(step) -{ - m_ui.setupUi(this); - m_ui.command->setExpectedKind(Utils::PathChooser::Command); - m_ui.command->setHistoryCompleter("PE.ProcessStepCommand.History"); - m_ui.workingDirectory->setExpectedKind(Utils::PathChooser::Directory); - - BuildConfiguration *bc = m_step->buildConfiguration(); - Utils::Environment env = bc ? bc->environment() : Utils::Environment::systemEnvironment(); - m_ui.command->setEnvironment(env); - m_ui.command->setPath(m_step->command()); - - m_ui.workingDirectory->setEnvironment(env); - m_ui.workingDirectory->setPath(m_step->workingDirectory()); - - m_ui.commandArgumentsLineEdit->setText(m_step->arguments()); - - updateDetails(); - - connect(m_ui.command, &Utils::PathChooser::rawPathChanged, - this, &ProcessStepConfigWidget::commandLineEditTextEdited); - connect(m_ui.workingDirectory, &Utils::PathChooser::rawPathChanged, - this, &ProcessStepConfigWidget::workingDirectoryLineEditTextEdited); - - connect(m_ui.commandArgumentsLineEdit, &QLineEdit::textEdited, - this, &ProcessStepConfigWidget::commandArgumentsLineEditTextEdited); - Core::VariableChooser::addSupportForChildWidgets(this, m_step->macroExpander()); -} - -void ProcessStepConfigWidget::updateDetails() -{ - QString displayName = m_step->displayName(); - if (displayName.isEmpty()) - displayName = tr("Custom Process Step"); - ProcessParameters param; - BuildConfiguration *bc = m_step->buildConfiguration(); - param.setMacroExpander(bc ? bc->macroExpander() : Utils::globalMacroExpander()); - param.setEnvironment(bc ? bc->environment() : Utils::Environment::systemEnvironment()); - - param.setWorkingDirectory(m_step->workingDirectory()); - param.setCommand(m_step->command()); - param.setArguments(m_step->arguments()); - m_summaryText = param.summary(displayName); - emit updateSummary(); -} - -QString ProcessStepConfigWidget::displayName() const -{ - return m_step->displayName(); -} - -QString ProcessStepConfigWidget::summaryText() const -{ - return m_summaryText; -} - -void ProcessStepConfigWidget::commandLineEditTextEdited() -{ - m_step->setCommand(m_ui.command->rawPath()); - updateDetails(); -} - -void ProcessStepConfigWidget::workingDirectoryLineEditTextEdited() -{ - m_step->setWorkingDirectory(m_ui.workingDirectory->rawPath()); -} - -void ProcessStepConfigWidget::commandArgumentsLineEditTextEdited() -{ - m_step->setArguments(m_ui.commandArgumentsLineEdit->text()); - updateDetails(); -} +} // ProjectExplorer diff --git a/src/plugins/projectexplorer/processstep.h b/src/plugins/projectexplorer/processstep.h index 3bff8fbdca..241d440ad2 100644 --- a/src/plugins/projectexplorer/processstep.h +++ b/src/plugins/projectexplorer/processstep.h @@ -25,11 +25,11 @@ #pragma once -#include "ui_processstep.h" #include "abstractprocessstep.h" +#include "projectconfigurationaspects.h" +#include "projectexplorer_export.h" namespace ProjectExplorer { -namespace Internal { class ProcessStepFactory : public BuildStepFactory { @@ -37,7 +37,7 @@ public: ProcessStepFactory(); }; -class ProcessStep : public AbstractProcessStep +class PROJECTEXPLORER_EXPORT ProcessStep : public AbstractProcessStep { Q_OBJECT friend class ProcessStepFactory; @@ -47,41 +47,13 @@ public: BuildStepConfigWidget *createConfigWidget() override; - QString command() const; - QString arguments() const; - QString workingDirectory() const; - - void setCommand(const QString &command); - void setArguments(const QString &arguments); - void setWorkingDirectory(const QString &workingDirectory); - private: bool init() override; - void doRun() override; - QVariantMap toMap() const override; - bool fromMap(const QVariantMap &map) override; - - QString m_command; - QString m_arguments; - QString m_workingDirectory; -}; + void setupProcessParameters(ProcessParameters *pp); -class ProcessStepConfigWidget : public BuildStepConfigWidget -{ - Q_OBJECT -public: - ProcessStepConfigWidget(ProcessStep *step); - virtual QString displayName() const; - virtual QString summaryText() const; -private: - void commandLineEditTextEdited(); - void workingDirectoryLineEditTextEdited(); - void commandArgumentsLineEditTextEdited(); - void updateDetails(); - ProcessStep *m_step; - Ui::ProcessStepWidget m_ui; - QString m_summaryText; + ProjectExplorer::BaseStringAspect *m_command; + ProjectExplorer::BaseStringAspect *m_arguments; + ProjectExplorer::BaseStringAspect *m_workingDirectory; }; -} // namespace Internal } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/processstep.ui b/src/plugins/projectexplorer/processstep.ui deleted file mode 100644 index bcbaf740dd..0000000000 --- a/src/plugins/projectexplorer/processstep.ui +++ /dev/null @@ -1,62 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>ProjectExplorer::Internal::ProcessStepWidget</class> - <widget class="QWidget" name="ProjectExplorer::Internal::ProcessStepWidget"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>262</width> - <height>85</height> - </rect> - </property> - <layout class="QFormLayout" name="formLayout"> - <property name="fieldGrowthPolicy"> - <enum>QFormLayout::AllNonFixedFieldsGrow</enum> - </property> - <property name="margin"> - <number>0</number> - </property> - <item row="0" column="0"> - <widget class="QLabel" name="commandLabel"> - <property name="text"> - <string>Command:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="Utils::PathChooser" name="command" native="true"/> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="commandArgumentsLabel"> - <property name="text"> - <string>Arguments:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLineEdit" name="commandArgumentsLineEdit"/> - </item> - <item row="3" column="0"> - <widget class="QLabel" name="workingDirecoryLabel"> - <property name="text"> - <string>Working directory:</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="Utils::PathChooser" name="workingDirectory" native="true"/> - </item> - </layout> - </widget> - <customwidgets> - <customwidget> - <class>Utils::PathChooser</class> - <extends>QWidget</extends> - <header location="global">utils/pathchooser.h</header> - <container>1</container> - </customwidget> - </customwidgets> - <resources/> - <connections/> -</ui> diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index 94484f3493..d909c4b909 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -30,6 +30,7 @@ #include "deployconfiguration.h" #include "editorconfiguration.h" #include "kit.h" +#include "makestep.h" #include "projectexplorer.h" #include "projectnodes.h" #include "target.h" @@ -51,6 +52,8 @@ #include <utils/macroexpander.h> #include <utils/qtcassert.h> +#include <QFileDialog> + #include <limits> #include <memory> @@ -112,7 +115,7 @@ const Project::NodeMatcher Project::GeneratedFiles = [](const Node *node) { // ProjectDocument: // -------------------------------------------------------------------- -ProjectDocument::ProjectDocument(const QString &mimeType, const Utils::FileName &fileName, +ProjectDocument::ProjectDocument(const QString &mimeType, const Utils::FilePath &fileName, const ProjectDocument::ProjectCallback &callback) : m_callback(callback) { @@ -149,7 +152,7 @@ bool ProjectDocument::reload(QString *errorString, Core::IDocument::ReloadFlag f class ProjectPrivate { public: - ProjectPrivate(const QString &mimeType, const Utils::FileName &fileName, + ProjectPrivate(const QString &mimeType, const Utils::FilePath &fileName, const ProjectDocument::ProjectCallback &callback) { m_document = std::make_unique<ProjectDocument>(mimeType, fileName, callback); @@ -177,6 +180,7 @@ public: Kit::Predicate m_preferredKitPredicate; Utils::MacroExpander m_macroExpander; + Utils::FilePath m_rootProjectDirectory; mutable QVector<const Node *> m_sortedNodeList; }; @@ -186,7 +190,7 @@ ProjectPrivate::~ProjectPrivate() std::unique_ptr<ProjectNode> oldNode = std::move(m_rootProjectNode); } -Project::Project(const QString &mimeType, const Utils::FileName &fileName, +Project::Project(const QString &mimeType, const Utils::FilePath &fileName, const ProjectDocument::ProjectCallback &callback) : d(new ProjectPrivate(mimeType, fileName, callback)) { @@ -229,9 +233,9 @@ Core::IDocument *Project::document() const return d->m_document.get(); } -Utils::FileName Project::projectFilePath() const +Utils::FilePath Project::projectFilePath() const { - QTC_ASSERT(document(), return Utils::FileName()); + QTC_ASSERT(document(), return Utils::FilePath()); return document()->filePath(); } @@ -327,9 +331,9 @@ Target *Project::target(Kit *k) const return Utils::findOrDefault(d->m_targets, Utils::equal(&Target::kit, k)); } -QList<Task> Project::projectIssues(const Kit *k) const +Tasks Project::projectIssues(const Kit *k) const { - QList<Task> result; + Tasks result; if (!k->isValid()) result.append(createProjectTask(Task::TaskType::Error, tr("Kit is not valid."))); return {}; @@ -580,19 +584,19 @@ Project::RestoreResult Project::restoreSettings(QString *errorMessage) /*! * Returns a sorted list of all files matching the predicate \a filter. */ -Utils::FileNameList Project::files(const Project::NodeMatcher &filter) const +Utils::FilePathList Project::files(const Project::NodeMatcher &filter) const { - Utils::FileNameList result; + Utils::FilePathList result; if (d->m_sortedNodeList.empty() && filter(containerNode())) result.append(projectFilePath()); - Utils::FileName lastAdded; + Utils::FilePath lastAdded; for (const Node *n : qAsConst(d->m_sortedNodeList)) { if (filter && !filter(n)) continue; // Remove duplicates: - const Utils::FileName path = n->filePath(); + const Utils::FilePath path = n->filePath(); if (path == lastAdded) continue; // skip duplicates lastAdded = path; @@ -635,7 +639,7 @@ QVariantMap Project::toMap() const This includes the absolute path. */ -Utils::FileName Project::projectDirectory() const +Utils::FilePath Project::projectDirectory() const { return projectDirectory(projectFilePath()); } @@ -646,20 +650,37 @@ Utils::FileName Project::projectDirectory() const This includes the absolute path. */ -Utils::FileName Project::projectDirectory(const Utils::FileName &top) +Utils::FilePath Project::projectDirectory(const Utils::FilePath &top) { if (top.isEmpty()) - return Utils::FileName(); - return Utils::FileName::fromString(top.toFileInfo().absoluteDir().path()); + return Utils::FilePath(); + return Utils::FilePath::fromString(top.toFileInfo().absoluteDir().path()); +} + +void Project::changeRootProjectDirectory() +{ + Utils::FilePath rootPath = Utils::FilePath::fromString( + QFileDialog::getExistingDirectory(Core::ICore::dialogParent(), + tr("Select the Root Directory"), + rootProjectDirectory().toString(), + QFileDialog::ShowDirsOnly + | QFileDialog::DontResolveSymlinks)); + if (rootPath != d->m_rootProjectDirectory) { + d->m_rootProjectDirectory = rootPath; + setNamedSettings(Constants::PROJECT_ROOT_PATH_KEY, d->m_rootProjectDirectory.toString()); + emit rootProjectDirectoryChanged(); + } } /*! - Returns the common root directory that contains all files which belongs to a project. + Returns the common root directory that contains all files which belong to a project. */ - -Utils::FileName Project::rootProjectDirectory() const +Utils::FilePath Project::rootProjectDirectory() const { - return projectDirectory(); // TODO parse all files and find the common path + if (!d->m_rootProjectDirectory.isEmpty()) + return d->m_rootProjectDirectory; + + return projectDirectory(); } ProjectNode *Project::rootProjectNode() const @@ -701,6 +722,9 @@ Project::RestoreResult Project::fromMap(const QVariantMap &map, QString *errorMe createTargetFromMap(map, i); } + d->m_rootProjectDirectory = Utils::FilePath::fromString( + namedSettings(Constants::PROJECT_ROOT_PATH_KEY).toString()); + return RestoreResult::Ok; } @@ -729,11 +753,11 @@ QStringList Project::filesGeneratedFrom(const QString &file) const return QStringList(); } -bool Project::isKnownFile(const Utils::FileName &filename) const +bool Project::isKnownFile(const Utils::FilePath &filename) const { if (d->m_sortedNodeList.empty()) return filename == projectFilePath(); - const FileNode element(filename, FileType::Unknown, false); + const FileNode element(filename, FileType::Unknown); return std::binary_search(std::begin(d->m_sortedNodeList), std::end(d->m_sortedNodeList), &element, nodeLessThan); } @@ -778,7 +802,7 @@ void Project::projectLoaded() Task Project::createProjectTask(Task::TaskType type, const QString &description) { - return Task(type, description, Utils::FileName(), -1, Core::Id()); + return Task(type, description, Utils::FilePath(), -1, Core::Id()); } Core::Context Project::projectContext() const @@ -824,6 +848,20 @@ bool Project::knowsAllBuildExecutables() const return true; } +MakeInstallCommand Project::makeInstallCommand(const Target *target, const QString &installRoot) +{ + QTC_ASSERT(hasMakeInstallEquivalent(), return MakeInstallCommand()); + MakeInstallCommand cmd; + if (const BuildConfiguration * const bc = target->activeBuildConfiguration()) { + if (const auto makeStep = bc->stepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD) + ->firstOfType<MakeStep>()) { + cmd.command = makeStep->effectiveMakeCommand(); + } + } + cmd.arguments << "install" << ("INSTALL_ROOT=" + QDir::toNativeSeparators(installRoot)); + return cmd; +} + void Project::setup(const QList<BuildInfo> &infoList) { std::vector<std::unique_ptr<Target>> toRegister; @@ -921,18 +959,18 @@ void Project::setPreferredKitPredicate(const Kit::Predicate &predicate) namespace ProjectExplorer { -static Utils::FileName constructTestPath(const char *basePath) +static Utils::FilePath constructTestPath(const char *basePath) { - Utils::FileName drive; + Utils::FilePath drive; if (Utils::HostOsInfo::isWindowsHost()) - drive = Utils::FileName::fromString("C:"); + drive = Utils::FilePath::fromString("C:"); return drive + QLatin1String(basePath); } -const Utils::FileName TEST_PROJECT_PATH = constructTestPath("/tmp/foobar/baz.project"); -const Utils::FileName TEST_PROJECT_NONEXISTING_FILE = constructTestPath("/tmp/foobar/nothing.cpp"); -const Utils::FileName TEST_PROJECT_CPP_FILE = constructTestPath("/tmp/foobar/main.cpp"); -const Utils::FileName TEST_PROJECT_GENERATED_FILE = constructTestPath("/tmp/foobar/generated.foo"); +const Utils::FilePath TEST_PROJECT_PATH = constructTestPath("/tmp/foobar/baz.project"); +const Utils::FilePath TEST_PROJECT_NONEXISTING_FILE = constructTestPath("/tmp/foobar/nothing.cpp"); +const Utils::FilePath TEST_PROJECT_CPP_FILE = constructTestPath("/tmp/foobar/main.cpp"); +const Utils::FilePath TEST_PROJECT_GENERATED_FILE = constructTestPath("/tmp/foobar/generated.foo"); const QString TEST_PROJECT_MIMETYPE = "application/vnd.test.qmakeprofile"; const QString TEST_PROJECT_DISPLAYNAME = "testProjectFoo"; const char TEST_PROJECT_ID[] = "Test.Project.Id"; @@ -958,12 +996,6 @@ public: bool needsConfiguration() const final { return false; } }; -class TestProjectNode : public ProjectNode -{ -public: - TestProjectNode(const Utils::FileName &dir) : ProjectNode(dir) { } -}; - void ProjectExplorerPlugin::testProject_setup() { TestProject project; @@ -1060,11 +1092,12 @@ void ProjectExplorerPlugin::testProject_parsingFail() std::unique_ptr<ProjectNode> createFileTree(Project *project) { - std::unique_ptr<ProjectNode> root = std::make_unique<TestProjectNode>(project->projectDirectory()); + std::unique_ptr<ProjectNode> root = std::make_unique<ProjectNode>(project->projectDirectory()); std::vector<std::unique_ptr<FileNode>> nodes; - nodes.emplace_back(std::make_unique<FileNode>(TEST_PROJECT_PATH, FileType::Project, false)); - nodes.emplace_back(std::make_unique<FileNode>(TEST_PROJECT_CPP_FILE, FileType::Source, false)); - nodes.emplace_back(std::make_unique<FileNode>(TEST_PROJECT_GENERATED_FILE, FileType::Source, true)); + nodes.emplace_back(std::make_unique<FileNode>(TEST_PROJECT_PATH, FileType::Project)); + nodes.emplace_back(std::make_unique<FileNode>(TEST_PROJECT_CPP_FILE, FileType::Source)); + nodes.emplace_back(std::make_unique<FileNode>(TEST_PROJECT_GENERATED_FILE, FileType::Source)); + nodes.back()->setIsGenerated(true); root->addNestedNodes(std::move(nodes)); return root; @@ -1079,7 +1112,7 @@ void ProjectExplorerPlugin::testProject_projectTree() QCOMPARE(fileSpy.count(), 0); QVERIFY(!project.rootProjectNode()); - project.setRootProjectNode(std::make_unique<TestProjectNode>(project.projectDirectory())); + project.setRootProjectNode(std::make_unique<ProjectNode>(project.projectDirectory())); QCOMPARE(fileSpy.count(), 0); QVERIFY(!project.rootProjectNode()); @@ -1095,14 +1128,14 @@ void ProjectExplorerPlugin::testProject_projectTree() QCOMPARE(project.isKnownFile(TEST_PROJECT_CPP_FILE), true); QCOMPARE(project.isKnownFile(TEST_PROJECT_GENERATED_FILE), true); - Utils::FileNameList allFiles = project.files(Project::AllFiles); + Utils::FilePathList allFiles = project.files(Project::AllFiles); QCOMPARE(allFiles.count(), 3); QVERIFY(allFiles.contains(TEST_PROJECT_PATH)); QVERIFY(allFiles.contains(TEST_PROJECT_CPP_FILE)); QVERIFY(allFiles.contains(TEST_PROJECT_GENERATED_FILE)); QCOMPARE(project.files(Project::GeneratedFiles), {TEST_PROJECT_GENERATED_FILE}); - Utils::FileNameList sourceFiles = project.files(Project::SourceFiles); + Utils::FilePathList sourceFiles = project.files(Project::SourceFiles); QCOMPARE(sourceFiles.count(), 2); QVERIFY(sourceFiles.contains(TEST_PROJECT_PATH)); QVERIFY(sourceFiles.contains(TEST_PROJECT_CPP_FILE)); diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index cf9590ad68..e66e049c0c 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -27,6 +27,7 @@ #include "projectexplorer_export.h" +#include "deploymentdata.h" #include "kit.h" #include "subscription.h" @@ -63,7 +64,7 @@ class PROJECTEXPLORER_EXPORT ProjectDocument : public Core::IDocument public: using ProjectCallback = std::function<void()>; - ProjectDocument(const QString &mimeType, const Utils::FileName &fileName, + ProjectDocument(const QString &mimeType, const Utils::FilePath &fileName, const ProjectCallback &callback = {}); Core::IDocument::ReloadBehavior reloadBehavior(Core::IDocument::ChangeTrigger state, @@ -90,7 +91,7 @@ public: isParsingRole }; - Project(const QString &mimeType, const Utils::FileName &fileName, + Project(const QString &mimeType, const Utils::FilePath &fileName, const ProjectDocument::ProjectCallback &callback = {}); ~Project() override; @@ -100,10 +101,13 @@ public: QString mimeType() const; Core::IDocument *document() const; - Utils::FileName projectFilePath() const; - Utils::FileName projectDirectory() const; - Utils::FileName rootProjectDirectory() const; - static Utils::FileName projectDirectory(const Utils::FileName &top); + Utils::FilePath projectFilePath() const; + Utils::FilePath projectDirectory() const; + static Utils::FilePath projectDirectory(const Utils::FilePath &top); + + // This does not affect nodes, only the root path. + void changeRootProjectDirectory(); + Utils::FilePath rootProjectDirectory() const; virtual ProjectNode *rootProjectNode() const; ContainerNode *containerNode() const; @@ -122,7 +126,7 @@ public: Target *activeTarget() const; Target *target(Core::Id id) const; Target *target(Kit *k) const; - virtual QList<Task> projectIssues(const Kit *k) const; + virtual Tasks projectIssues(const Kit *k) const; std::unique_ptr<Target> createTarget(Kit *k); static bool copySteps(Target *sourceTarget, Target *newTarget); @@ -137,9 +141,9 @@ public: static const NodeMatcher SourceFiles; static const NodeMatcher GeneratedFiles; - Utils::FileNameList files(const NodeMatcher &matcher) const; + Utils::FilePathList files(const NodeMatcher &matcher) const; virtual QStringList filesGeneratedFrom(const QString &sourceFile) const; - bool isKnownFile(const Utils::FileName &filename) const; + bool isKnownFile(const Utils::FilePath &filename) const; virtual QVariantMap toMap() const; @@ -162,6 +166,10 @@ public: // of configuration. virtual bool knowsAllBuildExecutables() const; + virtual DeploymentKnowledge deploymentKnowledge() const { return DeploymentKnowledge::Bad; } + virtual bool hasMakeInstallEquivalent() const { return false; } + virtual MakeInstallCommand makeInstallCommand(const Target *target, const QString &installRoot); + void setup(const QList<BuildInfo> &infoList); Utils::MacroExpander *macroExpander() const; @@ -221,6 +229,8 @@ signals: void parsingStarted(); void parsingFinished(bool success); + void rootProjectDirectoryChanged(); + protected: virtual RestoreResult fromMap(const QVariantMap &map, QString *errorMessage); void createTargetFromMap(const QVariantMap &map, int index); diff --git a/src/plugins/projectexplorer/projectconfiguration.cpp b/src/plugins/projectexplorer/projectconfiguration.cpp index d51084f1ed..3d05c9ff87 100644 --- a/src/plugins/projectexplorer/projectconfiguration.cpp +++ b/src/plugins/projectexplorer/projectconfiguration.cpp @@ -181,26 +181,13 @@ ProjectConfigurationAspect *ProjectConfiguration::aspect(Core::Id id) const return m_aspects.aspect(id); } -Core::Id ProjectExplorer::idFromMap(const QVariantMap &map) -{ - return Core::Id::fromSetting(map.value(QLatin1String(CONFIGURATION_ID_KEY))); -} - -// StatefulProjectConfiguration - -bool StatefulProjectConfiguration::isEnabled() const +void ProjectConfiguration::acquaintAspects() { - return m_isEnabled; + for (ProjectConfigurationAspect *aspect : m_aspects) + aspect->acquaintSiblings(m_aspects); } -StatefulProjectConfiguration::StatefulProjectConfiguration(QObject *parent, Core::Id id) : - ProjectConfiguration(parent, id) -{ } - -void StatefulProjectConfiguration::setEnabled(bool enabled) +Core::Id ProjectExplorer::idFromMap(const QVariantMap &map) { - if (enabled == m_isEnabled) - return; - m_isEnabled = enabled; - emit enabledChanged(); + return Core::Id::fromSetting(map.value(QLatin1String(CONFIGURATION_ID_KEY))); } diff --git a/src/plugins/projectexplorer/projectconfiguration.h b/src/plugins/projectexplorer/projectconfiguration.h index 685661d482..2d5be2299d 100644 --- a/src/plugins/projectexplorer/projectconfiguration.h +++ b/src/plugins/projectexplorer/projectconfiguration.h @@ -41,6 +41,7 @@ QT_END_NAMESPACE namespace ProjectExplorer { class Project; +class ProjectConfigurationAspects; class PROJECTEXPLORER_EXPORT ProjectConfigurationAspect : public QObject { @@ -68,6 +69,7 @@ public: virtual void fromMap(const QVariantMap &) {} virtual void toMap(QVariantMap &) const {} virtual void addToConfigurationLayout(QFormLayout *) {} + virtual void acquaintSiblings(const ProjectConfigurationAspects &) {} signals: void changed(); @@ -168,6 +170,8 @@ public: ProjectConfigurationAspect *aspect(Core::Id id) const; template <typename T> T *aspect() const { return m_aspects.aspect<T>(); } + void acquaintAspects(); + signals: void displayNameChanged(); void toolTipChanged(); @@ -183,29 +187,6 @@ private: Utils::MacroExpander m_macroExpander; }; -class PROJECTEXPLORER_EXPORT StatefulProjectConfiguration : public ProjectConfiguration -{ - Q_OBJECT - -public: - StatefulProjectConfiguration() = default; - - bool isEnabled() const; - - virtual QString disabledReason() const = 0; - -signals: - void enabledChanged(); - -protected: - StatefulProjectConfiguration(QObject *parent, Core::Id id); - - void setEnabled(bool enabled); - -private: - bool m_isEnabled = false; -}; - // helper function: PROJECTEXPLORER_EXPORT Core::Id idFromMap(const QVariantMap &map); diff --git a/src/plugins/projectexplorer/projectconfigurationaspects.cpp b/src/plugins/projectexplorer/projectconfigurationaspects.cpp index c6f9ad9552..a4cec91d28 100644 --- a/src/plugins/projectexplorer/projectconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/projectconfigurationaspects.cpp @@ -79,15 +79,17 @@ public: QPointer<PathChooser> m_pathChooserDisplay; QPointer<QTextEdit> m_textEditDisplay; QPixmap m_labelPixmap; + Utils::FilePath m_baseFileName; }; class BaseIntegerAspectPrivate { public: - QVariant m_value; + qint64 m_value = 0; QVariant m_minimumValue; QVariant m_maximumValue; int m_displayIntegerBase = 10; + qint64 m_displayScaleFactor = 1; QString m_label; QString m_prefix; QString m_suffix; @@ -136,9 +138,14 @@ void BaseStringAspect::toMap(QVariantMap &map) const d->m_checker->toMap(map); } -FileName BaseStringAspect::fileName() const +FilePath BaseStringAspect::fileName() const { - return FileName::fromString(d->m_value); + return FilePath::fromString(d->m_value); +} + +void BaseStringAspect::setFileName(const FilePath &val) +{ + setValue(val.toString()); } void BaseStringAspect::setLabelText(const QString &labelText) @@ -207,6 +214,13 @@ void BaseStringAspect::setEnvironment(const Environment &env) d->m_pathChooserDisplay->setEnvironment(env); } +void BaseStringAspect::setBaseFileName(const FilePath &baseFileName) +{ + d->m_baseFileName = baseFileName; + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->setBaseFileName(baseFileName); +} + void BaseStringAspect::addToConfigurationLayout(QFormLayout *layout) { QTC_CHECK(!d->m_label); @@ -225,6 +239,7 @@ void BaseStringAspect::addToConfigurationLayout(QFormLayout *layout) if (!d->m_historyCompleterKey.isEmpty()) d->m_pathChooserDisplay->setHistoryCompleter(d->m_historyCompleterKey); d->m_pathChooserDisplay->setEnvironment(d->m_environment); + d->m_pathChooserDisplay->setBaseFileName(d->m_baseFileName); connect(d->m_pathChooserDisplay, &PathChooser::pathChanged, this, &BaseStringAspect::setValue); hbox->addWidget(d->m_pathChooserDisplay); @@ -277,7 +292,7 @@ void BaseStringAspect::update() const bool enabled = !d->m_checker || d->m_checker->value(); if (d->m_pathChooserDisplay) { - d->m_pathChooserDisplay->setFileName(FileName::fromString(displayedString)); + d->m_pathChooserDisplay->setFileName(FilePath::fromString(displayedString)); d->m_pathChooserDisplay->setEnabled(enabled); } @@ -357,6 +372,7 @@ bool BaseBoolAspect::defaultValue() const void BaseBoolAspect::setDefaultValue(bool defaultValue) { d->m_defaultValue = defaultValue; + d->m_value = defaultValue; } bool BaseBoolAspect::value() const @@ -397,23 +413,24 @@ void BaseIntegerAspect::addToConfigurationLayout(QFormLayout *layout) { QTC_CHECK(!d->m_spinBox); d->m_spinBox = new QSpinBox(layout->parentWidget()); - d->m_spinBox->setValue(d->m_value.toInt()); + d->m_spinBox->setValue(int(d->m_value / d->m_displayScaleFactor)); d->m_spinBox->setDisplayIntegerBase(d->m_displayIntegerBase); d->m_spinBox->setPrefix(d->m_prefix); d->m_spinBox->setSuffix(d->m_suffix); if (d->m_maximumValue.isValid() && d->m_maximumValue.isValid()) - d->m_spinBox->setRange(d->m_minimumValue.toInt(), d->m_maximumValue.toInt()); + d->m_spinBox->setRange(int(d->m_minimumValue.toLongLong() / d->m_displayScaleFactor), + int(d->m_maximumValue.toLongLong() / d->m_displayScaleFactor)); layout->addRow(d->m_label, d->m_spinBox); - connect(d->m_spinBox.data(), static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + connect(d->m_spinBox.data(), QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int value) { - d->m_value = value; + d->m_value = value * d->m_displayScaleFactor; emit changed(); }); } void BaseIntegerAspect::fromMap(const QVariantMap &map) { - d->m_value = map.value(settingsKey()); + d->m_value = map.value(settingsKey()).toLongLong(); } void BaseIntegerAspect::toMap(QVariantMap &data) const @@ -421,19 +438,19 @@ void BaseIntegerAspect::toMap(QVariantMap &data) const data.insert(settingsKey(), d->m_value); } -int BaseIntegerAspect::value() const +qint64 BaseIntegerAspect::value() const { - return d->m_value.toInt(); + return d->m_value; } -void BaseIntegerAspect::setValue(int value) +void BaseIntegerAspect::setValue(qint64 value) { d->m_value = value; if (d->m_spinBox) - d->m_spinBox->setValue(d->m_value.toInt()); + d->m_spinBox->setValue(int(d->m_value / d->m_displayScaleFactor)); } -void BaseIntegerAspect::setRange(int min, int max) +void BaseIntegerAspect::setRange(qint64 min, qint64 max) { d->m_minimumValue = min; d->m_maximumValue = max; @@ -459,4 +476,9 @@ void BaseIntegerAspect::setDisplayIntegerBase(int base) d->m_displayIntegerBase = base; } +void BaseIntegerAspect::setDisplayScaleFactor(qint64 factor) +{ + d->m_displayScaleFactor = factor; +} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectconfigurationaspects.h b/src/plugins/projectexplorer/projectconfigurationaspects.h index 593f6d3ed5..250482b44e 100644 --- a/src/plugins/projectexplorer/projectconfigurationaspects.h +++ b/src/plugins/projectexplorer/projectconfigurationaspects.h @@ -89,6 +89,7 @@ public: void setHistoryCompleter(const QString &historyCompleterKey); void setExpectedKind(const Utils::PathChooser::Kind expectedKind); void setEnvironment(const Utils::Environment &env); + void setBaseFileName(const Utils::FilePath &baseFileName); bool isChecked() const; void makeCheckable(const QString &optionalLabel, const QString &optionalBaseKey); @@ -104,8 +105,8 @@ public: void fromMap(const QVariantMap &map) override; void toMap(QVariantMap &map) const override; - Utils::FileName fileName() const; - void setFileName(const Utils::FileName &val); + Utils::FilePath fileName() const; + void setFileName(const Utils::FilePath &val); private: void update(); @@ -123,14 +124,15 @@ public: void addToConfigurationLayout(QFormLayout *layout) override; - int value() const; - void setValue(int val); + qint64 value() const; + void setValue(qint64 val); - void setRange(int min, int max); + void setRange(qint64 min, qint64 max); void setLabel(const QString &label); void setPrefix(const QString &prefix); void setSuffix(const QString &suffix); void setDisplayIntegerBase(int base); + void setDisplayScaleFactor(qint64 factor); void fromMap(const QVariantMap &map) override; void toMap(QVariantMap &map) const override; diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 3d619eab82..488a9fe117 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -25,7 +25,9 @@ #include "projectexplorer.h" +#include "appoutputpane.h" #include "buildsteplist.h" +#include "compileoutputwindow.h" #include "configtaskhandler.h" #include "customexecutablerunconfiguration.h" #include "customwizard/customwizard.h" @@ -48,6 +50,7 @@ #include "kitfeatureprovider.h" #include "kitmanager.h" #include "kitoptionspage.h" +#include "parseissuesdialog.h" #include "target.h" #include "toolchainmanager.h" #include "toolchainoptionspage.h" @@ -98,31 +101,32 @@ #include "projectwelcomepage.h" #include <app/app_version.h> -#include <extensionsystem/pluginspec.h> -#include <extensionsystem/pluginmanager.h> -#include <coreplugin/icore.h> -#include <coreplugin/id.h> -#include <coreplugin/idocumentfactory.h> -#include <coreplugin/idocument.h> -#include <coreplugin/coreconstants.h> -#include <coreplugin/documentmanager.h> -#include <coreplugin/imode.h> -#include <coreplugin/modemanager.h> -#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actioncontainer.h> +#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/diffservice.h> +#include <coreplugin/documentmanager.h> #include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/fileutils.h> #include <coreplugin/findplaceholder.h> -#include <coreplugin/vcsmanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/id.h> +#include <coreplugin/idocument.h> +#include <coreplugin/idocumentfactory.h> +#include <coreplugin/imode.h> #include <coreplugin/iversioncontrol.h> -#include <coreplugin/fileutils.h> -#include <coreplugin/diffservice.h> +#include <coreplugin/locator/directoryfilter.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/vcsmanager.h> +#include <extensionsystem/pluginmanager.h> +#include <extensionsystem/pluginspec.h> +#include <ssh/sshconnection.h> +#include <ssh/sshsettings.h> #include <texteditor/findinfiles.h> #include <texteditor/textdocument.h> #include <texteditor/texteditorconstants.h> -#include <ssh/sshconnection.h> -#include <ssh/sshsettings.h> #include <utils/algorithm.h> #include <utils/fileutils.h> @@ -244,6 +248,11 @@ const char PROJECT_OPEN_LOCATIONS_CONTEXT_MENU[] = "Project.P.OpenLocation.CtxM const char DEFAULT_BUILD_DIRECTORY_TEMPLATE[] = "../%{JS: Util.asciify(\"build-%{CurrentProject:Name}-%{CurrentKit:FileSystemName}-%{CurrentBuild:Name}\")}"; const char DEFAULT_BUILD_DIRECTORY_TEMPLATE_KEY[] = "Directories/BuildDirectory.Template"; +const char TERMINAL_MODE_SETTINGS_KEY[] = "ProjectExplorer/Settings/TerminalMode"; +const char CLOSE_FILES_WITH_PROJECT_SETTINGS_KEY[] + = "ProjectExplorer/Settings/CloseFilesWithProject"; +const char CLEAR_ISSUES_ON_REBUILD_SETTINGS_KEY[] = "ProjectExplorer/Settings/ClearIssuesOnRebuild"; + } // namespace Constants @@ -259,11 +268,20 @@ static Utils::optional<Utils::Environment> buildEnv(const Project *project) return project->activeTarget()->activeBuildConfiguration()->environment(); } -static Utils::optional<Utils::Environment> runEnv(const Project *project) +static bool canOpenTerminalWithRunEnv(const Project *project) { - if (!project || !project->activeTarget() || !project->activeTarget()->activeRunConfiguration()) - return {}; - return project->activeTarget()->activeRunConfiguration()->runnable().environment; + if (!project) + return false; + const Target * const target = project->activeTarget(); + if (!target) + return false; + const RunConfiguration * const runConfig = target->activeRunConfiguration(); + if (!runConfig) + return false; + IDevice::ConstPtr device = runConfig->runnable().device; + if (!device) + device = DeviceKitAspect::device(target->kit()); + return device && device->canOpenTerminal(); } static Target *activeTarget() @@ -316,6 +334,8 @@ class ProjectExplorerPluginPrivate : public QObject Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::ProjectExplorerPlugin) public: + ProjectExplorerPluginPrivate(); + void deploy(QList<Project *>); int queue(QList<Project *>, QList<Id> stepIds); void updateContextMenuActions(); @@ -352,6 +372,7 @@ public: void handleAddExistingFiles(); void addExistingDirectory(); void addNewSubproject(); + void addExistingProjects(); void removeProject(); void openFile(); void searchOnFileSystem(); @@ -370,6 +391,7 @@ public: void updateUnloadProjectMenu(); using EnvironmentGetter = std::function<Utils::optional<Utils::Environment>(const Project *project)>; void openTerminalHere(const EnvironmentGetter &env); + void openTerminalHereWithRunEnv(); void invalidateProject(ProjectExplorer::Project *project); @@ -434,6 +456,7 @@ public: QAction *m_addExistingFilesAction; QAction *m_addExistingDirectoryAction; QAction *m_addNewSubprojectAction; + QAction *m_addExistingProjectsAction; QAction *m_removeFileAction; QAction *m_duplicateFileAction; QAction *m_removeProjectAction; @@ -464,7 +487,7 @@ public: int m_activeRunControlCount = 0; int m_shutdownWatchDogId = -1; - QHash<QString, std::function<Project *(const Utils::FileName &)>> m_projectCreators; + QHash<QString, std::function<Project *(const Utils::FilePath &)>> m_projectCreators; QList<QPair<QString, QString> > m_recentProjects; // pair of filename, displayname static const int m_maxRecentProjects = 25; @@ -478,7 +501,6 @@ public: bool m_shuttingDown = false; Core::Id m_runMode = Constants::NO_RUN_MODE; - KitManager *m_kitManager = nullptr; ToolChainManager *m_toolChainManager = nullptr; QStringList m_arguments; @@ -532,6 +554,7 @@ public: AllProjectsFilter m_allProjectsFilter; CurrentProjectFilter m_currentProjectFilter; + DirectoryFilter m_allProjectDirectoriesFilter; ProcessStepFactory m_processStepFactory; @@ -539,11 +562,15 @@ public: CurrentProjectFind m_curretProjectFind; CustomExecutableRunConfigurationFactory m_customExecutableRunConfigFactory; + SimpleRunWorkerFactory<SimpleTargetRunner, CustomExecutableRunConfiguration> + m_customExecutableRunWorkerFactory; ProjectFileWizardExtension m_projectFileWizardExtension; // Settings pages ProjectExplorerSettingsPage m_projectExplorerSettingsPage; + AppOutputSettingsPage m_appOutputSettingsPage; + CompileOutputSettingsPage m_compileOutputSettingsPage; DeviceSettingsPage m_deviceSettingsPage; SshSettingsPage m_sshSettingsPage; @@ -552,6 +579,12 @@ public: DefaultDeployConfigurationFactory m_defaultDeployConfigFactory; IDocumentFactory m_documentFactory; + + DeviceTypeKitAspect deviceTypeKitAspect; + DeviceKitAspect deviceeKitAspect; + ToolChainKitAspect toolChainKitAspect; + SysRootKitAspect sysRootKitAspect; + EnvironmentKitAspect environmentKitAspect; }; static ProjectExplorerPlugin *m_instance = nullptr; @@ -568,7 +601,7 @@ ProjectExplorerPlugin::~ProjectExplorerPlugin() JsonWizardFactory::destroyAllFactories(); // Force sequence of deletion: - delete dd->m_kitManager; // remove all the profile information + KitManager::destroy(); // remove all the profile information delete dd->m_toolChainManager; ProjectPanelFactory::destroyFactories(); delete dd; @@ -595,7 +628,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er CustomWizard::setVerbose(arguments.count(QLatin1String("-customwizard-verbose"))); JsonWizardFactory::setVerbose(arguments.count(QLatin1String("-customwizard-verbose"))); - dd->m_kitManager = new KitManager; // register before ToolChainManager dd->m_toolChainManager = new ToolChainManager; // Register languages @@ -604,13 +636,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er IWizardFactory::registerFeatureProvider(new KitFeatureProvider); - // Register KitInformation: - KitManager::registerKitInformation<DeviceTypeKitInformation>(); - KitManager::registerKitInformation<DeviceKitInformation>(); - KitManager::registerKitInformation<ToolChainKitInformation>(); - KitManager::registerKitInformation<SysRootKitInformation>(); - KitManager::registerKitInformation<EnvironmentKitInformation>(); - IWizardFactory::registerFactoryCreator([]() -> QList<IWizardFactory *> { QList<IWizardFactory *> result; result << CustomWizard::createWizards(); @@ -640,8 +665,21 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er dd, &ProjectExplorerPluginPrivate::updateActions); connect(sessionManager, &SessionManager::sessionLoaded, dd, &ProjectExplorerPluginPrivate::updateActions); - connect(sessionManager, &SessionManager::sessionLoaded, - dd, &ProjectExplorerPluginPrivate::updateWelcomePage); + connect(sessionManager, + &SessionManager::sessionLoaded, + dd, + &ProjectExplorerPluginPrivate::updateWelcomePage); + + connect(sessionManager, &SessionManager::projectAdded, dd, [](ProjectExplorer::Project *project) { + dd->m_allProjectDirectoriesFilter.addDirectory(project->projectDirectory().toString()); + }); + connect(sessionManager, + &SessionManager::projectRemoved, + dd, + [](ProjectExplorer::Project *project) { + dd->m_allProjectDirectoriesFilter.removeDirectory( + project->projectDirectory().toString()); + }); ProjectTree *tree = &dd->m_projectTree; connect(tree, &ProjectTree::currentProjectChanged, @@ -652,7 +690,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er dd, &ProjectExplorerPluginPrivate::updateActions); connect(tree, &ProjectTree::currentProjectChanged, this, [](Project *project) { TextEditor::FindInFiles::instance()->setBaseDirectory(project ? project->projectDirectory() - : Utils::FileName()); + : Utils::FilePath()); }); // For JsonWizard: @@ -807,13 +845,11 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er mfileContextMenu->appendGroup(Constants::G_PROJECT_TREE); // Open Terminal submenu -#if !defined(Q_OS_UNIX) || QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) ActionContainer * const openTerminal = ActionManager::createMenu(ProjectExplorer::Constants::M_OPENTERMINALCONTEXT); openTerminal->setOnAllDisabledBehavior(ActionContainer::Show); dd->m_openTerminalMenu = openTerminal->menu(); - dd->m_openTerminalMenu->setTitle(FileUtils::msgTerminalAction()); -#endif + dd->m_openTerminalMenu->setTitle(FileUtils::msgTerminalWithAction()); // "open with" submenu ActionContainer * const openWith = @@ -881,27 +917,19 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er mfileContextMenu->addAction(cmd, Constants::G_FILE_OPEN); mfolderContextMenu->addAction(cmd, Constants::G_FOLDER_FILES); -#if !defined(Q_OS_UNIX) || QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) // Open Terminal Here menu - mfileContextMenu->addMenu(openTerminal, Constants::G_FILE_OPEN); - mfolderContextMenu->addMenu(openTerminal, Constants::G_FOLDER_FILES); - - dd->m_openTerminalHere = new QAction(tr("System Environment"), this); - cmd = ActionManager::registerAction(dd->m_openTerminalHere, Constants::OPENTERMINALHERE, - projecTreeContext); - dd->m_openTerminalMenu->addAction(dd->m_openTerminalHere); -#else - dd->m_openTerminalHere = new QAction(FileUtils::msgTerminalAction(), this); + dd->m_openTerminalHere = new QAction(FileUtils::msgTerminalHereAction(), this); cmd = ActionManager::registerAction(dd->m_openTerminalHere, Constants::OPENTERMINALHERE, projecTreeContext); mfileContextMenu->addAction(cmd, Constants::G_FILE_OPEN); mfolderContextMenu->addAction(cmd, Constants::G_FOLDER_FILES); -#endif + + mfileContextMenu->addMenu(openTerminal, Constants::G_FILE_OPEN); + mfolderContextMenu->addMenu(openTerminal, Constants::G_FOLDER_FILES); dd->m_openTerminalHereBuildEnv = new QAction(tr("Build Environment"), this); dd->m_openTerminalHereRunEnv = new QAction(tr("Run Environment"), this); -#if !defined(Q_OS_UNIX) || QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) cmd = ActionManager::registerAction(dd->m_openTerminalHereBuildEnv, "ProjectExplorer.OpenTerminalHereBuildEnv", projecTreeContext); @@ -911,7 +939,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er "ProjectExplorer.OpenTerminalHereRunEnv", projecTreeContext); dd->m_openTerminalMenu->addAction(dd->m_openTerminalHereRunEnv); -#endif // Open With menu mfileContextMenu->addMenu(openWith, Constants::G_FILE_OPEN); @@ -1141,6 +1168,13 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES); mfolderContextMenu->addAction(cmd, Constants::G_FOLDER_FILES); + // add existing projects action + dd->m_addExistingProjectsAction = new QAction(tr("Add Existing Projects..."), this); + cmd = ActionManager::registerAction(dd->m_addExistingProjectsAction, + "ProjectExplorer.AddExistingProjects", projecTreeContext); + mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES); + msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES); + // add existing directory action dd->m_addExistingDirectoryAction = new QAction(tr("Add Existing Directory..."), this); cmd = ActionManager::registerAction(dd->m_addExistingDirectoryAction, @@ -1306,18 +1340,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er s->value(QLatin1String("ProjectExplorer/Settings/DeployBeforeRun"), true).toBool(); dd->m_projectExplorerSettings.saveBeforeBuild = s->value(QLatin1String("ProjectExplorer/Settings/SaveBeforeBuild"), false).toBool(); - dd->m_projectExplorerSettings.showCompilerOutput = - s->value(QLatin1String("ProjectExplorer/Settings/ShowCompilerOutput"), false).toBool(); - dd->m_projectExplorerSettings.showRunOutput = - s->value(QLatin1String("ProjectExplorer/Settings/ShowRunOutput"), true).toBool(); - dd->m_projectExplorerSettings.showDebugOutput = - s->value(QLatin1String("ProjectExplorer/Settings/ShowDebugOutput"), false).toBool(); - dd->m_projectExplorerSettings.cleanOldAppOutput = - s->value(QLatin1String("ProjectExplorer/Settings/CleanOldAppOutput"), false).toBool(); - dd->m_projectExplorerSettings.mergeStdErrAndStdOut = - s->value(QLatin1String("ProjectExplorer/Settings/MergeStdErrAndStdOut"), false).toBool(); - dd->m_projectExplorerSettings.wrapAppOutput = - s->value(QLatin1String("ProjectExplorer/Settings/WrapAppOutput"), true).toBool(); dd->m_projectExplorerSettings.useJom = s->value(QLatin1String("ProjectExplorer/Settings/UseJom"), true).toBool(); dd->m_projectExplorerSettings.autorestoreLastSession = @@ -1328,12 +1350,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er s->value(QLatin1String("ProjectExplorer/Settings/PromptToStopRunControl"), false).toBool(); dd->m_projectExplorerSettings.automaticallyCreateRunConfigurations = s->value(QLatin1String("ProjectExplorer/Settings/AutomaticallyCreateRunConfigurations"), true).toBool(); - dd->m_projectExplorerSettings.maxAppOutputChars = - s->value(QLatin1String("ProjectExplorer/Settings/MaxAppOutputLines"), - Core::Constants::DEFAULT_MAX_CHAR_COUNT).toInt() * 100; - dd->m_projectExplorerSettings.maxBuildOutputChars = - s->value(QLatin1String("ProjectExplorer/Settings/MaxBuildOutputLines"), - Core::Constants::DEFAULT_MAX_CHAR_COUNT).toInt() * 100; dd->m_projectExplorerSettings.environmentId = QUuid(s->value(QLatin1String("ProjectExplorer/Settings/EnvironmentId")).toByteArray()); if (dd->m_projectExplorerSettings.environmentId.isNull()) @@ -1343,6 +1359,12 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er if (tmp < 0 || tmp > ProjectExplorerSettings::StopSameBuildDir) tmp = Utils::HostOsInfo::isWindowsHost() ? 1 : 0; dd->m_projectExplorerSettings.stopBeforeBuild = ProjectExplorerSettings::StopBeforeBuild(tmp); + dd->m_projectExplorerSettings.terminalMode = static_cast<TerminalMode>(s->value( + Constants::TERMINAL_MODE_SETTINGS_KEY, int(TerminalMode::Smart)).toInt()); + dd->m_projectExplorerSettings.closeSourceFilesWithProject + = s->value(Constants::CLOSE_FILES_WITH_PROJECT_SETTINGS_KEY, true).toBool(); + dd->m_projectExplorerSettings.clearIssuesOnRebuild + = s->value(Constants::CLEAR_ISSUES_ON_REBUILD_SETTINGS_KEY, true).toBool(); dd->m_projectExplorerSettings.buildDirectoryTemplate = s->value(Constants::DEFAULT_BUILD_DIRECTORY_TEMPLATE_KEY).toString(); if (dd->m_projectExplorerSettings.buildDirectoryTemplate.isEmpty()) @@ -1448,6 +1470,8 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er dd, &ProjectExplorerPluginPrivate::addExistingDirectory); connect(dd->m_addNewSubprojectAction, &QAction::triggered, dd, &ProjectExplorerPluginPrivate::addNewSubproject); + connect(dd->m_addExistingProjectsAction, &QAction::triggered, + dd, &ProjectExplorerPluginPrivate::addExistingProjects); connect(dd->m_removeProjectAction, &QAction::triggered, dd, &ProjectExplorerPluginPrivate::removeProject); connect(dd->m_openFileAction, &QAction::triggered, @@ -1459,11 +1483,11 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er connect(dd->m_openTerminalHere, &QAction::triggered, dd, []() { dd->openTerminalHere(sysEnv); }); connect(dd->m_openTerminalHereBuildEnv, &QAction::triggered, dd, []() { dd->openTerminalHere(buildEnv); }); - connect(dd->m_openTerminalHereRunEnv, &QAction::triggered, dd, []() { dd->openTerminalHere(runEnv); }); + connect(dd->m_openTerminalHereRunEnv, &QAction::triggered, dd, []() { dd->openTerminalHereWithRunEnv(); }); connect(dd->m_filePropertiesAction, &QAction::triggered, this, []() { - const Node *currentNode = ProjectTree::findCurrentNode(); - QTC_ASSERT(currentNode && currentNode->nodeType() == NodeType::File, return); + const Node *currentNode = ProjectTree::currentNode(); + QTC_ASSERT(currentNode && currentNode->asFileNode(), return); DocumentManager::showFilePropertiesDialog(currentNode->filePath()); }); connect(dd->m_removeFileAction, &QAction::triggered, @@ -1502,7 +1526,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er expander->registerFileVariables(Constants::VAR_CURRENTPROJECT_PREFIX, tr("Current project's main file."), []() -> QString { - Utils::FileName projectFilePath; + Utils::FilePath projectFilePath; if (Project *project = ProjectTree::currentProject()) projectFilePath = project->projectFilePath(); return projectFilePath.toString(); @@ -1547,7 +1571,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er tr("The host address of the device in the currently active kit."), []() -> QString { Kit *kit = currentKit(); - const IDevice::ConstPtr device = DeviceKitInformation::device(kit); + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); return device ? device->sshParameters().host() : QString(); }); @@ -1555,7 +1579,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er tr("The SSH port of the device in the currently active kit."), []() -> QString { Kit *kit = currentKit(); - const IDevice::ConstPtr device = DeviceKitInformation::device(kit); + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); return device ? QString::number(device->sshParameters().port()) : QString(); }); @@ -1563,7 +1587,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er tr("The username with which to log into the device in the currently active kit."), []() -> QString { Kit *kit = currentKit(); - const IDevice::ConstPtr device = DeviceKitInformation::device(kit); + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); return device ? device->sshParameters().userName() : QString(); }); @@ -1573,7 +1597,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er "in the currently active kit."), []() -> QString { Kit *kit = currentKit(); - const IDevice::ConstPtr device = DeviceKitInformation::device(kit); + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); return device ? device->sshParameters().privateKeyFile : QString(); }); @@ -1598,14 +1622,8 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er tr("The currently active run configuration's executable (if applicable)."), []() -> QString { if (Target *target = activeTarget()) { - if (RunConfiguration *rc = target->activeRunConfiguration()) { - // TODO: This duplicates code and is not always correct, but see - // QTCREATORBUG-18317. - // Solution: Re-introduce RunConfiguration::executable()? - if (auto executableAspect = rc->aspect<ExecutableAspect>()) - return executableAspect->executable().toString(); - return QString(); - } + if (RunConfiguration *rc = target->activeRunConfiguration()) + return rc->executable().toString(); } return QString(); }); @@ -1718,7 +1736,7 @@ void ProjectExplorerPlugin::unloadProject(Project *project) if (!DocumentManager::saveModifiedDocumentSilently(document)) return; - if (!dd->closeAllFilesInProject(project)) + if (projectExplorerSettings().closeSourceFilesWithProject && !dd->closeAllFilesInProject(project)) return; dd->addToRecentProjects(document->filePath().toString(), project->displayName()); @@ -1729,7 +1747,7 @@ void ProjectExplorerPlugin::unloadProject(Project *project) void ProjectExplorerPluginPrivate::closeAllProjects() { - if (!EditorManager::closeAllEditors()) + if (!EditorManager::closeAllDocuments()) return; // Action has been cancelled SessionManager::closeAllProjects(); @@ -1778,16 +1796,16 @@ void ProjectExplorerPlugin::extensionsInitialized() QSsh::SshSettings::loadSettings(Core::ICore::settings()); const auto searchPathRetriever = [] { - Utils::FileNameList searchPaths; - searchPaths << Utils::FileName::fromString(Core::ICore::libexecPath()); + Utils::FilePathList searchPaths; + searchPaths << Utils::FilePath::fromString(Core::ICore::libexecPath()); if (Utils::HostOsInfo::isWindowsHost()) { const QString gitBinary = Core::ICore::settings()->value("Git/BinaryPath", "git") .toString(); const QStringList rawGitSearchPaths = Core::ICore::settings()->value("Git/Path") .toString().split(':', QString::SkipEmptyParts); - const Utils::FileNameList gitSearchPaths = Utils::transform(rawGitSearchPaths, - [](const QString &rawPath) { return Utils::FileName::fromString(rawPath); }); - const Utils::FileName fullGitPath = Utils::Environment::systemEnvironment() + const Utils::FilePathList gitSearchPaths = Utils::transform(rawGitSearchPaths, + [](const QString &rawPath) { return Utils::FilePath::fromString(rawPath); }); + const Utils::FilePath fullGitPath = Utils::Environment::systemEnvironment() .searchInPath(gitBinary, gitSearchPaths); if (!fullGitPath.isEmpty()) { searchPaths << fullGitPath.parentDir() @@ -1798,6 +1816,16 @@ void ProjectExplorerPlugin::extensionsInitialized() }; QSsh::SshSettings::setExtraSearchPathRetriever(searchPathRetriever); + const auto parseIssuesAction = new QAction(tr("Parse Build Output..."), this); + ActionContainer *mtools = ActionManager::actionContainer(Core::Constants::M_TOOLS); + Command * const cmd = ActionManager::registerAction(parseIssuesAction, + "ProjectExplorer.ParseIssuesAction"); + connect(parseIssuesAction, &QAction::triggered, this, [] { + ParseIssuesDialog dlg(ICore::mainWindow()); + dlg.exec(); + }); + mtools->addAction(cmd); + // delay restoring kits until UI is shown for improved perceived startup performance QTimer::singleShot(0, this, &ProjectExplorerPlugin::restoreKits); } @@ -1808,7 +1836,7 @@ void ProjectExplorerPlugin::restoreKits() ExtraAbi::load(); // Load this before Toolchains! DeviceManager::instance()->load(); ToolChainManager::restoreToolChains(); - dd->m_kitManager->restoreKits(); + KitManager::restoreKits(); QTimer::singleShot(0, dd, &ProjectExplorerPluginPrivate::restoreSession); // delay a bit... } @@ -1874,15 +1902,15 @@ void ProjectExplorerPluginPrivate::setStartupProject(Project *project) bool ProjectExplorerPluginPrivate::closeAllFilesInProject(const Project *project) { QTC_ASSERT(project, return false); - QList<IDocument *> openFiles = DocumentModel::openedDocuments(); - Utils::erase(openFiles, [project](const IDocument *doc) { - return !project->isKnownFile(doc->filePath()); + QList<DocumentModel::Entry *> openFiles = DocumentModel::entries(); + Utils::erase(openFiles, [project](const DocumentModel::Entry *entry) { + return entry->pinned || !project->isKnownFile(entry->fileName()); }); for (const Project * const otherProject : SessionManager::projects()) { if (otherProject == project) continue; - Utils::erase(openFiles, [otherProject](const IDocument *doc) { - return otherProject->isKnownFile(doc->filePath()); + Utils::erase(openFiles, [otherProject](const DocumentModel::Entry *entry) { + return otherProject->isKnownFile(entry->fileName()); }); } return EditorManager::closeDocuments(openFiles); @@ -1920,20 +1948,18 @@ void ProjectExplorerPluginPrivate::savePersistentSettings() s->setValue(QLatin1String("ProjectExplorer/Settings/BuildBeforeDeploy"), dd->m_projectExplorerSettings.buildBeforeDeploy); s->setValue(QLatin1String("ProjectExplorer/Settings/DeployBeforeRun"), dd->m_projectExplorerSettings.deployBeforeRun); s->setValue(QLatin1String("ProjectExplorer/Settings/SaveBeforeBuild"), dd->m_projectExplorerSettings.saveBeforeBuild); - s->setValue(QLatin1String("ProjectExplorer/Settings/ShowCompilerOutput"), dd->m_projectExplorerSettings.showCompilerOutput); - s->setValue(QLatin1String("ProjectExplorer/Settings/ShowRunOutput"), dd->m_projectExplorerSettings.showRunOutput); - s->setValue(QLatin1String("ProjectExplorer/Settings/ShowDebugOutput"), dd->m_projectExplorerSettings.showDebugOutput); - s->setValue(QLatin1String("ProjectExplorer/Settings/CleanOldAppOutput"), dd->m_projectExplorerSettings.cleanOldAppOutput); - s->setValue(QLatin1String("ProjectExplorer/Settings/MergeStdErrAndStdOut"), dd->m_projectExplorerSettings.mergeStdErrAndStdOut); - s->setValue(QLatin1String("ProjectExplorer/Settings/WrapAppOutput"), dd->m_projectExplorerSettings.wrapAppOutput); s->setValue(QLatin1String("ProjectExplorer/Settings/UseJom"), dd->m_projectExplorerSettings.useJom); s->setValue(QLatin1String("ProjectExplorer/Settings/AutoRestoreLastSession"), dd->m_projectExplorerSettings.autorestoreLastSession); s->setValue(QLatin1String("ProjectExplorer/Settings/AddLibraryPathsToRunEnv"), dd->m_projectExplorerSettings.addLibraryPathsToRunEnv); s->setValue(QLatin1String("ProjectExplorer/Settings/PromptToStopRunControl"), dd->m_projectExplorerSettings.prompToStopRunControl); + s->setValue(Constants::TERMINAL_MODE_SETTINGS_KEY, + int(dd->m_projectExplorerSettings.terminalMode)); + s->setValue(Constants::CLOSE_FILES_WITH_PROJECT_SETTINGS_KEY, + dd->m_projectExplorerSettings.closeSourceFilesWithProject); + s->setValue(Constants::CLEAR_ISSUES_ON_REBUILD_SETTINGS_KEY, + dd->m_projectExplorerSettings.clearIssuesOnRebuild); s->setValue(QLatin1String("ProjectExplorer/Settings/AutomaticallyCreateRunConfigurations"), dd->m_projectExplorerSettings.automaticallyCreateRunConfigurations); - s->setValue(QLatin1String("ProjectExplorer/Settings/MaxAppOutputLines"), dd->m_projectExplorerSettings.maxAppOutputChars / 100); - s->setValue(QLatin1String("ProjectExplorer/Settings/MaxBuildOutputLines"), dd->m_projectExplorerSettings.maxBuildOutputChars / 100); s->setValue(QLatin1String("ProjectExplorer/Settings/EnvironmentId"), dd->m_projectExplorerSettings.environmentId.toByteArray()); s->setValue(QLatin1String("ProjectExplorer/Settings/StopBeforeBuild"), dd->m_projectExplorerSettings.stopBeforeBuild); @@ -2008,7 +2034,7 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(con QTC_ASSERT(!fileName.isEmpty(), continue); const QFileInfo fi(fileName); - const auto filePath = Utils::FileName::fromString(fi.absoluteFilePath()); + const auto filePath = Utils::FilePath::fromString(fi.absoluteFilePath()); Project *found = Utils::findOrDefault(SessionManager::projects(), Utils::equal(&Project::projectFilePath, filePath)); if (found) { @@ -2073,7 +2099,7 @@ void ProjectExplorerPluginPrivate::currentModeChanged(Id mode, Id oldMode) // Saving settings directly in a mode change is not a good idea, since the mode change // can be part of a bigger change. Save settings after that bigger change had a chance to // complete. - QTimer::singleShot(0, ICore::instance(), &ICore::saveSettings); + QTimer::singleShot(0, ICore::instance(), [] { ICore::saveSettings(ICore::ModeChanged); }); } if (mode == Core::Constants::MODE_WELCOME) updateWelcomePage(); @@ -2216,14 +2242,13 @@ void ProjectExplorerPluginPrivate::executeRunConfiguration(RunConfiguration *run } } - RunControl::WorkerCreator producer = RunControl::producer(runConfiguration, runMode); - QTC_ASSERT(producer, return); - auto runControl = new RunControl(runConfiguration, runMode); + auto runControl = new RunControl(runMode); + runControl->setRunConfiguration(runConfiguration); // A user needed interaction may have cancelled the run // (by example asking for a process pid or server url). - if (!producer(runControl)) { + if (!runControl->createMainWorker()) { delete runControl; return; } @@ -2250,8 +2275,8 @@ void ProjectExplorerPluginPrivate::startRunControl(RunControl *runControl) m_outputPane.flash(); // one flash for starting m_outputPane.showTabFor(runControl); Core::Id runMode = runControl->runMode(); - bool popup = (runMode == Constants::NORMAL_RUN_MODE && dd->m_projectExplorerSettings.showRunOutput) - || (runMode == Constants::DEBUG_RUN_MODE && m_projectExplorerSettings.showDebugOutput); + bool popup = (runMode == Constants::NORMAL_RUN_MODE && m_outputPane.settings().popUpForRunOutput) + || (runMode == Constants::DEBUG_RUN_MODE && m_outputPane.settings().popUpForDebugOutput); m_outputPane.setBehaviorOnOutput(runControl, popup ? AppOutputPane::Popup : AppOutputPane::Flash); connect(runControl, &QObject::destroyed, this, &ProjectExplorerPluginPrivate::checkForShutdown, Qt::QueuedConnection); @@ -2334,10 +2359,10 @@ QList<QPair<QString, QString> > ProjectExplorerPluginPrivate::recentProjects() c static QString pathOrDirectoryFor(const Node *node, bool dir) { - Utils::FileName path = node->filePath(); + Utils::FilePath path = node->filePath(); QString location; const FolderNode *folder = node->asFolderNode(); - if (node->nodeType() == NodeType::VirtualFolder && folder) { + if (node->isVirtualFolderType() && folder) { // Virtual Folder case // If there are files directly below or no subfolders, take the folder path if (!folder->fileNodes().isEmpty() || folder->folderNodes().isEmpty()) { @@ -2515,6 +2540,16 @@ bool ProjectExplorerPlugin::saveModifiedFiles() //NBS handle case where there is no activeBuildConfiguration // because someone delete all build configurations +ProjectExplorerPluginPrivate::ProjectExplorerPluginPrivate() + : m_allProjectDirectoriesFilter("Files in All Project Directories") +{ + m_allProjectDirectoriesFilter.setDisplayName(m_allProjectDirectoriesFilter.id().toString()); + m_allProjectDirectoriesFilter.setShortcutString("a"); // shared with "Files in Any Project" + m_allProjectDirectoriesFilter.setIncludedByDefault(false); // but not included in default + m_allProjectDirectoriesFilter.setFilters({}); + m_allProjectDirectoriesFilter.setIsCustomFilter(false); +} + void ProjectExplorerPluginPrivate::deploy(QList<Project *> projects) { QList<Id> steps; @@ -2561,11 +2596,11 @@ int ProjectExplorerPluginPrivate::queue(QList<Project *> projects, QList<Id> ste BuildConfiguration *bc = t ? t->activeBuildConfiguration() : nullptr; if (!bc) return false; - if (!Utils::FileName::fromString(rc->runnable().executable).isChildOf(bc->buildDirectory())) + if (!Utils::FilePath::fromString(rc->runnable().executable).isChildOf(bc->buildDirectory())) return false; IDevice::ConstPtr device = rc->runnable().device; if (device.isNull()) - device = DeviceKitInformation::device(t->kit()); + device = DeviceKitAspect::device(t->kit()); return !device.isNull() && device->type() == Core::Id(Constants::DESKTOP_DEVICE_TYPE); }); } @@ -2637,7 +2672,7 @@ void ProjectExplorerPlugin::buildProject(Project *p) void ProjectExplorerPluginPrivate::runProjectContextMenu() { - const Node *node = ProjectTree::findCurrentNode(); + const Node *node = ProjectTree::currentNode(); const ProjectNode *projectNode = node ? node->asProjectNode() : nullptr; if (projectNode == ProjectTree::currentProject()->rootProjectNode() || !projectNode) { m_instance->runProject(ProjectTree::currentProject(), Constants::NORMAL_RUN_MODE); @@ -3015,8 +3050,7 @@ bool ProjectExplorerPlugin::canRunStartupProject(Core::Id runMode, QString *whyN } // shouldn't actually be shown to the user... - RunControl::WorkerCreator producer = RunControl::producer(activeRC, runMode); - if (!producer) { + if (!RunControl::canRun(activeRC, runMode)) { if (whyNot) *whyNot = tr("Cannot run \"%1\".").arg(activeRC->displayName()); return false; @@ -3139,6 +3173,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions() m_addExistingDirectoryAction->setEnabled(false); m_addNewFileAction->setEnabled(false); m_addNewSubprojectAction->setEnabled(false); + m_addExistingProjectsAction->setEnabled(false); m_removeProjectAction->setEnabled(false); m_removeFileAction->setEnabled(false); m_duplicateFileAction->setEnabled(false); @@ -3150,6 +3185,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions() m_addExistingDirectoryAction->setVisible(true); m_addNewFileAction->setVisible(true); m_addNewSubprojectAction->setVisible(true); + m_addExistingProjectsAction->setVisible(true); m_removeProjectAction->setVisible(true); m_removeFileAction->setVisible(true); m_duplicateFileAction->setVisible(false); @@ -3168,7 +3204,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions() runMenu->menu()->clear(); runMenu->menu()->menuAction()->setVisible(false); - const Node *currentNode = ProjectTree::findCurrentNode(); + const Node *currentNode = ProjectTree::currentNode(); if (currentNode && currentNode->managingProject()) { ProjectNode *pn; @@ -3179,7 +3215,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions() Project *project = ProjectTree::currentProject(); m_openTerminalHereBuildEnv->setVisible(bool(buildEnv(project))); - m_openTerminalHereRunEnv->setVisible(bool(runEnv(project))); + m_openTerminalHereRunEnv->setVisible(canOpenTerminalWithRunEnv(project)); if (pn && project) { if (pn == project->rootProjectNode()) { @@ -3187,8 +3223,9 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions() } else { QList<RunConfiguration *> runConfigs; if (Target *t = project->activeTarget()) { + const QString buildKey = pn->buildKey(); for (RunConfiguration *rc : t->runConfigurations()) { - if (rc->canRunForNode(pn)) + if (rc->buildKey() == buildKey) runConfigs.append(rc); } } @@ -3217,10 +3254,12 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions() // Also handles ProjectNode m_addNewFileAction->setEnabled(supports(AddNewFile) && !ICore::isNewItemDialogRunning()); - m_addNewSubprojectAction->setEnabled(currentNode->nodeType() == NodeType::Project + m_addNewSubprojectAction->setEnabled(currentNode->isProjectNodeType() && supports(AddSubProject) && !ICore::isNewItemDialogRunning()); - m_removeProjectAction->setEnabled(currentNode->nodeType() == NodeType::Project + m_addExistingProjectsAction->setEnabled(currentNode->isProjectNodeType() + && supports(AddExistingProject)); + m_removeProjectAction->setEnabled(currentNode->isProjectNodeType() && supports(RemoveSubProject)); m_addExistingFilesAction->setEnabled(supports(AddExistingFile)); m_addExistingDirectoryAction->setEnabled(supports(AddExistingDirectory)); @@ -3265,6 +3304,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions() if (supports(HideFolderActions)) { m_addNewFileAction->setVisible(false); m_addNewSubprojectAction->setVisible(false); + m_addExistingProjectsAction->setVisible(false); m_removeProjectAction->setVisible(false); m_addExistingFilesAction->setVisible(false); m_addExistingDirectoryAction->setVisible(false); @@ -3289,7 +3329,7 @@ void ProjectExplorerPluginPrivate::updateLocationSubMenus() QTC_CHECK(folderMenu->actions().isEmpty()); const FolderNode *const fn - = ProjectTree::findCurrentNode() ? ProjectTree::findCurrentNode()->asFolderNode() : nullptr; + = ProjectTree::currentNode() ? ProjectTree::currentNode()->asFolderNode() : nullptr; const QList<FolderNode::LocationInfo> locations = fn ? fn->locationInfo() : QList<FolderNode::LocationInfo>(); @@ -3302,7 +3342,7 @@ void ProjectExplorerPluginPrivate::updateLocationSubMenus() for (const FolderNode::LocationInfo &li : locations) { const int line = li.line; - const Utils::FileName path = li.path; + const Utils::FilePath path = li.path; auto *action = new QAction(li.displayName, nullptr); connect(action, &QAction::triggered, this, [line, path]() { Core::EditorManager::openEditorAt(path.toString(), line); @@ -3317,7 +3357,7 @@ void ProjectExplorerPluginPrivate::updateLocationSubMenus() void ProjectExplorerPluginPrivate::addNewFile() { - Node* currentNode = ProjectTree::findCurrentNode(); + Node *currentNode = ProjectTree::currentNode(); QTC_ASSERT(currentNode, return); QString location = directoryFor(currentNode); @@ -3343,11 +3383,11 @@ void ProjectExplorerPluginPrivate::addNewFile() void ProjectExplorerPluginPrivate::addNewSubproject() { - Node* currentNode = ProjectTree::findCurrentNode(); + Node* currentNode = ProjectTree::currentNode(); QTC_ASSERT(currentNode, return); QString location = directoryFor(currentNode); - if (currentNode->nodeType() == NodeType::Project + if (currentNode->isProjectNodeType() && currentNode->supportsAction(AddSubProject, currentNode)) { QVariantMap map; map.insert(QLatin1String(Constants::PREFERRED_PROJECT_NODE), QVariant::fromValue(currentNode)); @@ -3371,9 +3411,50 @@ void ProjectExplorerPluginPrivate::addNewSubproject() } } +void ProjectExplorerPluginPrivate::addExistingProjects() +{ + Node * const currentNode = ProjectTree::currentNode(); + if (!currentNode) + return; + ProjectNode *projectNode = currentNode->asProjectNode(); + if (!projectNode && currentNode->asContainerNode()) + projectNode = currentNode->asContainerNode()->rootProjectNode(); + QTC_ASSERT(projectNode, return); + const QString dir = directoryFor(currentNode); + QStringList subProjectFilePaths = QFileDialog::getOpenFileNames( + ICore::mainWindow(), tr("Choose Project File"), dir, + projectNode->subProjectFileNamePatterns().join(";;")); + if (!ProjectTree::hasNode(projectNode)) + return; + const QList<Node *> childNodes = projectNode->nodes(); + Utils::erase(subProjectFilePaths, [childNodes](const QString &filePath) { + return Utils::anyOf(childNodes, [filePath](const Node *n) { + return n->filePath().toString() == filePath; + }); + }); + if (subProjectFilePaths.empty()) + return; + QStringList failedProjects; + QStringList addedProjects; + for (const QString &filePath : subProjectFilePaths) { + if (projectNode->addSubProject(filePath)) + addedProjects << filePath; + else + failedProjects << filePath; + } + if (!failedProjects.empty()) { + const QString message = tr("The following subprojects could not be added to project " + "\"%2\":").arg(projectNode->managingProject()->displayName()); + QMessageBox::warning(ICore::mainWindow(), tr("Adding Subproject Failed"), + message + "\n " + failedProjects.join("\n ")); + return; + } + VcsManager::promptToAdd(dir, addedProjects); +} + void ProjectExplorerPluginPrivate::handleAddExistingFiles() { - Node *node = ProjectTree::findCurrentNode(); + Node *node = ProjectTree::currentNode(); FolderNode *folderNode = node ? node->asFolderNode() : nullptr; QTC_ASSERT(folderNode, return); @@ -3388,19 +3469,17 @@ void ProjectExplorerPluginPrivate::handleAddExistingFiles() void ProjectExplorerPluginPrivate::addExistingDirectory() { - Node *node = ProjectTree::findCurrentNode(); + Node *node = ProjectTree::currentNode(); FolderNode *folderNode = node ? node->asFolderNode() : nullptr; QTC_ASSERT(folderNode, return); - SelectableFilesDialogAddDirectory dialog(Utils::FileName::fromString(directoryFor(node)), - Utils::FileNameList(), ICore::mainWindow()); - const QString addFileFilter = folderNode->addFileFilter(); - if (!addFileFilter.isEmpty()) - dialog.setAddFileFilter(addFileFilter); + SelectableFilesDialogAddDirectory dialog(Utils::FilePath::fromString(directoryFor(node)), + Utils::FilePathList(), ICore::mainWindow()); + dialog.setAddFileFilter({}); if (dialog.exec() == QDialog::Accepted) - ProjectExplorerPlugin::addExistingFiles(folderNode, Utils::transform(dialog.selectedFiles(), &Utils::FileName::toString)); + ProjectExplorerPlugin::addExistingFiles(folderNode, Utils::transform(dialog.selectedFiles(), &Utils::FilePath::toString)); } void ProjectExplorerPlugin::addExistingFiles(FolderNode *folderNode, const QStringList &filePaths) @@ -3430,7 +3509,7 @@ void ProjectExplorerPlugin::addExistingFiles(FolderNode *folderNode, const QStri void ProjectExplorerPluginPrivate::removeProject() { - Node *node = ProjectTree::findCurrentNode(); + Node *node = ProjectTree::currentNode(); if (!node) return; ProjectNode *projectNode = node->managingProject(); @@ -3444,28 +3523,28 @@ void ProjectExplorerPluginPrivate::removeProject() void ProjectExplorerPluginPrivate::openFile() { - const Node *currentNode = ProjectTree::findCurrentNode(); + const Node *currentNode = ProjectTree::currentNode(); QTC_ASSERT(currentNode, return); EditorManager::openEditor(currentNode->filePath().toString()); } void ProjectExplorerPluginPrivate::searchOnFileSystem() { - const Node *currentNode = ProjectTree::findCurrentNode(); + const Node *currentNode = ProjectTree::currentNode(); QTC_ASSERT(currentNode, return); TextEditor::FindInFiles::findOnFileSystem(pathFor(currentNode)); } void ProjectExplorerPluginPrivate::showInGraphicalShell() { - Node *currentNode = ProjectTree::findCurrentNode(); + Node *currentNode = ProjectTree::currentNode(); QTC_ASSERT(currentNode, return); FileUtils::showInGraphicalShell(ICore::mainWindow(), pathFor(currentNode)); } void ProjectExplorerPluginPrivate::openTerminalHere(const EnvironmentGetter &env) { - const Node *currentNode = ProjectTree::findCurrentNode(); + const Node *currentNode = ProjectTree::currentNode(); QTC_ASSERT(currentNode, return); const auto environment = env(ProjectTree::projectForNode(currentNode)); @@ -3475,21 +3554,46 @@ void ProjectExplorerPluginPrivate::openTerminalHere(const EnvironmentGetter &env FileUtils::openTerminal(directoryFor(currentNode), environment.value()); } +void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv() +{ + const Node *currentNode = ProjectTree::currentNode(); + QTC_ASSERT(currentNode, return); + + const Project * const project = ProjectTree::projectForNode(currentNode); + QTC_ASSERT(project, return); + const Target * const target = project->activeTarget(); + QTC_ASSERT(target, return); + const RunConfiguration * const runConfig = target->activeRunConfiguration(); + QTC_ASSERT(runConfig, return); + + const Runnable runnable = runConfig->runnable(); + IDevice::ConstPtr device = runnable.device; + if (!device) + device = DeviceKitAspect::device(target->kit()); + QTC_ASSERT(device && device->canOpenTerminal(), return); + const QString workingDir = device->type() == Constants::DESKTOP_DEVICE_TYPE + ? directoryFor(currentNode) : runnable.workingDirectory; + device->openTerminal(runnable.environment, workingDir); +} + void ProjectExplorerPluginPrivate::removeFile() { - const Node *currentNode = ProjectTree::findCurrentNode(); - QTC_ASSERT(currentNode && currentNode->nodeType() == NodeType::File, return); + const Node *currentNode = ProjectTree::currentNode(); + QTC_ASSERT(currentNode && currentNode->asFileNode(), return); - const Utils::FileName filePath = currentNode->filePath(); + const Utils::FilePath filePath = currentNode->filePath(); Utils::RemoveFileDialog removeFileDialog(filePath.toString(), ICore::mainWindow()); if (removeFileDialog.exec() == QDialog::Accepted) { const bool deleteFile = removeFileDialog.isDeleteFileChecked(); // Re-read the current node, in case the project is re-parsed while the dialog is open - if (currentNode != ProjectTree::findCurrentNode()) { - currentNode = ProjectTreeWidget::nodeForFile(filePath); - QTC_ASSERT(currentNode && currentNode->nodeType() == NodeType::File, return); + if (!ProjectTree::hasNode(currentNode)) { + QMessageBox::warning(ICore::mainWindow(), tr("Removing File Failed"), + tr("File %1 was not removed, because the project has changed " + "in the meantime.\nPlease try again.") + .arg(filePath.toUserOutput())); + return; } // remove from project @@ -3512,8 +3616,8 @@ void ProjectExplorerPluginPrivate::removeFile() void ProjectExplorerPluginPrivate::duplicateFile() { - Node *currentNode = ProjectTree::findCurrentNode(); - QTC_ASSERT(currentNode && currentNode->nodeType() == NodeType::File, return); + Node *currentNode = ProjectTree::currentNode(); + QTC_ASSERT(currentNode && currentNode->asFileNode(), return); FileNode *fileNode = currentNode->asFileNode(); QString filePath = currentNode->filePath().toString(); @@ -3543,8 +3647,8 @@ void ProjectExplorerPluginPrivate::duplicateFile() void ProjectExplorerPluginPrivate::deleteFile() { - Node *currentNode = ProjectTree::findCurrentNode(); - QTC_ASSERT(currentNode && currentNode->nodeType() == NodeType::File, return); + Node *currentNode = ProjectTree::currentNode(); + QTC_ASSERT(currentNode && currentNode->asFileNode(), return); FileNode *fileNode = currentNode->asFileNode(); @@ -3696,6 +3800,16 @@ const ProjectExplorerSettings &ProjectExplorerPlugin::projectExplorerSettings() return dd->m_projectExplorerSettings; } +void ProjectExplorerPlugin::setAppOutputSettings(const AppOutputSettings &settings) +{ + dd->m_outputPane.setSettings(settings); +} + +const AppOutputSettings &ProjectExplorerPlugin::appOutputSettings() +{ + return dd->m_outputPane.settings(); +} + QStringList ProjectExplorerPlugin::projectFilePatterns() { QStringList patterns; @@ -3707,7 +3821,7 @@ QStringList ProjectExplorerPlugin::projectFilePatterns() return patterns; } -bool ProjectExplorerPlugin::isProjectFile(const Utils::FileName &filePath) +bool ProjectExplorerPlugin::isProjectFile(const Utils::FilePath &filePath) { Utils::MimeType mt = Utils::mimeTypeForFile(filePath.toString()); for (const QString &mime : dd->m_projectCreators.keys()) { @@ -3737,16 +3851,6 @@ QString ProjectExplorerPlugin::buildDirectoryTemplate() return dd->m_projectExplorerSettings.buildDirectoryTemplate; } -/*! - Sets the current build directory template to \a directory. - - \sa defaultbuildDirectory -*/ -void ProjectExplorerPlugin::setBuildDirectoryTemplate(const QString &dir) -{ - dd->m_projectExplorerSettings.buildDirectoryTemplate = dir; -} - QString ProjectExplorerPlugin::defaultBuildDirectoryTemplate() { return QString(Constants::DEFAULT_BUILD_DIRECTORY_TEMPLATE); @@ -3758,12 +3862,12 @@ QList<QPair<QString, QString> > ProjectExplorerPlugin::recentProjects() } void ProjectManager::registerProjectCreator(const QString &mimeType, - const std::function<Project *(const Utils::FileName &)> &creator) + const std::function<Project *(const Utils::FilePath &)> &creator) { dd->m_projectCreators[mimeType] = creator; } -Project *ProjectManager::openProject(const Utils::MimeType &mt, const Utils::FileName &fileName) +Project *ProjectManager::openProject(const Utils::MimeType &mt, const Utils::FilePath &fileName) { if (mt.isValid()) { for (const QString &mimeType : dd->m_projectCreators.keys()) { diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h index e00fc393d0..23c2098216 100644 --- a/src/plugins/projectexplorer/projectexplorer.h +++ b/src/plugins/projectexplorer/projectexplorer.h @@ -45,7 +45,7 @@ class Id; namespace Utils { class ProcessHandle; -class FileName; +class FilePath; } namespace ProjectExplorer { @@ -56,7 +56,10 @@ class Node; class FolderNode; class FileNode; -namespace Internal { class ProjectExplorerSettings; } +namespace Internal { +class AppOutputSettings; +class ProjectExplorerSettings; +} class PROJECTEXPLORER_EXPORT ProjectExplorerPlugin : public ExtensionSystem::IPlugin { @@ -129,13 +132,16 @@ public: static void setProjectExplorerSettings(const Internal::ProjectExplorerSettings &pes); static const Internal::ProjectExplorerSettings &projectExplorerSettings(); + static void setAppOutputSettings(const Internal::AppOutputSettings &settings); + static const Internal::AppOutputSettings &appOutputSettings(); + static void startRunControl(RunControl *runControl); static void showRunErrorMessage(const QString &errorMessage); // internal public for FlatModel static void renameFile(Node *node, const QString &newFilePath); static QStringList projectFilePatterns(); - static bool isProjectFile(const Utils::FileName &filePath); + static bool isProjectFile(const Utils::FilePath &filePath); static QList<QPair<QString, QString> > recentProjects(); static bool canRunStartupProject(Core::Id runMode, QString *whyNot = nullptr); @@ -164,7 +170,6 @@ public: static void openOpenProjectDialog(); static QString buildDirectoryTemplate(); - static void setBuildDirectoryTemplate(const QString &dir); static QString defaultBuildDirectoryTemplate(); signals: @@ -252,6 +257,8 @@ private slots: void testProject_parsingSuccess(); void testProject_parsingFail(); void testProject_projectTree(); + + void testSessionSwitch(); #endif // WITH_TESTS }; diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro index 8ef32ae9ba..097fcb6da5 100644 --- a/src/plugins/projectexplorer/projectexplorer.pro +++ b/src/plugins/projectexplorer/projectexplorer.pro @@ -10,6 +10,7 @@ include(../../shared/clang/clang_defines.pri) HEADERS += projectexplorer.h \ abi.h \ abiwidget.h \ + addrunconfigdialog.h \ ansifilterparser.h \ buildinfo.h \ clangparser.h \ @@ -17,6 +18,8 @@ HEADERS += projectexplorer.h \ environmentaspect.h \ environmentaspectwidget.h \ extraabi.h \ + fileinsessionfinder.h \ + filterkitaspectsdialog.h \ gcctoolchain.h \ importwidget.h \ userfileaccessor.h \ @@ -31,9 +34,7 @@ HEADERS += projectexplorer.h \ targetsetupwidget.h \ kit.h \ kitchooser.h \ - kitconfigwidget.h \ kitinformation.h \ - kitinformationconfigwidget.h \ kitfeatureprovider.h \ kitmanager.h \ kitmanagerconfigwidget.h \ @@ -76,6 +77,7 @@ HEADERS += projectexplorer.h \ editorconfiguration.h \ editorsettingspropertiespage.h \ runconfiguration.h \ + runcontrol.h \ applicationlauncher.h \ runsettingspropertiespage.h \ projecttreewidget.h \ @@ -158,11 +160,14 @@ HEADERS += projectexplorer.h \ customexecutablerunconfiguration.h \ projectmacro.h \ makestep.h \ - projectconfigurationaspects.h + parseissuesdialog.h \ + projectconfigurationaspects.h \ + treescanner.h SOURCES += projectexplorer.cpp \ abi.cpp \ abiwidget.cpp \ + addrunconfigdialog.cpp \ ansifilterparser.cpp \ buildinfo.cpp \ clangparser.cpp \ @@ -170,6 +175,8 @@ SOURCES += projectexplorer.cpp \ environmentaspect.cpp \ environmentaspectwidget.cpp \ extraabi.cpp \ + fileinsessionfinder.cpp \ + filterkitaspectsdialog.cpp \ gcctoolchain.cpp \ importwidget.cpp \ projectconfigurationmodel.cpp \ @@ -184,9 +191,7 @@ SOURCES += projectexplorer.cpp \ targetsetupwidget.cpp \ kit.cpp \ kitchooser.cpp \ - kitconfigwidget.cpp \ kitinformation.cpp \ - kitinformationconfigwidget.cpp \ kitmanager.cpp \ kitmanagerconfigwidget.cpp \ kitmodel.cpp \ @@ -224,6 +229,7 @@ SOURCES += projectexplorer.cpp \ editorconfiguration.cpp \ editorsettingspropertiespage.cpp \ runconfiguration.cpp \ + runcontrol.cpp \ applicationlauncher.cpp \ runsettingspropertiespage.cpp \ projecttreewidget.cpp \ @@ -298,9 +304,11 @@ SOURCES += projectexplorer.cpp \ customexecutablerunconfiguration.cpp \ projectmacro.cpp \ makestep.cpp \ - projectconfigurationaspects.cpp + parseissuesdialog.cpp \ + projectconfigurationaspects.cpp \ + treescanner.cpp -FORMS += processstep.ui \ +FORMS += \ editorsettingspropertiespage.ui \ sessiondialog.ui \ projectwizardpage.ui \ @@ -344,3 +352,7 @@ journald { RESOURCES += projectexplorer.qrc DEFINES += PROJECTEXPLORER_LIBRARY + +!isEmpty(PROJECT_USER_FILE_EXTENSION) { + DEFINES += PROJECT_USER_FILE_EXTENSION=$${PROJECT_USER_FILE_EXTENSION} +} diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index 5836c06c45..395c1cced9 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -24,6 +24,7 @@ Project { "abi.cpp", "abi.h", "abiwidget.cpp", "abiwidget.h", "abstractprocessstep.cpp", "abstractprocessstep.h", + "addrunconfigdialog.cpp", "addrunconfigdialog.h", "allprojectsfilter.cpp", "allprojectsfilter.h", "allprojectsfind.cpp", "allprojectsfind.h", "ansifilterparser.cpp", "ansifilterparser.h", @@ -69,6 +70,8 @@ Project { "expanddata.cpp", "expanddata.h", "extraabi.cpp", "extraabi.h", "extracompiler.cpp", "extracompiler.h", + "fileinsessionfinder.cpp", "fileinsessionfinder.h", + "filterkitaspectsdialog.cpp", "filterkitaspectsdialog.h", "foldernavigationwidget.cpp", "foldernavigationwidget.h", "gccparser.cpp", "gccparser.h", "gcctoolchain.cpp", "gcctoolchain.h", @@ -81,10 +84,8 @@ Project { "itaskhandler.h", "kit.cpp", "kit.h", "kitchooser.cpp", "kitchooser.h", - "kitconfigwidget.cpp", "kitconfigwidget.h", "kitfeatureprovider.h", "kitinformation.cpp", "kitinformation.h", - "kitinformationconfigwidget.cpp", "kitinformationconfigwidget.h", "kitmanager.cpp", "kitmanager.h", "kitmanagerconfigwidget.cpp", "kitmanagerconfigwidget.h", "kitmodel.cpp", "kitmodel.h", @@ -98,8 +99,9 @@ Project { "namedwidget.cpp", "namedwidget.h", "osparser.cpp", "osparser.h", "panelswidget.cpp", "panelswidget.h", + "parseissuesdialog.cpp", "parseissuesdialog.h", "processparameters.cpp", "processparameters.h", - "processstep.cpp", "processstep.h", "processstep.ui", + "processstep.cpp", "processstep.h", "project.cpp", "project.h", "projectconfiguration.cpp", "projectconfiguration.h", "projectconfigurationaspects.cpp", "projectconfigurationaspects.h", @@ -127,6 +129,7 @@ Project { "projectwizardpage.cpp", "projectwizardpage.h", "projectwizardpage.ui", "removetaskhandler.cpp", "removetaskhandler.h", "runconfiguration.cpp", "runconfiguration.h", + "runcontrol.cpp", "runcontrol.h", "runconfigurationaspects.cpp", "runconfigurationaspects.h", "runsettingspropertiespage.cpp", "runsettingspropertiespage.h", "selectablefilesmodel.cpp", "selectablefilesmodel.h", @@ -151,6 +154,7 @@ Project { "toolchainmanager.cpp", "toolchainmanager.h", "toolchainoptionspage.cpp", "toolchainoptionspage.h", "toolchainsettingsaccessor.cpp", "toolchainsettingsaccessor.h", + "treescanner.cpp", "treescanner.h", "userfileaccessor.cpp", "userfileaccessor.h", "vcsannotatetaskhandler.cpp", "vcsannotatetaskhandler.h", "waitforstopdialog.cpp", "waitforstopdialog.h", diff --git a/src/plugins/projectexplorer/projectexplorer.qrc b/src/plugins/projectexplorer/projectexplorer.qrc index 9eb42e7ebc..29bda00509 100644 --- a/src/plugins/projectexplorer/projectexplorer.qrc +++ b/src/plugins/projectexplorer/projectexplorer.qrc @@ -73,6 +73,8 @@ <file>images/fileoverlay_cpp@2x.png</file> <file>images/fileoverlay_h.png</file> <file>images/fileoverlay_h@2x.png</file> + <file>images/fileoverlay_py.png</file> + <file>images/fileoverlay_py@2x.png</file> <file>images/fileoverlay_unknown.png</file> <file>images/fileoverlay_unknown@2x.png</file> <file>images/cancelbuild_overlay.png</file> diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h index 34e218b149..ad2d9e049e 100644 --- a/src/plugins/projectexplorer/projectexplorerconstants.h +++ b/src/plugins/projectexplorer/projectexplorerconstants.h @@ -124,6 +124,7 @@ const char DEVICE_SETTINGS_PAGE_ID[] = "AA.Device Settings"; const char TASK_CATEGORY_COMPILE[] = "Task.Category.Compile"; const char TASK_CATEGORY_BUILDSYSTEM[] = "Task.Category.Buildsystem"; const char TASK_CATEGORY_DEPLOYMENT[] = "Task.Category.Deploy"; +const char TASK_CATEGORY_AUTOTEST[] = "Task.Category.Autotest"; // Wizard categories const char QT_PROJECT_WIZARD_CATEGORY[] = "H.Project"; @@ -185,6 +186,7 @@ const char VAR_CURRENTBUILD_NAME[] = "CurrentBuild:Name"; const char VAR_CURRENTBUILD_TYPE[] = "CurrentBuild:Type"; const char VAR_CURRENTBUILD_ENV[] = "CurrentBuild:Env"; const char VAR_CURRENTRUN_NAME[] = "CurrentRun:Name"; +const char VAR_CURRENTRUN_WORKINGDIR[] = "CurrentRun:WorkingDir"; // JsonWizard: const char PAGE_ID_PREFIX[] = "PE.Wizard.Page."; @@ -209,9 +211,11 @@ const char FILEOVERLAY_QRC[]=":/projectexplorer/images/fileoverlay_qrc.png"; const char FILEOVERLAY_CPP[]=":/projectexplorer/images/fileoverlay_cpp.png"; const char FILEOVERLAY_H[]=":/projectexplorer/images/fileoverlay_h.png"; const char FILEOVERLAY_SCXML[]=":/projectexplorer/images/fileoverlay_scxml.png"; +const char FILEOVERLAY_PY[]=":/projectexplorer/images/fileoverlay_py.png"; const char FILEOVERLAY_UNKNOWN[]=":/projectexplorer/images/fileoverlay_unknown.png"; const char ADD_FILES_DIALOG_FILTER_HISTORY_KEY[] = "ProjectExplorer.AddFilesFilterKey"; +const char PROJECT_ROOT_PATH_KEY[] = "ProjectExplorer.Project.RootPath"; } // namespace Constants } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectexplorersettings.h b/src/plugins/projectexplorer/projectexplorersettings.h index 13277e9736..85e9a77abe 100644 --- a/src/plugins/projectexplorer/projectexplorersettings.h +++ b/src/plugins/projectexplorer/projectexplorersettings.h @@ -33,6 +33,8 @@ namespace ProjectExplorer { namespace Internal { +enum class TerminalMode { On, Off, Smart }; + class ProjectExplorerSettings { public: @@ -41,20 +43,15 @@ public: bool buildBeforeDeploy = true; bool deployBeforeRun = true; bool saveBeforeBuild = false; - bool showCompilerOutput = false; - bool showRunOutput = true; - bool showDebugOutput = false; - bool cleanOldAppOutput = false; - bool mergeStdErrAndStdOut = false; - bool wrapAppOutput = true; bool useJom = true; bool autorestoreLastSession = false; // This option is set in the Session Manager! bool prompToStopRunControl = false; bool automaticallyCreateRunConfigurations = true; bool addLibraryPathsToRunEnv = true; - int maxAppOutputChars = Core::Constants::DEFAULT_MAX_CHAR_COUNT; - int maxBuildOutputChars = Core::Constants::DEFAULT_MAX_CHAR_COUNT; + bool closeSourceFilesWithProject = true; + bool clearIssuesOnRebuild = true; StopBeforeBuild stopBeforeBuild = StopBeforeBuild::StopNone; + TerminalMode terminalMode = TerminalMode::Smart; QString buildDirectoryTemplate; // Add a UUid which is used to identify the development environment. @@ -68,23 +65,37 @@ inline bool operator==(const ProjectExplorerSettings &p1, const ProjectExplorerS return p1.buildBeforeDeploy == p2.buildBeforeDeploy && p1.deployBeforeRun == p2.deployBeforeRun && p1.saveBeforeBuild == p2.saveBeforeBuild - && p1.showCompilerOutput == p2.showCompilerOutput - && p1.showRunOutput == p2.showRunOutput - && p1.showDebugOutput == p2.showDebugOutput - && p1.cleanOldAppOutput == p2.cleanOldAppOutput - && p1.mergeStdErrAndStdOut == p2.mergeStdErrAndStdOut - && p1.wrapAppOutput == p2.wrapAppOutput && p1.useJom == p2.useJom && p1.autorestoreLastSession == p2.autorestoreLastSession && p1.prompToStopRunControl == p2.prompToStopRunControl && p1.automaticallyCreateRunConfigurations == p2.automaticallyCreateRunConfigurations && p1.addLibraryPathsToRunEnv == p2.addLibraryPathsToRunEnv - && p1.maxAppOutputChars == p2.maxAppOutputChars - && p1.maxBuildOutputChars == p2.maxBuildOutputChars && p1.environmentId == p2.environmentId && p1.stopBeforeBuild == p2.stopBeforeBuild + && p1.terminalMode == p2.terminalMode + && p1.closeSourceFilesWithProject == p2.closeSourceFilesWithProject + && p1.clearIssuesOnRebuild == p2.clearIssuesOnRebuild && p1.buildDirectoryTemplate == p2.buildDirectoryTemplate; } +class AppOutputSettings +{ +public: + bool popUpForRunOutput = true; + bool popUpForDebugOutput = false; + bool cleanOldOutput = false; + bool mergeChannels = false; + bool wrapOutput = false; + int maxCharCount = Core::Constants::DEFAULT_MAX_CHAR_COUNT; +}; + +class CompileOutputSettings +{ +public: + bool popUp = false; + bool wrapOutput = false; + int maxCharCount = Core::Constants::DEFAULT_MAX_CHAR_COUNT; +}; + } // namespace ProjectExplorer } // namespace Internal diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.cpp b/src/plugins/projectexplorer/projectexplorersettingspage.cpp index d7e506021c..73f622aedf 100644 --- a/src/plugins/projectexplorer/projectexplorersettingspage.cpp +++ b/src/plugins/projectexplorer/projectexplorersettingspage.cpp @@ -57,15 +57,14 @@ public: bool useProjectsDirectory(); void setUseProjectsDirectory(bool v); - QString buildDirectoryTemplate() const; - void setBuildDirectoryTemplate(const QString &bd); - private: void slotDirectoryButtonGroupChanged(); void resetBuildDirectoryTemplate(); void updateBuildDirectoryResetButton(); void setJomVisible(bool); + QString buildDirectoryTemplate() const; + void setBuildDirectoryTemplate(const QString &bd); Ui::ProjectExplorerSettingsPageUi m_ui; mutable ProjectExplorerSettings m_settings; @@ -79,7 +78,7 @@ ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget(QWidget *parent) : m_ui.directoryButtonGroup->setId(m_ui.currentDirectoryRadioButton, UseCurrentDirectory); m_ui.directoryButtonGroup->setId(m_ui.directoryRadioButton, UseProjectDirectory); - connect(m_ui.directoryButtonGroup, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), + connect(m_ui.directoryButtonGroup, QOverload<int>::of(&QButtonGroup::buttonClicked), this, &ProjectExplorerSettingsWidget::slotDirectoryButtonGroupChanged); connect(m_ui.buildDirectoryResetButton, &QAbstractButton::clicked, this, &ProjectExplorerSettingsWidget::resetBuildDirectoryTemplate); @@ -101,19 +100,15 @@ ProjectExplorerSettings ProjectExplorerSettingsWidget::settings() const m_settings.buildBeforeDeploy = m_ui.buildProjectBeforeDeployCheckBox->isChecked(); m_settings.deployBeforeRun = m_ui.deployProjectBeforeRunCheckBox->isChecked(); m_settings.saveBeforeBuild = m_ui.saveAllFilesCheckBox->isChecked(); - m_settings.showCompilerOutput = m_ui.showCompileOutputCheckBox->isChecked(); - m_settings.showRunOutput = m_ui.showRunOutputCheckBox->isChecked(); - m_settings.showDebugOutput = m_ui.showDebugOutputCheckBox->isChecked(); - m_settings.cleanOldAppOutput = m_ui.cleanOldAppOutputCheckBox->isChecked(); - m_settings.mergeStdErrAndStdOut = m_ui.mergeStdErrAndStdOutCheckBox->isChecked(); - m_settings.wrapAppOutput = m_ui.wrapAppOutputCheckBox->isChecked(); m_settings.useJom = m_ui.jomCheckbox->isChecked(); m_settings.addLibraryPathsToRunEnv = m_ui.addLibraryPathsToRunEnvCheckBox->isChecked(); m_settings.prompToStopRunControl = m_ui.promptToStopRunControlCheckBox->isChecked(); m_settings.automaticallyCreateRunConfigurations = m_ui.automaticallyCreateRunConfiguration->isChecked(); - m_settings.maxAppOutputChars = m_ui.maxAppOutputBox->value(); - m_settings.maxBuildOutputChars = m_ui.maxBuildOutputBox->value(); m_settings.stopBeforeBuild = static_cast<ProjectExplorerSettings::StopBeforeBuild>(m_ui.stopBeforeBuildComboBox->currentIndex()); + m_settings.terminalMode = static_cast<TerminalMode>(m_ui.terminalModeComboBox->currentIndex()); + m_settings.closeSourceFilesWithProject = m_ui.closeSourceFilesCheckBox->isChecked(); + m_settings.clearIssuesOnRebuild = m_ui.clearIssuesCheckBox->isChecked(); + m_settings.buildDirectoryTemplate = buildDirectoryTemplate(); return m_settings; } @@ -123,19 +118,15 @@ void ProjectExplorerSettingsWidget::setSettings(const ProjectExplorerSettings & m_ui.buildProjectBeforeDeployCheckBox->setChecked(m_settings.buildBeforeDeploy); m_ui.deployProjectBeforeRunCheckBox->setChecked(m_settings.deployBeforeRun); m_ui.saveAllFilesCheckBox->setChecked(m_settings.saveBeforeBuild); - m_ui.showCompileOutputCheckBox->setChecked(m_settings.showCompilerOutput); - m_ui.showRunOutputCheckBox->setChecked(m_settings.showRunOutput); - m_ui.showDebugOutputCheckBox->setChecked(m_settings.showDebugOutput); - m_ui.cleanOldAppOutputCheckBox->setChecked(m_settings.cleanOldAppOutput); - m_ui.mergeStdErrAndStdOutCheckBox->setChecked(m_settings.mergeStdErrAndStdOut); - m_ui.wrapAppOutputCheckBox->setChecked(m_settings.wrapAppOutput); m_ui.jomCheckbox->setChecked(m_settings.useJom); m_ui.addLibraryPathsToRunEnvCheckBox->setChecked(m_settings.addLibraryPathsToRunEnv); m_ui.promptToStopRunControlCheckBox->setChecked(m_settings.prompToStopRunControl); m_ui.automaticallyCreateRunConfiguration->setChecked(m_settings.automaticallyCreateRunConfigurations); - m_ui.maxAppOutputBox->setValue(m_settings.maxAppOutputChars); - m_ui.maxBuildOutputBox->setValue(m_settings.maxBuildOutputChars); m_ui.stopBeforeBuildComboBox->setCurrentIndex(static_cast<int>(m_settings.stopBeforeBuild)); + m_ui.terminalModeComboBox->setCurrentIndex(static_cast<int>(m_settings.terminalMode)); + m_ui.closeSourceFilesCheckBox->setChecked(m_settings.closeSourceFilesWithProject); + m_ui.clearIssuesCheckBox->setChecked(m_settings.clearIssuesOnRebuild); + setBuildDirectoryTemplate(pes.buildDirectoryTemplate); } QString ProjectExplorerSettingsWidget::projectsDirectory() const @@ -205,7 +196,6 @@ QWidget *ProjectExplorerSettingsPage::widget() m_widget->setSettings(ProjectExplorerPlugin::projectExplorerSettings()); m_widget->setProjectsDirectory(Core::DocumentManager::projectsDirectory().toString()); m_widget->setUseProjectsDirectory(Core::DocumentManager::useProjectsDirectory()); - m_widget->setBuildDirectoryTemplate(ProjectExplorerPlugin::buildDirectoryTemplate()); } return m_widget; } @@ -215,9 +205,8 @@ void ProjectExplorerSettingsPage::apply() if (m_widget) { ProjectExplorerPlugin::setProjectExplorerSettings(m_widget->settings()); Core::DocumentManager::setProjectsDirectory( - Utils::FileName::fromString(m_widget->projectsDirectory())); + Utils::FilePath::fromString(m_widget->projectsDirectory())); Core::DocumentManager::setUseProjectsDirectory(m_widget->useProjectsDirectory()); - ProjectExplorerPlugin::setBuildDirectoryTemplate(m_widget->buildDirectoryTemplate()); } } diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.ui b/src/plugins/projectexplorer/projectexplorersettingspage.ui index 7fecbfb38e..448ad3655b 100644 --- a/src/plugins/projectexplorer/projectexplorersettingspage.ui +++ b/src/plugins/projectexplorer/projectexplorersettingspage.ui @@ -6,11 +6,11 @@ <rect> <x>0</x> <y>0</y> - <width>831</width> - <height>520</height> + <width>994</width> + <height>808</height> </rect> </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> + <layout class="QVBoxLayout" name="verticalLayout_4"> <item> <widget class="QGroupBox" name="directoryGroupBox"> <property name="title"> @@ -47,126 +47,56 @@ </widget> </item> <item> - <widget class="QGroupBox" name="buildAndRunGroupBox"> + <widget class="QGroupBox" name="closeProjectGroupBox"> <property name="title"> - <string>Build and Run</string> + <string>Closing Projects</string> </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0"> - <widget class="QCheckBox" name="saveAllFilesCheckBox"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QCheckBox" name="closeSourceFilesCheckBox"> <property name="text"> - <string>Save all files before build</string> + <string>Close source files along with project</string> </property> </widget> </item> - <item row="0" column="1"> - <widget class="QCheckBox" name="cleanOldAppOutputCheckBox"> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="buildAndRunGroupBox"> + <property name="title"> + <string>Build and Run</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QCheckBox" name="saveAllFilesCheckBox"> <property name="text"> - <string>Clear old application output on a new run</string> + <string>Save all files before build</string> </property> </widget> </item> - <item row="1" column="0"> + <item> <widget class="QCheckBox" name="buildProjectBeforeDeployCheckBox"> <property name="text"> <string>Always build project before deploying it</string> </property> </widget> </item> - <item row="1" column="1"> - <widget class="QCheckBox" name="mergeStdErrAndStdOutCheckBox"> - <property name="toolTip"> - <string>Enabling this option ensures that the order of interleaved messages from stdout and stderr is preserved, at the cost of disabling highlighting of stderr.</string> - </property> - <property name="text"> - <string>Merge stderr and stdout</string> - </property> - </widget> - </item> - <item row="2" column="0"> + <item> <widget class="QCheckBox" name="deployProjectBeforeRunCheckBox"> <property name="text"> <string>Always deploy project before running it</string> </property> </widget> </item> - <item row="2" column="1"> - <widget class="QCheckBox" name="wrapAppOutputCheckBox"> - <property name="text"> - <string>Word-wrap application output</string> - </property> - </widget> - </item> - <item row="3" column="1"> + <item> <widget class="QCheckBox" name="addLibraryPathsToRunEnvCheckBox"> <property name="text"> <string>Add linker library search paths to run environment</string> </property> </widget> </item> - <item row="3" column="0"> - <widget class="QCheckBox" name="showCompileOutputCheckBox"> - <property name="text"> - <string>Open Compile Output pane when building</string> - </property> - </widget> - </item> - <item row="4" column="1"> - <widget class="QWidget" name="widget" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout"> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="limitBuildOutputLabel"> - <property name="text"> - <string>Limit build output to</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="maxBuildOutputBox"> - <property name="minimum"> - <number>5000</number> - </property> - <property name="maximum"> - <number>100000000</number> - </property> - <property name="singleStep"> - <number>5000</number> - </property> - <property name="value"> - <number>10000000</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="limitBuildOutputLabel_2"> - <property name="text"> - <string>characters</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item row="4" column="0"> - <widget class="QCheckBox" name="showRunOutputCheckBox"> - <property name="text"> - <string>Open Application Output pane on output when running</string> - </property> - </widget> - </item> - <item row="5" column="1"> + <item> <widget class="QWidget" name="widget_1" native="true"> <layout class="QHBoxLayout" name="horizontalLayout_5"> <property name="leftMargin"> @@ -181,47 +111,10 @@ <property name="bottomMargin"> <number>0</number> </property> - <item> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Limit application output to</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="maxAppOutputBox"> - <property name="minimum"> - <number>5000</number> - </property> - <property name="maximum"> - <number>100000000</number> - </property> - <property name="singleStep"> - <number>5000</number> - </property> - <property name="value"> - <number>10000000</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>characters</string> - </property> - </widget> - </item> </layout> </widget> </item> - <item row="5" column="0"> - <widget class="QCheckBox" name="showDebugOutputCheckBox"> - <property name="text"> - <string>Open Application Output pane on output when debugging</string> - </property> - </widget> - </item> - <item row="6" column="0"> + <item> <widget class="QCheckBox" name="promptToStopRunControlCheckBox"> <property name="toolTip"> <string>Asks before terminating the running application in response to clicking the stop button in Application Output.</string> @@ -231,7 +124,7 @@ </property> </widget> </item> - <item row="7" column="0"> + <item> <widget class="QCheckBox" name="automaticallyCreateRunConfiguration"> <property name="toolTip"> <string>Creates suitable run configurations automatically when setting up a new kit.</string> @@ -241,55 +134,109 @@ </property> </widget> </item> - <item row="8" column="0" colspan="2"> - <layout class="QHBoxLayout" name="horizontalLayout_4"> - <item> + <item> + <widget class="QCheckBox" name="clearIssuesCheckBox"> + <property name="text"> + <string>Clear issues list on new build</string> + </property> + </widget> + </item> + <item> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> <widget class="QLabel" name="labelStopBeforeBuild"> <property name="text"> <string>Stop applications before building:</string> </property> </widget> </item> - <item> - <widget class="QComboBox" name="stopBeforeBuildComboBox"> + <item row="0" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout"> <item> - <property name="text"> - <string>None</string> - </property> + <widget class="QComboBox" name="stopBeforeBuildComboBox"> + <item> + <property name="text"> + <string>None</string> + </property> + </item> + <item> + <property name="text"> + <string>Same Project</string> + </property> + </item> + <item> + <property name="text"> + <string>All</string> + </property> + </item> + <item> + <property name="text"> + <string>Same Build Directory</string> + </property> + </item> + </widget> </item> <item> - <property name="text"> - <string>Same Project</string> - </property> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> </item> + </layout> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="terminalModeLabel"> + <property name="text"> + <string>Default for "Run in terminal":</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> - <property name="text"> - <string>All</string> - </property> + <widget class="QComboBox" name="terminalModeComboBox"> + <item> + <property name="text"> + <string>Enabled</string> + </property> + </item> + <item> + <property name="text"> + <string>Disabled</string> + </property> + </item> + <item> + <property name="text"> + <string>Deduced From Project</string> + </property> + </item> + </widget> </item> <item> - <property name="text"> - <string>Same Build Directory</string> - </property> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> </item> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> + </layout> </item> </layout> </item> - <item row="9" column="0" colspan="2"> + <item> <layout class="QVBoxLayout" name="verticalLayout"> <property name="spacing"> <number>0</number> @@ -322,7 +269,7 @@ </item> </layout> </item> - <item row="10" column="0" colspan="2"> + <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <property name="topMargin"> <number>12</number> @@ -347,17 +294,14 @@ </layout> </item> </layout> + <zorder>widget_1</zorder> + <zorder>automaticallyCreateRunConfiguration</zorder> <zorder>saveAllFilesCheckBox</zorder> <zorder>buildProjectBeforeDeployCheckBox</zorder> <zorder>deployProjectBeforeRunCheckBox</zorder> - <zorder>showCompileOutputCheckBox</zorder> - <zorder>cleanOldAppOutputCheckBox</zorder> - <zorder>mergeStdErrAndStdOutCheckBox</zorder> - <zorder>wrapAppOutputCheckBox</zorder> - <zorder>widget</zorder> <zorder>promptToStopRunControlCheckBox</zorder> - <zorder>showRunOutputCheckBox</zorder> - <zorder>showDebugOutputCheckBox</zorder> + <zorder>addLibraryPathsToRunEnvCheckBox</zorder> + <zorder>clearIssuesCheckBox</zorder> </widget> </item> <item> diff --git a/src/plugins/projectexplorer/projectfilewizardextension.cpp b/src/plugins/projectexplorer/projectfilewizardextension.cpp index 5dab7bbf99..33be4cdcfe 100644 --- a/src/plugins/projectexplorer/projectfilewizardextension.cpp +++ b/src/plugins/projectexplorer/projectfilewizardextension.cpp @@ -256,7 +256,7 @@ void ProjectFileWizardExtension::applyCodeStyle(GeneratedFile *file) const Indenter *indenter = nullptr; if (factory) { indenter = factory->createIndenter(&doc); - indenter->setFileName(Utils::FileName::fromString(file->path())); + indenter->setFileName(Utils::FilePath::fromString(file->path())); } if (!indenter) indenter = new NormalIndenter(&doc); diff --git a/src/plugins/projectexplorer/projectimporter.cpp b/src/plugins/projectexplorer/projectimporter.cpp index 174f6a7472..4d32dba9d6 100644 --- a/src/plugins/projectexplorer/projectimporter.cpp +++ b/src/plugins/projectexplorer/projectimporter.cpp @@ -71,9 +71,9 @@ static bool hasOtherUsers(Core::Id id, const QVariant &v, Kit *k) }); } -ProjectImporter::ProjectImporter(const Utils::FileName &path) : m_projectPath(path) +ProjectImporter::ProjectImporter(const Utils::FilePath &path) : m_projectPath(path) { - useTemporaryKitInformation(ToolChainKitInformation::id(), + useTemporaryKitAspect(ToolChainKitAspect::id(), [this](Kit *k, const QVariantList &vl) { cleanupTemporaryToolChains(k, vl); }, [this](Kit *k, const QVariantList &vl) { persistTemporaryToolChains(k, vl); }); } @@ -84,7 +84,7 @@ ProjectImporter::~ProjectImporter() removeProject(k); } -const QList<BuildInfo> ProjectImporter::import(const Utils::FileName &importPath, bool silent) +const QList<BuildInfo> ProjectImporter::import(const Utils::FilePath &importPath, bool silent) { QList<BuildInfo> result; @@ -97,12 +97,20 @@ const QList<BuildInfo> ProjectImporter::import(const Utils::FileName &importPath return result; } - const Utils::FileName absoluteImportPath = Utils::FileName::fromString(fi.absoluteFilePath()); + const Utils::FilePath absoluteImportPath = Utils::FilePath::fromString(fi.absoluteFilePath()); + const auto handleFailure = [this, importPath, silent] { + if (silent) + return; + QMessageBox::critical(Core::ICore::mainWindow(), tr("No Build Found"), + tr("No build found in %1 matching project %2.") + .arg(importPath.toUserOutput(), projectFilePath().toUserOutput())); + }; qCDebug(log) << "Examining directory" << absoluteImportPath.toString(); QList<void *> dataList = examineDirectory(absoluteImportPath); if (dataList.isEmpty()) { qCDebug(log) << "Nothing to import found in" << absoluteImportPath.toString(); + handleFailure(); return result; } @@ -141,11 +149,8 @@ const QList<BuildInfo> ProjectImporter::import(const Utils::FileName &importPath deleteDirectoryData(dd); dataList.clear(); - if (result.isEmpty() && !silent) - QMessageBox::critical(Core::ICore::mainWindow(), - QCoreApplication::translate("ProjectExplorer::ProjectImporter", "No Build Found"), - QCoreApplication::translate("ProjectExplorer::ProjectImporter", "No build found in %1 matching project %2.") - .arg(importPath.toUserOutput()).arg(projectFilePath().toUserOutput())); + if (result.isEmpty()) + handleFailure(); return result; } @@ -167,7 +172,7 @@ Target *ProjectImporter::preferredTarget(const QList<Target *> &possibleTargets) return t; if (pickedFallback) continue; - if (DeviceTypeKitInformation::deviceTypeId(t->kit()) == Constants::DESKTOP_DEVICE_TYPE) { + if (DeviceTypeKitAspect::deviceTypeId(t->kit()) == Constants::DESKTOP_DEVICE_TYPE) { activeTarget = t; pickedFallback = true; } @@ -286,27 +291,18 @@ bool ProjectImporter::isTemporaryKit(Kit *k) const Kit *ProjectImporter::createTemporaryKit(const KitSetupFunction &setup) const { - auto k = std::make_unique<Kit>(); - Kit *kptr = k.get(); UpdateGuard guard(*this); - { - KitGuard kitGuard(kptr); - k->setUnexpandedDisplayName(QCoreApplication::translate("ProjectExplorer::ProjectImporter", "Imported Kit"));; - - // Set up values: - foreach (KitInformation *ki, KitManager::kitInformation()) - ki->setup(kptr); - - setup(kptr); - - foreach (KitInformation *ki, KitManager::kitInformation()) - ki->fix(kptr); - - markKitAsTemporary(kptr); - addProject(kptr); - } // ~KitGuard, sending kitUpdated - KitManager::registerKit(std::move(k)); // potentially adds kits to other targetsetuppages - return kptr; + const auto init = [&](Kit *k) { + KitGuard kitGuard(k); + k->setUnexpandedDisplayName(QCoreApplication::translate("ProjectExplorer::ProjectImporter", + "Imported Kit")); + k->setup(); + setup(k); + k->fix(); + markKitAsTemporary(k); + addProject(k); + }; // ~KitGuard, sending kitUpdated + return KitManager::registerKit(init); // potentially adds kits to other targetsetuppages } bool ProjectImporter::findTemporaryHandler(Core::Id id) const @@ -326,7 +322,7 @@ void ProjectImporter::cleanupTemporaryToolChains(Kit *k, const QVariantList &vl) ToolChain *tc = toolChainFromVariant(v); QTC_ASSERT(tc, continue); ToolChainManager::deregisterToolChain(tc); - ToolChainKitInformation::setToolChain(k, nullptr); + ToolChainKitAspect::setToolChain(k, nullptr); } } @@ -335,13 +331,13 @@ void ProjectImporter::persistTemporaryToolChains(Kit *k, const QVariantList &vl) for (const QVariant &v : vl) { ToolChain *tmpTc = toolChainFromVariant(v); QTC_ASSERT(tmpTc, continue); - ToolChain *actualTc = ToolChainKitInformation::toolChain(k, tmpTc->language()); + ToolChain *actualTc = ToolChainKitAspect::toolChain(k, tmpTc->language()); if (tmpTc && actualTc != tmpTc) ToolChainManager::deregisterToolChain(tmpTc); } } -void ProjectImporter::useTemporaryKitInformation(Core::Id id, +void ProjectImporter::useTemporaryKitAspect(Core::Id id, ProjectImporter::CleanupFunction cleanup, ProjectImporter::PersistFunction persist) { @@ -371,7 +367,7 @@ bool ProjectImporter::hasKitWithTemporaryData(Core::Id id, const QVariant &data) } static ProjectImporter::ToolChainData -createToolChains(const Utils::FileName &toolChainPath, const Core::Id &language) +createToolChains(const Utils::FilePath &toolChainPath, const Core::Id &language) { ProjectImporter::ToolChainData data; @@ -391,7 +387,7 @@ createToolChains(const Utils::FileName &toolChainPath, const Core::Id &language) } ProjectImporter::ToolChainData -ProjectImporter::findOrCreateToolChains(const Utils::FileName &toolChainPath, +ProjectImporter::findOrCreateToolChains(const Utils::FilePath &toolChainPath, const Core::Id &language) const { ToolChainData result; @@ -400,7 +396,7 @@ ProjectImporter::findOrCreateToolChains(const Utils::FileName &toolChainPath, }); for (const ToolChain *tc : result.tcs) { const QByteArray tcId = tc->id(); - result.areTemporary = result.areTemporary ? true : hasKitWithTemporaryData(ToolChainKitInformation::id(), tcId); + result.areTemporary = result.areTemporary ? true : hasKitWithTemporaryData(ToolChainKitAspect::id(), tcId); } if (!result.tcs.isEmpty()) return result; diff --git a/src/plugins/projectexplorer/projectimporter.h b/src/plugins/projectexplorer/projectimporter.h index c7df542a7a..f1cf482607 100644 --- a/src/plugins/projectexplorer/projectimporter.h +++ b/src/plugins/projectexplorer/projectimporter.h @@ -44,19 +44,20 @@ class ToolChain; // Documentation inside. class PROJECTEXPLORER_EXPORT ProjectImporter : public QObject { + Q_OBJECT public: struct ToolChainData { QList<ToolChain *> tcs; bool areTemporary = false; }; - ProjectImporter(const Utils::FileName &path); + ProjectImporter(const Utils::FilePath &path); ~ProjectImporter() override; - const Utils::FileName projectFilePath() const { return m_projectPath; } - const Utils::FileName projectDirectory() const { return m_projectPath.parentDir(); } + const Utils::FilePath projectFilePath() const { return m_projectPath; } + const Utils::FilePath projectDirectory() const { return m_projectPath.parentDir(); } - virtual const QList<BuildInfo> import(const Utils::FileName &importPath, bool silent = false); + virtual const QList<BuildInfo> import(const Utils::FilePath &importPath, bool silent = false); virtual QStringList importCandidates() = 0; virtual Target *preferredTarget(const QList<Target *> &possibleTargets); @@ -87,7 +88,7 @@ protected: }; // importPath is an existing directory at this point! - virtual QList<void *> examineDirectory(const Utils::FileName &importPath) const = 0; + virtual QList<void *> examineDirectory(const Utils::FilePath &importPath) const = 0; // will get one of the results from examineDirectory virtual bool matchKit(void *directoryData, const Kit *k) const = 0; // will get one of the results from examineDirectory @@ -103,13 +104,13 @@ protected: // Handle temporary additions to Kits (Qt Versions, ToolChains, etc.) using CleanupFunction = std::function<void(Kit *, const QVariantList &)>; using PersistFunction = std::function<void(Kit *, const QVariantList &)>; - void useTemporaryKitInformation(Core::Id id, + void useTemporaryKitAspect(Core::Id id, CleanupFunction cleanup, PersistFunction persist); void addTemporaryData(Core::Id id, const QVariant &cleanupData, Kit *k) const; // Does *any* kit feature the requested data yet? bool hasKitWithTemporaryData(Core::Id id, const QVariant &data) const; - ToolChainData findOrCreateToolChains(const Utils::FileName &toolChainPath, const Core::Id &language) const; + ToolChainData findOrCreateToolChains(const Utils::FilePath &toolChainPath, const Core::Id &language) const; private: void markKitAsTemporary(Kit *k) const; @@ -118,7 +119,7 @@ private: void cleanupTemporaryToolChains(ProjectExplorer::Kit *k, const QVariantList &vl); void persistTemporaryToolChains(ProjectExplorer::Kit *k, const QVariantList &vl); - const Utils::FileName m_projectPath; + const Utils::FilePath m_projectPath; mutable bool m_isUpdating = false; class TemporaryInformationHandler { diff --git a/src/plugins/projectexplorer/projectmanager.h b/src/plugins/projectexplorer/projectmanager.h index e504b1968e..c0f91cebc7 100644 --- a/src/plugins/projectexplorer/projectmanager.h +++ b/src/plugins/projectexplorer/projectmanager.h @@ -32,7 +32,7 @@ #include <functional> namespace Utils { -class FileName; +class FilePath; class MimeType; } // Utils @@ -44,19 +44,19 @@ class PROJECTEXPLORER_EXPORT ProjectManager { public: static bool canOpenProjectForMimeType(const Utils::MimeType &mt); - static Project *openProject(const Utils::MimeType &mt, const Utils::FileName &fileName); + static Project *openProject(const Utils::MimeType &mt, const Utils::FilePath &fileName); template <typename T> static void registerProjectType(const QString &mimeType) { - ProjectManager::registerProjectCreator(mimeType, [](const Utils::FileName &fileName) { + ProjectManager::registerProjectCreator(mimeType, [](const Utils::FilePath &fileName) { return new T(fileName); }); } private: static void registerProjectCreator(const QString &mimeType, - const std::function<Project *(const Utils::FileName &)> &); + const std::function<Project *(const Utils::FilePath &)> &); }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp index d69d9aad6a..c3df0688ae 100644 --- a/src/plugins/projectexplorer/projectmodels.cpp +++ b/src/plugins/projectexplorer/projectmodels.cpp @@ -32,17 +32,31 @@ #include "session.h" #include "target.h" +#include <coreplugin/documentmanager.h> #include <coreplugin/fileiconprovider.h> +#include <coreplugin/icore.h> +#include <coreplugin/iversioncontrol.h> +#include <coreplugin/vcsmanager.h> #include <utils/utilsicons.h> #include <utils/algorithm.h> #include <utils/dropsupport.h> +#include <utils/pathchooser.h> #include <utils/stringutils.h> #include <utils/theme/theme.h> +#include <QButtonGroup> +#include <QDialog> +#include <QDialogButtonBox> #include <QFileInfo> #include <QFont> +#include <QHBoxLayout> +#include <QLabel> +#include <QMessageBox> #include <QMimeData> #include <QLoggingCategory> +#include <QPushButton> +#include <QRadioButton> +#include <QVBoxLayout> #include <functional> @@ -188,6 +202,8 @@ Qt::ItemFlags FlatModel::flags(const QModelIndex &index) const // either folder or file node if (node->supportsAction(Rename, node)) f = f | Qt::ItemIsEditable; + } else if (node->supportsAction(ProjectAction::AddExistingFile, node)) { + f |= Qt::ItemIsDropEnabled; } } return f; @@ -203,8 +219,8 @@ bool FlatModel::setData(const QModelIndex &index, const QVariant &value, int rol Node *node = nodeForIndex(index); QTC_ASSERT(node, return false); - Utils::FileName orgFilePath = node->filePath(); - Utils::FileName newFilePath = orgFilePath.parentDir().appendPath(value.toString()); + const Utils::FilePath orgFilePath = node->filePath(); + const Utils::FilePath newFilePath = orgFilePath.parentDir().pathAppended(value.toString()); ProjectExplorerPlugin::renameFile(node, newFilePath.toString()); emit renamed(orgFilePath, newFilePath); @@ -245,7 +261,7 @@ void FlatModel::addOrRebuildProjectModel(Project *project) if (container->childCount() == 0) { auto projectFileNode = std::make_unique<FileNode>(project->projectFilePath(), - FileType::Project, false); + FileType::Project); seen.insert(projectFileNode.get()); container->appendChild(new WrapperNode(projectFileNode.get())); project->containerNode()->addNestedNode(std::move(projectFileNode)); @@ -409,7 +425,7 @@ QStringList FlatModel::mimeTypes() const QMimeData *FlatModel::mimeData(const QModelIndexList &indexes) const { - auto data = new Utils::DropMimeData; + auto data = new DropMimeData; foreach (const QModelIndex &index, indexes) { if (Node *node = nodeForIndex(index)) { if (node->asFileNode()) @@ -420,6 +436,303 @@ QMimeData *FlatModel::mimeData(const QModelIndexList &indexes) const return data; } +bool FlatModel::canDropMimeData(const QMimeData *data, Qt::DropAction, int, int, + const QModelIndex &) const +{ + // For now, we support only drops of Qt Creator file nodes. + const auto * const dropData = dynamic_cast<const DropMimeData *>(data); + if (!dropData) + return false; + QTC_ASSERT(!dropData->values().empty(), return false); + return dropData->files().size() == dropData->values().size(); +} + +enum class DropAction { Copy, CopyWithFiles, Move, MoveWithFiles }; + +class DropFileDialog : public QDialog +{ + Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::Internal::FlatModel) +public: + DropFileDialog(const FilePath &defaultTargetDir) + : m_buttonBox(new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)), + m_buttonGroup(new QButtonGroup(this)) + { + setWindowTitle(tr("Choose Drop Action")); + const bool offerFileIo = !defaultTargetDir.isEmpty(); + auto * const layout = new QVBoxLayout(this); + layout->addWidget(new QLabel(tr("You just dragged some files from one project node to " + "another.\nWhat should Qt Creator do now?"), this)); + auto * const copyButton = new QRadioButton(this); + m_buttonGroup->addButton(copyButton, int(DropAction::Copy)); + layout->addWidget(copyButton); + auto * const moveButton = new QRadioButton(this); + m_buttonGroup->addButton(moveButton, int(DropAction::Move)); + layout->addWidget(moveButton); + if (offerFileIo) { + copyButton->setText(tr("Copy Only File References")); + moveButton->setText(tr("Move Only File References")); + auto * const copyWithFilesButton + = new QRadioButton(tr("Copy file references and files"), this); + m_buttonGroup->addButton(copyWithFilesButton, int(DropAction::CopyWithFiles)); + layout->addWidget(copyWithFilesButton); + auto * const moveWithFilesButton + = new QRadioButton(tr("Move file references and files"), this); + m_buttonGroup->addButton(moveWithFilesButton, int(DropAction::MoveWithFiles)); + layout->addWidget(moveWithFilesButton); + moveWithFilesButton->setChecked(true); + auto * const targetDirLayout = new QHBoxLayout; + layout->addLayout(targetDirLayout); + targetDirLayout->addWidget(new QLabel(tr("Target directory:"), this)); + m_targetDirChooser = new PathChooser(this); + m_targetDirChooser->setExpectedKind(PathChooser::ExistingDirectory); + m_targetDirChooser->setFileName(defaultTargetDir); + connect(m_targetDirChooser, &PathChooser::validChanged, this, [this](bool valid) { + m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid); + }); + targetDirLayout->addWidget(m_targetDirChooser); + connect(m_buttonGroup, QOverload<int>::of(&QButtonGroup::buttonClicked), this, [this] { + switch (dropAction()) { + case DropAction::CopyWithFiles: + case DropAction::MoveWithFiles: + m_targetDirChooser->setEnabled(true); + m_buttonBox->button(QDialogButtonBox::Ok) + ->setEnabled(m_targetDirChooser->isValid()); + break; + case DropAction::Copy: + case DropAction::Move: + m_targetDirChooser->setEnabled(false); + m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); + break; + } + }); + } else { + copyButton->setText(tr("Copy File References")); + moveButton->setText(tr("Move File References")); + moveButton->setChecked(true); + } + connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + layout->addWidget(m_buttonBox); + } + + DropAction dropAction() const { return static_cast<DropAction>(m_buttonGroup->checkedId()); } + FilePath targetDir() const + { + return m_targetDirChooser ? m_targetDirChooser->fileName() : FilePath(); + } + +private: + PathChooser *m_targetDirChooser = nullptr; + QDialogButtonBox * const m_buttonBox; + QButtonGroup * const m_buttonGroup; +}; + + +bool FlatModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, + const QModelIndex &parent) +{ + Q_UNUSED(action); + + const auto * const dropData = dynamic_cast<const DropMimeData *>(data); + QTC_ASSERT(dropData, return false); + + auto fileNodes = transform<QList<const Node *>>(dropData->values(), + [](const QVariant &v) { return v.value<Node *>(); }); + QTC_ASSERT(!fileNodes.empty(), return true); + + // The drag operation does not block event handling, so it's possible that the project + // was reparsed and the nodes in the drop data are now invalid. If that happens for any node, + // we chicken out and abort the entire operation. + // Note: In theory, it might be possible that the memory was reused in such an unlucky + // way that the pointers refer to different project nodes now, but... + if (!allOf(fileNodes, [](const Node *n) { return ProjectTree::hasNode(n); })) + return true; + + // We handle only proper file nodes, i.e. no project or folder nodes and no "pseudo" + // file nodes that represent the project file. + fileNodes = filtered(fileNodes, [](const Node *n) { + return n->asFileNode() && n->asFileNode()->fileType() != FileType::Project; + }); + if (fileNodes.empty()) + return true; + + // We can handle more than one file being dropped, as long as they have the same parent node. + ProjectNode * const sourceProjectNode = fileNodes.first()->parentProjectNode(); + QTC_ASSERT(sourceProjectNode, return true); + if (anyOf(fileNodes, [sourceProjectNode](const Node *n) { + return n->parentProjectNode() != sourceProjectNode; })) { + return true; + } + Node *targetNode = nodeForIndex(index(row, column, parent)); + if (!targetNode) + targetNode = nodeForIndex(parent); + QTC_ASSERT(targetNode, return true); + ProjectNode *targetProjectNode = targetNode->asProjectNode(); + if (!targetProjectNode) + targetProjectNode = targetNode->parentProjectNode(); + QTC_ASSERT(targetProjectNode, return true); + if (sourceProjectNode == targetProjectNode) + return true; + + // Node weirdness: Sometimes the "file path" is a directory, sometimes it's a file... + const auto dirForProjectNode = [](const ProjectNode *pNode) { + const FilePath dir = pNode->filePath(); + if (dir.toFileInfo().isDir()) + return dir; + return FilePath::fromString(dir.toFileInfo().path()); + }; + FilePath targetDir = dirForProjectNode(targetProjectNode); + + // Ask the user what to do now: Copy or add? With or without file transfer? + DropFileDialog dlg(targetDir == dirForProjectNode(sourceProjectNode) ? FilePath() : targetDir); + if (dlg.exec() != QDialog::Accepted) + return true; + if (!dlg.targetDir().isEmpty()) + targetDir = dlg.targetDir(); + + // Check the nodes again. + if (!allOf(fileNodes, [](const Node *n) { return ProjectTree::hasNode(n); })) + return true; + + // Some helper functions for the file operations. + const auto targetFilePath = [&targetDir](const QString &sourceFilePath) { + return targetDir.pathAppended(QFileInfo(sourceFilePath).fileName()).toString(); + }; + + struct VcsInfo { + Core::IVersionControl *vcs = nullptr; + QString repoDir; + bool operator==(const VcsInfo &other) const { + return vcs == other.vcs && repoDir == other.repoDir; + } + }; + QHash<QString, VcsInfo> vcsHash; + const auto vcsInfoForFile = [&vcsHash](const QString &filePath) { + const QString dir = QFileInfo(filePath).path(); + const auto it = vcsHash.constFind(dir); + if (it != vcsHash.constEnd()) + return it.value(); + VcsInfo vcsInfo; + vcsInfo.vcs = Core::VcsManager::findVersionControlForDirectory(dir, &vcsInfo.repoDir); + vcsHash.insert(dir, vcsInfo); + return vcsInfo; + }; + + // Now do the actual work. + const QStringList sourceFiles = transform(fileNodes, [](const Node *n) { + return n->filePath().toString(); + }); + QStringList failedRemoveFromProject; + QStringList failedAddToProject; + QStringList failedCopyOrMove; + QStringList failedDelete; + QStringList failedVcsOp; + switch (dlg.dropAction()) { + case DropAction::CopyWithFiles: { + QStringList filesToAdd; + Core::IVersionControl * const vcs = Core::VcsManager::findVersionControlForDirectory( + targetDir.toString()); + const bool addToVcs = vcs && vcs->supportsOperation(Core::IVersionControl::AddOperation); + for (const QString &sourceFile : sourceFiles) { + const QString targetFile = targetFilePath(sourceFile); + if (QFile::copy(sourceFile, targetFile)) { + filesToAdd << targetFile; + if (addToVcs && !vcs->vcsAdd(targetFile)) + failedVcsOp << targetFile; + } else { + failedCopyOrMove << sourceFile; + } + } + targetProjectNode->addFiles(filesToAdd, &failedAddToProject); + break; + } + case DropAction::Copy: + targetProjectNode->addFiles(sourceFiles, &failedAddToProject); + break; + case DropAction::MoveWithFiles: { + QStringList filesToAdd; + QStringList filesToRemove; + const VcsInfo targetVcs = vcsInfoForFile(targetDir.toString()); + const bool vcsAddPossible = targetVcs.vcs + && targetVcs.vcs->supportsOperation(Core::IVersionControl::AddOperation); + for (const QString &sourceFile : sourceFiles) { + const QString targetFile = targetFilePath(sourceFile); + const VcsInfo sourceVcs = vcsInfoForFile(sourceFile); + if (sourceVcs.vcs && targetVcs.vcs && sourceVcs == targetVcs + && sourceVcs.vcs->supportsOperation(Core::IVersionControl::MoveOperation)) { + if (sourceVcs.vcs->vcsMove(sourceFile, targetFile)) { + filesToAdd << targetFile; + filesToRemove << sourceFile; + } else { + failedCopyOrMove << sourceFile; + } + continue; + } + if (!QFile::copy(sourceFile, targetFile)) { + failedCopyOrMove << sourceFile; + continue; + } + filesToAdd << targetFile; + filesToRemove << sourceFile; + Core::FileChangeBlocker changeGuard(sourceFile); + if (sourceVcs.vcs && sourceVcs.vcs->supportsOperation( + Core::IVersionControl::DeleteOperation) + && !sourceVcs.vcs->vcsDelete(sourceFile)) { + failedVcsOp << sourceFile; + } + if (QFile::exists(sourceFile) && !QFile::remove(sourceFile)) + failedDelete << sourceFile; + if (vcsAddPossible && !targetVcs.vcs->vcsAdd(targetFile)) + failedVcsOp << targetFile; + } + sourceProjectNode->removeFiles(filesToRemove, &failedRemoveFromProject); + targetProjectNode->addFiles(filesToAdd, &failedAddToProject); + break; + } + case DropAction::Move: + sourceProjectNode->removeFiles(sourceFiles, &failedRemoveFromProject); + targetProjectNode->addFiles(sourceFiles, &failedAddToProject); + break; + } + + // Summary for the user in case anything went wrong. + const auto makeUserFileList = [](const QStringList &files) { + return transform(files, [](const QString &f) { return QDir::toNativeSeparators(f); }) + .join("\n "); + }; + if (!failedAddToProject.empty() || !failedRemoveFromProject.empty() + || !failedCopyOrMove.empty() || !failedDelete.empty() || !failedVcsOp.empty()) { + QString message = tr("Not all operations finished successfully."); + if (!failedCopyOrMove.empty()) { + message.append('\n').append(tr("The following files could not be copied or moved:")) + .append("\n ").append(makeUserFileList(failedCopyOrMove)); + } + if (!failedRemoveFromProject.empty()) { + message.append('\n').append(tr("The following files could not be removed from the " + "project file:")) + .append("\n ").append(makeUserFileList(failedRemoveFromProject)); + } + if (!failedAddToProject.empty()) { + message.append('\n').append(tr("The following files could not be added to the " + "project file:")) + .append("\n ").append(makeUserFileList(failedAddToProject)); + } + if (!failedDelete.empty()) { + message.append('\n').append(tr("The following files could not be deleted:")) + .append("\n ").append(makeUserFileList(failedDelete)); + } + if (!failedVcsOp.empty()) { + message.append('\n').append(tr("A version control operation failed for the following " + "files. Please check your repository.")) + .append("\n ").append(makeUserFileList(failedVcsOp)); + } + QMessageBox::warning(Core::ICore::mainWindow(), tr("Failure Updating Project"), + message); + } + + return true; +} + WrapperNode *FlatModel::wrapperForNode(const Node *node) const { return findNonRootItem([node](WrapperNode *item) { diff --git a/src/plugins/projectexplorer/projectmodels.h b/src/plugins/projectexplorer/projectmodels.h index e406360f34..78753bf1da 100644 --- a/src/plugins/projectexplorer/projectmodels.h +++ b/src/plugins/projectexplorer/projectmodels.h @@ -67,6 +67,10 @@ public: Qt::DropActions supportedDragActions() const override; QStringList mimeTypes() const override; QMimeData *mimeData(const QModelIndexList &indexes) const override; + bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, + const QModelIndex &parent) const override; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, + const QModelIndex &parent) override; Node *nodeForIndex(const QModelIndex &index) const; WrapperNode *wrapperForNode(const Node *node) const; @@ -83,7 +87,7 @@ public: void onCollapsed(const QModelIndex &idx); signals: - void renamed(const Utils::FileName &oldName, const Utils::FileName &newName); + void renamed(const Utils::FilePath &oldName, const Utils::FilePath &newName); void requestExpansion(const QModelIndex &index); private: diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp index 286899b1b7..e6825e98be 100644 --- a/src/plugins/projectexplorer/projectnodes.cpp +++ b/src/plugins/projectexplorer/projectnodes.cpp @@ -52,7 +52,7 @@ namespace ProjectExplorer { -static FolderNode *folderNode(const FolderNode *folder, const Utils::FileName &directory) +static FolderNode *folderNode(const FolderNode *folder, const Utils::FilePath &directory) { return static_cast<FolderNode *>(Utils::findOrDefault(folder->folderNodes(), [&directory](const FolderNode *fn) { @@ -61,13 +61,13 @@ static FolderNode *folderNode(const FolderNode *folder, const Utils::FileName &d } static FolderNode *recursiveFindOrCreateFolderNode(FolderNode *folder, - const Utils::FileName &directory, - const Utils::FileName &overrideBaseDir, + const Utils::FilePath &directory, + const Utils::FilePath &overrideBaseDir, const FolderNode::FolderNodeFactory &factory) { - Utils::FileName path = overrideBaseDir.isEmpty() ? folder->filePath() : overrideBaseDir; + Utils::FilePath path = overrideBaseDir.isEmpty() ? folder->filePath() : overrideBaseDir; - Utils::FileName directoryWithoutPrefix; + Utils::FilePath directoryWithoutPrefix; bool isRelative = false; if (path.isEmpty() || path.toFileInfo().isRoot()) { @@ -89,7 +89,7 @@ static FolderNode *recursiveFindOrCreateFolderNode(FolderNode *folder, ProjectExplorer::FolderNode *parent = folder; foreach (const QString &part, parts) { - path.appendPath(part); + path = path.pathAppended(part); // Find folder in subFolders FolderNode *next = folderNode(parent, path); if (!next) { @@ -119,15 +119,23 @@ static FolderNode *recursiveFindOrCreateFolderNode(FolderNode *folder, \sa ProjectExplorer::NodesWatcher */ -Node::Node(NodeType nodeType, const Utils::FileName &filePath, int line, const QByteArray &id) : - m_filePath(filePath), m_nodeId(id), m_line(line), m_nodeType(nodeType) -{ } +Node::Node() = default; void Node::setPriority(int p) { m_priority = p; } +void Node::setFilePath(const Utils::FilePath &filePath) +{ + m_filePath = filePath; +} + +void Node::setLine(int line) +{ + m_line = line; +} + void Node::setListInProject(bool l) { if (l) @@ -144,7 +152,7 @@ void Node::setIsGenerated(bool g) m_flags = static_cast<NodeFlag>(m_flags & ~FlagIsGenerated); } -void Node::setAbsoluteFilePathAndLine(const Utils::FileName &path, int line) +void Node::setAbsoluteFilePathAndLine(const Utils::FilePath &path, int line) { if (m_filePath == path && m_line == line) return; @@ -155,11 +163,6 @@ void Node::setAbsoluteFilePathAndLine(const Utils::FileName &path, int line) Node::~Node() = default; -NodeType Node::nodeType() const -{ - return m_nodeType; -} - int Node::priority() const { return m_priority; @@ -212,7 +215,7 @@ const ProjectNode *Node::managingProject() const /*! The path of the file or folder in the filesystem the node represents. */ -const Utils::FileName &Node::filePath() const +const Utils::FilePath &Node::filePath() const { return m_filePath; } @@ -222,11 +225,6 @@ int Node::line() const return m_line; } -QByteArray Node::id() const -{ - return m_nodeId; -} - QString Node::displayName() const { return filePath().fileName(); @@ -299,7 +297,7 @@ FileType Node::fileTypeForMimeType(const Utils::MimeType &mt) return type; } -FileType Node::fileTypeForFileName(const Utils::FileName &file) +FileType Node::fileTypeForFileName(const Utils::FilePath &file) { return fileTypeForMimeType(Utils::mimeTypeForFile(file.toString(), Utils::MimeMatchMode::MatchExtension)); @@ -315,13 +313,11 @@ FileType Node::fileTypeForFileName(const Utils::FileName &file) \sa ProjectExplorer::FolderNode, ProjectExplorer::ProjectNode */ -FileNode::FileNode(const Utils::FileName &filePath, const FileType fileType, bool generated, - int line, const QByteArray &id) : - Node(NodeType::File, filePath, line, id), +FileNode::FileNode(const Utils::FilePath &filePath, const FileType fileType) : m_fileType(fileType) { + setFilePath(filePath); setListInProject(true); - setIsGenerated(generated); if (fileType == FileType::Project) setPriority(DefaultProjectFilePriority); else @@ -330,7 +326,9 @@ FileNode::FileNode(const Utils::FileName &filePath, const FileType fileType, boo FileNode *FileNode::clone() const { - auto fn = new FileNode(filePath(), fileType(), isGenerated(), line(), id()); + auto fn = new FileNode(filePath(), fileType()); + fn->setLine(line()); + fn->setIsGenerated(isGenerated()); fn->setEnabled(isEnabled()); fn->setPriority(priority()); fn->setListInProject(listInProject()); @@ -342,8 +340,8 @@ FileType FileNode::fileType() const return m_fileType; } -static QList<FileNode *> scanForFilesRecursively(const Utils::FileName &directory, - const std::function<FileNode *(const Utils::FileName &)> factory, +static QList<FileNode *> scanForFilesRecursively(const Utils::FilePath &directory, + const std::function<FileNode *(const Utils::FilePath &)> factory, QSet<QString> &visited, QFutureInterface<QList<FileNode*>> *future, double progressStart, double progressRange, const QList<Core::IVersionControl*> &versionControls) @@ -358,7 +356,7 @@ static QList<FileNode *> scanForFilesRecursively(const Utils::FileName &director if (visitedCount == visited.count()) return result; - const QList<QFileInfo> entries = baseDir.entryInfoList(QStringList(), QDir::AllEntries|QDir::NoDotAndDotDot); + const QFileInfoList entries = baseDir.entryInfoList(QStringList(), QDir::AllEntries|QDir::NoDotAndDotDot); double progress = 0; const double progressIncrement = progressRange / static_cast<double>(entries.count()); int lastIntProgress = 0; @@ -366,7 +364,7 @@ static QList<FileNode *> scanForFilesRecursively(const Utils::FileName &director if (future && future->isCanceled()) return result; - const Utils::FileName entryName = Utils::FileName::fromString(entry.absoluteFilePath()); + const Utils::FilePath entryName = Utils::FilePath::fromString(entry.absoluteFilePath()); if (!Utils::contains(versionControls, [&entryName](const Core::IVersionControl *vc) { return vc->isVcsFileOrDirectory(entryName); })) { @@ -390,8 +388,8 @@ static QList<FileNode *> scanForFilesRecursively(const Utils::FileName &director } QList<FileNode *> -FileNode::scanForFiles(const Utils::FileName &directory, - const std::function<FileNode *(const Utils::FileName &)> factory, +FileNode::scanForFiles(const Utils::FilePath &directory, + const std::function<FileNode *(const Utils::FilePath &)> factory, QFutureInterface<QList<FileNode *>> *future) { QSet<QString> visited; @@ -409,6 +407,14 @@ bool FileNode::supportsAction(ProjectAction action, const Node *node) const return parentFolder && parentFolder->supportsAction(action, node); } +QString FileNode::displayName() const +{ + int l = line(); + if (l < 0) + return Node::displayName(); + return Node::displayName() + ':' + QString::number(l); +} + /*! \class ProjectExplorer::FolderNode @@ -416,16 +422,13 @@ bool FileNode::supportsAction(ProjectAction action, const Node *node) const \sa ProjectExplorer::FileNode, ProjectExplorer::ProjectNode */ -FolderNode::FolderNode(const Utils::FileName &folderPath, NodeType nodeType, - const QString &displayName, const QByteArray &id) : - Node(nodeType, folderPath, -1, id), - m_displayName(displayName) +FolderNode::FolderNode(const Utils::FilePath &folderPath) { + setFilePath(folderPath); setPriority(DefaultFolderPriority); setListInProject(false); setIsGenerated(false); - if (m_displayName.isEmpty()) - m_displayName = folderPath.toUserOutput(); + m_displayName = folderPath.toUserOutput(); } /*! @@ -556,7 +559,7 @@ QList<FileNode*> FolderNode::fileNodes() const return result; } -FileNode *FolderNode::fileNode(const Utils::FileName &file) const +FileNode *FolderNode::fileNode(const Utils::FilePath &file) const { return static_cast<FileNode *>(Utils::findOrDefault(m_nodes, [&file](const std::unique_ptr<Node> &n) { @@ -576,7 +579,7 @@ QList<FolderNode*> FolderNode::folderNodes() const } void FolderNode::addNestedNode(std::unique_ptr<FileNode> &&fileNode, - const Utils::FileName &overrideBaseDir, + const Utils::FilePath &overrideBaseDir, const FolderNodeFactory &factory) { FolderNode *folder = recursiveFindOrCreateFolderNode(this, fileNode->filePath().parentDir(), @@ -585,7 +588,7 @@ void FolderNode::addNestedNode(std::unique_ptr<FileNode> &&fileNode, } void FolderNode::addNestedNodes(std::vector<std::unique_ptr<FileNode> > &&files, - const Utils::FileName &overrideBaseDir, + const Utils::FilePath &overrideBaseDir, const FolderNode::FolderNodeFactory &factory) { for (std::unique_ptr<FileNode> &f : files) @@ -599,8 +602,12 @@ void FolderNode::addNestedNodes(std::vector<std::unique_ptr<FileNode> > &&files, void FolderNode::compress() { if (auto subFolder = m_nodes.size() == 1 ? m_nodes.at(0)->asFolderNode() : nullptr) { - if (subFolder->nodeType() != nodeType()) + const bool sameType = (isFolderNodeType() && subFolder->isFolderNodeType()) + || (isProjectNodeType() && subFolder->isProjectNodeType()) + || (isVirtualFolderType() && subFolder->isVirtualFolderType()); + if (!sameType) return; + // Only one subfolder: Compress! setDisplayName(QDir::toNativeSeparators(displayName() + "/" + subFolder->displayName())); for (Node *n : subFolder->nodes()) { @@ -619,16 +626,6 @@ void FolderNode::compress() } } -bool FolderNode::isAncesterOf(Node *n) -{ - if (n == this) - return true; - FolderNode *p = n->parentFolderNode(); - while (p && p != this) - p = p->parentFolderNode(); - return p == this; -} - bool FolderNode::replaceSubtree(Node *oldNode, std::unique_ptr<Node> &&newNode) { std::unique_ptr<Node> keepAlive; @@ -676,6 +673,9 @@ const QList<FolderNode::LocationInfo> FolderNode::locationInfo() const QString FolderNode::addFileFilter() const { + if (!m_addFileFilter.isNull()) + return m_addFileFilter; + FolderNode *fn = parentFolderNode(); return fn ? fn->addFileFilter() : QString(); } @@ -762,7 +762,7 @@ bool FolderNode::showInSimpleTree() const bool FolderNode::showWhenEmpty() const { - return false; + return m_showWhenEmpty; } /*! @@ -776,18 +776,9 @@ bool FolderNode::showWhenEmpty() const \sa ProjectExplorer::FileNode, ProjectExplorer::ProjectNode */ -VirtualFolderNode::VirtualFolderNode(const Utils::FileName &folderPath, int priority, - const QByteArray &id) : - FolderNode(folderPath, NodeType::VirtualFolder, QString(), id) +VirtualFolderNode::VirtualFolderNode(const Utils::FilePath &folderPath) : + FolderNode(folderPath) { - setPriority(priority); -} - -QString VirtualFolderNode::addFileFilter() const -{ - if (!m_addFileFilter.isNull()) - return m_addFileFilter; - return FolderNode::addFileFilter(); } /*! @@ -803,11 +794,12 @@ QString VirtualFolderNode::addFileFilter() const /*! Creates an uninitialized project node object. */ -ProjectNode::ProjectNode(const Utils::FileName &projectFilePath, const QByteArray &id) : - FolderNode(projectFilePath, NodeType::Project, projectFilePath.fileName(), id) +ProjectNode::ProjectNode(const Utils::FilePath &projectFilePath) : + FolderNode(projectFilePath) { setPriority(DefaultProjectPriority); setListInProject(true); + setDisplayName(projectFilePath.fileName()); } bool ProjectNode::canAddSubProject(const QString &proFilePath) const @@ -822,6 +814,11 @@ bool ProjectNode::addSubProject(const QString &proFilePath) return false; } +QStringList ProjectNode::subProjectFileNamePatterns() const +{ + return QStringList(); +} + bool ProjectNode::removeSubProject(const QString &proFilePath) { Q_UNUSED(proFilePath) @@ -873,7 +870,7 @@ bool ProjectNode::deploysFolder(const QString &folder) const return false; } -ProjectNode *ProjectNode::projectNode(const Utils::FileName &file) const +ProjectNode *ProjectNode::projectNode(const Utils::FilePath &file) const { for (const std::unique_ptr<Node> &n: m_nodes) { if (ProjectNode *pnode = n->asProjectNode()) @@ -907,9 +904,15 @@ void FolderNode::handleSubTreeChanged(FolderNode *node) parent->handleSubTreeChanged(node); } +void FolderNode::setShowWhenEmpty(bool showWhenEmpty) +{ + m_showWhenEmpty = showWhenEmpty; +} + ContainerNode::ContainerNode(Project *project) - : FolderNode(project->projectDirectory(), NodeType::Project), m_project(project) -{} + : FolderNode(project->projectDirectory()), m_project(project) +{ +} QString ContainerNode::displayName() const { diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h index c96b8f8713..ce36882aa9 100644 --- a/src/plugins/projectexplorer/projectnodes.h +++ b/src/plugins/projectexplorer/projectnodes.h @@ -44,13 +44,6 @@ namespace ProjectExplorer { class Project; -enum class NodeType : quint16 { - File = 1, - Folder, - VirtualFolder, - Project -}; - // File types common for qt projects enum class FileType : quint16 { Unknown = 0, @@ -68,6 +61,7 @@ enum ProjectAction { // Special value to indicate that the actions are handled by the parent InheritedFromParent, AddSubProject, + AddExistingProject, RemoveSubProject, // Let's the user select to which project file // the file is added @@ -89,7 +83,6 @@ enum ProjectAction { HidePathActions, HideFileActions, HideFolderActions, - HasSubProjectRunConfigurations }; class FileNode; @@ -111,8 +104,11 @@ public: }; virtual ~Node(); - Node(const Node &other) = delete; - NodeType nodeType() const; + + virtual bool isFolderNodeType() const { return false; } + virtual bool isProjectNodeType() const { return false; } + virtual bool isVirtualFolderType() const { return false; } + int priority() const; ProjectNode *parentProjectNode() const; // parent project, will be nullptr for the top-level project @@ -125,9 +121,8 @@ public: // or node->parentProjectNode() for all other cases. const ProjectNode *managingProject() const; // see above. - const Utils::FileName &filePath() const; // file system path + const Utils::FilePath &filePath() const; // file system path int line() const; - QByteArray id() const; virtual QString displayName() const; virtual QString tooltip() const; bool isEnabled() const; @@ -137,7 +132,7 @@ public: virtual bool supportsAction(ProjectAction action, const Node *node) const; void setEnabled(bool enabled); - void setAbsoluteFilePathAndLine(const Utils::FileName &filePath, int line); + void setAbsoluteFilePathAndLine(const Utils::FilePath &filePath, int line); virtual FileNode *asFileNode() { return nullptr; } virtual const FileNode *asFileNode() const { return nullptr; } @@ -154,24 +149,26 @@ public: void setParentFolderNode(FolderNode *parentFolder); void setListInProject(bool l); + void setIsGenerated(bool g); + void setPriority(int priority); + void setLine(int line); static FileType fileTypeForMimeType(const Utils::MimeType &mt); - static FileType fileTypeForFileName(const Utils::FileName &file); + static FileType fileTypeForFileName(const Utils::FilePath &file); protected: - Node(NodeType nodeType, const Utils::FileName &filePath, int line = -1, - const QByteArray &id = {}); + Node(); + Node(const Node &other) = delete; + bool operator=(const Node &other) = delete; - void setPriority(int priority); - void setIsGenerated(bool g); + void setFilePath(const Utils::FilePath &filePath); private: FolderNode *m_parentFolderNode = nullptr; - Utils::FileName m_filePath; - QByteArray m_nodeId; + Utils::FilePath m_filePath; int m_line = -1; int m_priority = DefaultPriority; - const NodeType m_nodeType; + enum NodeFlag : quint16 { FlagNone = 0, FlagIsEnabled = 1 << 0, @@ -184,8 +181,7 @@ private: class PROJECTEXPLORER_EXPORT FileNode : public Node { public: - FileNode(const Utils::FileName &filePath, const FileType fileType, bool generated, int line = -1, - const QByteArray &id = {}); + FileNode(const Utils::FilePath &filePath, const FileType fileType); FileNode *clone() const; @@ -195,10 +191,11 @@ public: const FileNode *asFileNode() const final { return this; } static QList<FileNode *> - scanForFiles(const Utils::FileName &directory, - const std::function<FileNode *(const Utils::FileName &fileName)> factory, + scanForFiles(const Utils::FilePath &directory, + const std::function<FileNode *(const Utils::FilePath &fileName)> factory, QFutureInterface<QList<FileNode *>> *future = nullptr); bool supportsAction(ProjectAction action, const Node *node) const override; + QString displayName() const override; private: FileType m_fileType; @@ -208,12 +205,13 @@ private: class PROJECTEXPLORER_EXPORT FolderNode : public Node { public: - explicit FolderNode(const Utils::FileName &folderPath, NodeType nodeType = NodeType::Folder, - const QString &displayName = QString(), const QByteArray &id = {}); + explicit FolderNode(const Utils::FilePath &folderPath); QString displayName() const override; QIcon icon() const; + bool isFolderNodeType() const override { return true; } + Node *findNode(const std::function<bool(Node *)> &filter); QList<Node *> findNodes(const std::function<bool(Node *)> &filter); @@ -225,21 +223,19 @@ public: ProjectNode *findProjectNode(const std::function<bool(const ProjectNode *)> &predicate); const QList<Node *> nodes() const; QList<FileNode *> fileNodes() const; - FileNode *fileNode(const Utils::FileName &file) const; + FileNode *fileNode(const Utils::FilePath &file) const; QList<FolderNode *> folderNodes() const; - using FolderNodeFactory = std::function<std::unique_ptr<FolderNode>(const Utils::FileName &)>; + using FolderNodeFactory = std::function<std::unique_ptr<FolderNode>(const Utils::FilePath &)>; void addNestedNodes(std::vector<std::unique_ptr<FileNode>> &&files, - const Utils::FileName &overrideBaseDir = Utils::FileName(), + const Utils::FilePath &overrideBaseDir = Utils::FilePath(), const FolderNodeFactory &factory - = [](const Utils::FileName &fn) { return std::make_unique<FolderNode>(fn); }); + = [](const Utils::FilePath &fn) { return std::make_unique<FolderNode>(fn); }); void addNestedNode(std::unique_ptr<FileNode> &&fileNode, - const Utils::FileName &overrideBaseDir = Utils::FileName(), + const Utils::FilePath &overrideBaseDir = Utils::FilePath(), const FolderNodeFactory &factory - = [](const Utils::FileName &fn) { return std::make_unique<FolderNode>(fn); }); + = [](const Utils::FilePath &fn) { return std::make_unique<FolderNode>(fn); }); void compress(); - bool isAncesterOf(Node *n); - // takes ownership of newNode. // Will delete newNode if oldNode is not a child of this node. bool replaceSubtree(Node *oldNode, std::unique_ptr<Node> &&newNode); @@ -249,17 +245,18 @@ public: class LocationInfo { public: - LocationInfo(const QString &dn, const Utils::FileName &p, const int l = -1) : + LocationInfo(const QString &dn, const Utils::FilePath &p, const int l = -1) : path(p), line(l), displayName(dn) { } - Utils::FileName path; + Utils::FilePath path; int line = -1; QString displayName; }; void setLocationInfo(const QList<LocationInfo> &info); const QList<LocationInfo> locationInfo() const; - virtual QString addFileFilter() const; + QString addFileFilter() const; + void setAddFileFilter(const QString &filter) { m_addFileFilter = filter; } bool supportsAction(ProjectAction action, const Node *node) const override; @@ -284,11 +281,12 @@ public: // determines if node will be shown in the flat view, by default folder and projects aren't shown virtual bool showInSimpleTree() const; + // determines if node will always be shown when hiding empty directories - virtual bool showWhenEmpty() const; + bool showWhenEmpty() const; + void setShowWhenEmpty(bool showWhenEmpty); void addNode(std::unique_ptr<Node> &&node); - std::unique_ptr<Node> takeNode(Node *node); bool isEmpty() const; @@ -302,34 +300,41 @@ protected: QList<LocationInfo> m_locations; private: + std::unique_ptr<Node> takeNode(Node *node); + QString m_displayName; + QString m_addFileFilter; mutable QIcon m_icon; + bool m_showWhenEmpty = false; }; class PROJECTEXPLORER_EXPORT VirtualFolderNode : public FolderNode { public: - explicit VirtualFolderNode(const Utils::FileName &folderPath, int priority, - const QByteArray &id = {}); + explicit VirtualFolderNode(const Utils::FilePath &folderPath); - void setAddFileFilter(const QString &filter) { m_addFileFilter = filter; } - QString addFileFilter() const override; - -private: - QString m_addFileFilter; + bool isFolderNodeType() const override { return false; } + bool isVirtualFolderType() const override { return true; } }; // Documentation inside. class PROJECTEXPLORER_EXPORT ProjectNode : public FolderNode { public: + explicit ProjectNode(const Utils::FilePath &projectFilePath); + virtual bool canAddSubProject(const QString &proFilePath) const; virtual bool addSubProject(const QString &proFile); + virtual QStringList subProjectFileNamePatterns() const; virtual bool removeSubProject(const QString &proFilePath); - virtual Utils::optional<Utils::FileName> visibleAfterAddFileAction() const { + virtual Utils::optional<Utils::FilePath> visibleAfterAddFileAction() const { return Utils::nullopt; } + bool isFolderNodeType() const override { return false; } + bool isProjectNodeType() const override { return true; } + bool showInSimpleTree() const override { return true; } + bool addFiles(const QStringList &filePaths, QStringList *notAdded = nullptr) override; bool removeFiles(const QStringList &filePaths, QStringList *notRemoved = nullptr) override; bool deleteFiles(const QStringList &filePaths) override; @@ -340,7 +345,7 @@ public: // by default returns false virtual bool deploysFolder(const QString &folder) const; - ProjectNode *projectNode(const Utils::FileName &file) const; + ProjectNode *projectNode(const Utils::FilePath &file) const; ProjectNode *asProjectNode() final { return this; } const ProjectNode *asProjectNode() const final { return this; } @@ -352,8 +357,15 @@ public: virtual QVariant data(Core::Id role) const; virtual bool setData(Core::Id role, const QVariant &value) const; + bool isProduct() const { return m_isProduct; } + protected: - explicit ProjectNode(const Utils::FileName &projectFilePath, const QByteArray &id = {}); + void setIsProduct() { m_isProduct = true; } + + QString m_target; + +private: + bool m_isProduct = false; }; class PROJECTEXPLORER_EXPORT ContainerNode : public FolderNode @@ -364,6 +376,9 @@ public: QString displayName() const final; bool supportsAction(ProjectAction action, const Node *node) const final; + bool isFolderNodeType() const override { return false; } + bool isProjectNodeType() const override { return true; } + ContainerNode *asContainerNode() final { return this; } const ContainerNode *asContainerNode() const final { return this; } diff --git a/src/plugins/projectexplorer/projecttree.cpp b/src/plugins/projectexplorer/projecttree.cpp index 5e853e6a78..5259a0d609 100644 --- a/src/plugins/projectexplorer/projecttree.cpp +++ b/src/plugins/projectexplorer/projecttree.cpp @@ -104,16 +104,16 @@ Project *ProjectTree::currentProject() return s_instance->m_currentProject; } -Node *ProjectTree::findCurrentNode() +Node *ProjectTree::currentNode() { s_instance->update(); return s_instance->m_currentNode; } -FileName ProjectTree::currentFilePath() +FilePath ProjectTree::currentFilePath() { - Node *currentNode = findCurrentNode(); - return currentNode ? currentNode->filePath() : FileName(); + Node *node = currentNode(); + return node ? node->filePath() : FilePath(); } void ProjectTree::registerWidget(ProjectTreeWidget *widget) @@ -167,7 +167,7 @@ void ProjectTree::updateFromProjectTreeWidget(ProjectTreeWidget *widget) void ProjectTree::updateFromDocumentManager() { if (Core::IDocument *document = Core::EditorManager::currentDocument()) { - const FileName fileName = document->filePath(); + const FilePath fileName = document->filePath(); updateFromNode(ProjectTreeWidget::nodeForFile(fileName)); } else { updateFromNode(nullptr); @@ -283,6 +283,12 @@ void ProjectTree::expandAll() w->expandAll(); } +void ProjectTree::changeProjectRootDirectory() +{ + if (m_currentProject) + m_currentProject->changeRootProjectDirectory(); +} + void ProjectTree::updateExternalFileWarning() { auto document = qobject_cast<Core::IDocument *>(sender()); @@ -296,12 +302,12 @@ void ProjectTree::updateExternalFileWarning() } if (!infoBar->canInfoBeAdded(externalFileId)) return; - const FileName fileName = document->filePath(); + const FilePath fileName = document->filePath(); const QList<Project *> projects = SessionManager::projects(); if (projects.isEmpty()) return; for (Project *project : projects) { - FileName projectDir = project->projectDirectory(); + FilePath projectDir = project->projectDirectory(); if (projectDir.isEmpty()) continue; if (fileName.isChildOf(projectDir)) @@ -309,7 +315,7 @@ void ProjectTree::updateExternalFileWarning() // External file. Test if it under the same VCS QString topLevel; if (Core::VcsManager::findVersionControlForDirectory(projectDir.toString(), &topLevel) - && fileName.isChildOf(FileName::fromString(topLevel))) { + && fileName.isChildOf(FilePath::fromString(topLevel))) { return; } } @@ -333,26 +339,16 @@ void ProjectTree::showContextMenu(ProjectTreeWidget *focus, const QPoint &global if (!node) { contextMenu = Core::ActionManager::actionContainer(Constants::M_SESSIONCONTEXT)->menu(); - } else { - switch (node->nodeType()) { - case NodeType::Project: { - if ((node->parentFolderNode() && node->parentFolderNode()->asContainerNode()) - || node->asContainerNode()) - contextMenu = Core::ActionManager::actionContainer(Constants::M_PROJECTCONTEXT)->menu(); - else - contextMenu = Core::ActionManager::actionContainer(Constants::M_SUBPROJECTCONTEXT)->menu(); - break; - } - case NodeType::VirtualFolder: - case NodeType::Folder: - contextMenu = Core::ActionManager::actionContainer(Constants::M_FOLDERCONTEXT)->menu(); - break; - case NodeType::File: - contextMenu = Core::ActionManager::actionContainer(Constants::M_FILECONTEXT)->menu(); - break; - default: - qWarning("ProjectExplorerPlugin::showContextMenu - Missing handler for node type"); - } + } else if (node->isProjectNodeType()) { + if ((node->parentFolderNode() && node->parentFolderNode()->asContainerNode()) + || node->asContainerNode()) + contextMenu = Core::ActionManager::actionContainer(Constants::M_PROJECTCONTEXT)->menu(); + else + contextMenu = Core::ActionManager::actionContainer(Constants::M_SUBPROJECTCONTEXT)->menu(); + } else if (node->isVirtualFolderType() || node->isFolderNodeType()) { + contextMenu = Core::ActionManager::actionContainer(Constants::M_FOLDERCONTEXT)->menu(); + } else if (node->asFileNode()) { + contextMenu = Core::ActionManager::actionContainer(Constants::M_FILECONTEXT)->menu(); } if (contextMenu && contextMenu->actions().count() > 0) { @@ -433,7 +429,7 @@ Project *ProjectTree::projectForNode(const Node *node) }); } -Node *ProjectTree::nodeForFile(const FileName &fileName) +Node *ProjectTree::nodeForFile(const FilePath &fileName) { Node *node = nullptr; for (const Project *project : SessionManager::projects()) { @@ -441,7 +437,7 @@ Node *ProjectTree::nodeForFile(const FileName &fileName) projectNode->forEachGenericNode([&](Node *n) { if (n->filePath() == fileName) { // prefer file nodes - if (!node || (node->nodeType() != NodeType::File && n->nodeType() == NodeType::File)) + if (!node || (!node->asFileNode() && n->asFileNode())) node = n; } }); diff --git a/src/plugins/projectexplorer/projecttree.h b/src/plugins/projectexplorer/projecttree.h index 238c55a910..4adcaa7571 100644 --- a/src/plugins/projectexplorer/projecttree.h +++ b/src/plugins/projectexplorer/projecttree.h @@ -31,7 +31,7 @@ #include <functional> -namespace Utils { class FileName; } +namespace Utils { class FilePath; } namespace ProjectExplorer { class FileNode; @@ -53,8 +53,8 @@ public: static ProjectTree *instance(); static Project *currentProject(); - static Node *findCurrentNode(); - static Utils::FileName currentFilePath(); + static Node *currentNode(); + static Utils::FilePath currentFilePath(); // Integration with ProjectTreeWidget static void registerWidget(Internal::ProjectTreeWidget *widget); @@ -76,11 +76,13 @@ public: static void forEachNode(const std::function<void(Node *)> &task); static Project *projectForNode(const Node *node); - static Node *nodeForFile(const Utils::FileName &fileName); + static Node *nodeForFile(const Utils::FilePath &fileName); void collapseAll(); void expandAll(); + void changeProjectRootDirectory(); + // for nodes to emit signals, do not call unless you are a node static void emitSubtreeChanged(FolderNode *node); diff --git a/src/plugins/projectexplorer/projecttreewidget.cpp b/src/plugins/projectexplorer/projecttreewidget.cpp index af4334a21e..5e4b19f8c9 100644 --- a/src/plugins/projectexplorer/projecttreewidget.cpp +++ b/src/plugins/projectexplorer/projecttreewidget.cpp @@ -149,7 +149,9 @@ public: setEditTriggers(QAbstractItemView::EditKeyPressed); setContextMenuPolicy(Qt::CustomContextMenu); setDragEnabled(true); - setDragDropMode(QAbstractItemView::DragOnly); + setDragDropMode(QAbstractItemView::DragDrop); + viewport()->setAcceptDrops(true); + setDropIndicatorShown(true); m_context = new IContext(this); m_context->setContext(Context(ProjectExplorer::Constants::C_PROJECT_TREE)); m_context->setWidget(this); @@ -296,7 +298,7 @@ ProjectTreeWidget::ProjectTreeWidget(QWidget *parent) : QWidget(parent) connect(m_toggleSync, &QAbstractButton::clicked, this, &ProjectTreeWidget::toggleAutoSynchronization); - setCurrentItem(ProjectTree::findCurrentNode()); + setCurrentItem(ProjectTree::currentNode()); setAutoSynchronization(true); m_projectTreeWidgets << this; @@ -348,11 +350,26 @@ void ProjectTreeWidget::rowsInserted(const QModelIndex &parent, int start, int e } } -Node *ProjectTreeWidget::nodeForFile(const FileName &fileName) +Node *ProjectTreeWidget::nodeForFile(const FilePath &fileName) { Node *bestNode = nullptr; int bestNodeExpandCount = INT_MAX; + // FIXME: Check that the values used make sense in the context. + auto priority = [](Node *node) { + if (node->asFileNode()) + return 1; + if (node->isFolderNodeType()) + return 2; + if (node->isVirtualFolderType()) + return 3; + if (node->isProjectNodeType()) + return 4; + QTC_CHECK(false); + return 1; + }; + + // FIXME: Looks like this could be done with less cycles. for (Project *project : SessionManager::projects()) { if (ProjectNode *projectNode = project->rootProjectNode()) { projectNode->forEachGenericNode([&](Node *node) { @@ -360,10 +377,10 @@ Node *ProjectTreeWidget::nodeForFile(const FileName &fileName) if (!bestNode) { bestNode = node; bestNodeExpandCount = ProjectTreeWidget::expandedCount(node); - } else if (node->nodeType() < bestNode->nodeType()) { + } else if (priority(node) < priority(bestNode)) { bestNode = node; bestNodeExpandCount = ProjectTreeWidget::expandedCount(node); - } else if (node->nodeType() == bestNode->nodeType()) { + } else if (priority(node) == priority(bestNode)) { int nodeExpandCount = ProjectTreeWidget::expandedCount(node); if (nodeExpandCount < bestNodeExpandCount) { bestNode = node; @@ -440,7 +457,7 @@ void ProjectTreeWidget::editCurrentItem() editor->setSelection(0, dotIndex); } -void ProjectTreeWidget::renamed(const FileName &oldPath, const FileName &newPath) +void ProjectTreeWidget::renamed(const FilePath &oldPath, const FilePath &newPath) { update(); Q_UNUSED(oldPath); @@ -457,7 +474,7 @@ void ProjectTreeWidget::renamed(const FileName &oldPath, const FileName &newPath void ProjectTreeWidget::syncFromDocumentManager() { // sync from document manager - FileName fileName; + FilePath fileName; if (IDocument *doc = EditorManager::currentDocument()) fileName = doc->filePath(); if (!currentNode() || currentNode()->filePath() != fileName) @@ -522,7 +539,7 @@ void ProjectTreeWidget::showContextMenu(const QPoint &pos) void ProjectTreeWidget::openItem(const QModelIndex &mainIndex) { Node *node = m_model->nodeForIndex(mainIndex); - if (!node || node->nodeType() != NodeType::File) + if (!node || !node->asFileNode()) return; IEditor *editor = EditorManager::openEditor(node->filePath().toString()); if (editor && node->line() >= 0) diff --git a/src/plugins/projectexplorer/projecttreewidget.h b/src/plugins/projectexplorer/projecttreewidget.h index 6de41b0adf..c4b729f097 100644 --- a/src/plugins/projectexplorer/projecttreewidget.h +++ b/src/plugins/projectexplorer/projecttreewidget.h @@ -62,7 +62,7 @@ public: void sync(ProjectExplorer::Node *node); void showMessage(ProjectExplorer::Node *node, const QString &message); - static Node *nodeForFile(const Utils::FileName &fileName); + static Node *nodeForFile(const Utils::FilePath &fileName); void toggleAutoSynchronization(); void editCurrentItem(); @@ -81,7 +81,7 @@ private: void setCurrentItem(ProjectExplorer::Node *node); static int expandedCount(Node *node); void rowsInserted(const QModelIndex &parent, int start, int end); - void renamed(const Utils::FileName &oldPath, const Utils::FileName &newPath); + void renamed(const Utils::FilePath &oldPath, const Utils::FilePath &newPath); void syncFromDocumentManager(); @@ -94,7 +94,7 @@ private: QString m_modelId; bool m_autoSync = true; - Utils::FileName m_delayedRename; + Utils::FilePath m_delayedRename; static QList<ProjectTreeWidget *> m_projectTreeWidgets; friend class ProjectTreeWidgetFactory; diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp index 2cbb9a9b9d..7827034c2a 100644 --- a/src/plugins/projectexplorer/projectwelcomepage.cpp +++ b/src/plugins/projectexplorer/projectwelcomepage.cpp @@ -340,7 +340,7 @@ public: }; for (int i = 0; i < 3; ++i) { const QString &action = actions.at(i); - const int ww = fm.width(action); + const int ww = fm.horizontalAdvance(action); const QRect actionRect(xx, yy - 10, ww, 15); const bool isForcedDisabled = (i != 0 && sessionName == "default"); const bool isActive = actionRect.contains(mousePos) && !isForcedDisabled; @@ -399,7 +399,7 @@ public: else if (m_activeActionRects[1].contains(pos)) sessionModel->renameSession(ICore::mainWindow(), sessionName); else if (m_activeActionRects[2].contains(pos)) - sessionModel->deleteSession(sessionName); + sessionModel->deleteSessions(QStringList(sessionName)); return true; } } @@ -475,7 +475,8 @@ public: QString projectName = idx.data(Qt::DisplayRole).toString(); QString projectPath = idx.data(ProjectModel::FilePathRole).toString(); QFontMetrics fm(sizedFont(13, option.widget)); - int width = std::max(fm.width(projectName), fm.width(projectPath)) + 36; + int width = std::max(fm.horizontalAdvance(projectName), + fm.horizontalAdvance(projectPath)) + 36; return QSize(width, 48); } diff --git a/src/plugins/projectexplorer/projectwindow.cpp b/src/plugins/projectexplorer/projectwindow.cpp index 434249bee1..065de1e715 100644 --- a/src/plugins/projectexplorer/projectwindow.cpp +++ b/src/plugins/projectexplorer/projectwindow.cpp @@ -376,7 +376,7 @@ public: m_projectSelection = new QComboBox; m_projectSelection->setModel(&m_comboBoxModel); - connect(m_projectSelection, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated), + connect(m_projectSelection, QOverload<int>::of(&QComboBox::activated), this, &ProjectWindowPrivate::projectSelected, Qt::QueuedConnection); SessionManager *sessionManager = SessionManager::instance(); @@ -546,7 +546,7 @@ public: QString importDir = QFileDialog::getExistingDirectory(ICore::mainWindow(), ProjectWindow::tr("Import Directory"), dir); - FileName path = FileName::fromString(importDir); + FilePath path = FilePath::fromString(importDir); Target *lastTarget = nullptr; BuildConfiguration *lastBc = nullptr; diff --git a/src/plugins/projectexplorer/projectwizardpage.cpp b/src/plugins/projectexplorer/projectwizardpage.cpp index 500a13a4fc..9eaf136d8b 100644 --- a/src/plugins/projectexplorer/projectwizardpage.cpp +++ b/src/plugins/projectexplorer/projectwizardpage.cpp @@ -171,7 +171,7 @@ BestNodeSelector::BestNodeSelector(const QString &commonDirectory, const QString void BestNodeSelector::inspect(AddNewTree *tree, bool isContextNode) { FolderNode *node = tree->node(); - if (node->nodeType() == NodeType::Project) { + if (node->isProjectNodeType()) { if (static_cast<ProjectNode *>(node)->deploysFolder(m_commonDirectory)) { m_deploys = true; m_deployText += tree->displayName() + QLatin1Char('\n'); @@ -284,9 +284,9 @@ ProjectWizardPage::ProjectWizardPage(QWidget *parent) : WizardPage(parent), { m_ui->setupUi(this); m_ui->vcsManageButton->setText(ICore::msgShowOptionsDialog()); - connect(m_ui->projectComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(m_ui->projectComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ProjectWizardPage::projectChanged); - connect(m_ui->addToVersionControlComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(m_ui->addToVersionControlComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ProjectWizardPage::versionControlChanged); connect(m_ui->vcsManageButton, &QAbstractButton::clicked, this, &ProjectWizardPage::manageVcs); setProperty(SHORT_TITLE_PROPERTY, tr("Summary")); @@ -299,7 +299,7 @@ ProjectWizardPage::ProjectWizardPage(QWidget *parent) : WizardPage(parent), ProjectWizardPage::~ProjectWizardPage() { - disconnect(m_ui->projectComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + disconnect(m_ui->projectComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ProjectWizardPage::projectChanged); delete m_ui; } @@ -534,7 +534,7 @@ void ProjectWizardPage::setFiles(const QStringList &fileNames) const bool filePath2HasDir = filePath2.contains(QLatin1Char('/')); if (filePath1HasDir == filePath2HasDir) - return FileName::fromString(filePath1) < FileName::fromString(filePath2); + return FilePath::fromString(filePath1) < FilePath::fromString(filePath2); return filePath1HasDir; } ); diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp index 2c8e238495..5c3c6fc0e9 100644 --- a/src/plugins/projectexplorer/runconfiguration.cpp +++ b/src/plugins/projectexplorer/runconfiguration.cpp @@ -26,6 +26,7 @@ #include "runconfiguration.h" #include "project.h" +#include "runcontrol.h" #include "target.h" #include "toolchain.h" #include "abi.h" @@ -55,21 +56,9 @@ #include <QLoggingCategory> #include <QSettings> -#ifdef Q_OS_OSX -#include <ApplicationServices/ApplicationServices.h> -#endif - -#if defined (WITH_JOURNALD) -#include "journaldwatcher.h" -#endif - using namespace Utils; using namespace ProjectExplorer::Internal; -namespace { -Q_LOGGING_CATEGORY(statesLog, "qtc.projectmanager.states", QtWarningMsg) -} - namespace ProjectExplorer { /////////////////////////////////////////////////////////////////////// @@ -169,7 +158,7 @@ void GlobalOrProjectAspect::resetProjectToGlobalSettings() static std::vector<RunConfiguration::AspectFactory> theAspectFactories; RunConfiguration::RunConfiguration(Target *target, Core::Id id) - : StatefulProjectConfiguration(target, id) + : ProjectConfiguration(target, id) { connect(target->project(), &Project::parsingStarted, this, [this]() { updateEnabledState(); }); @@ -197,12 +186,26 @@ RunConfiguration::RunConfiguration(Target *target, Core::Id id) const auto envAspect = aspect<EnvironmentAspect>(); return envAspect ? envAspect->environment().value(var) : QString(); }); + + expander->registerVariable(Constants::VAR_CURRENTRUN_WORKINGDIR, + tr("The currently active run configuration's working directory"), + [this, expander] { + const auto wdAspect = aspect<WorkingDirectoryAspect>(); + return wdAspect ? wdAspect->workingDirectory(expander).toString() : QString(); + }); + expander->registerVariable(Constants::VAR_CURRENTRUN_NAME, QCoreApplication::translate("ProjectExplorer", "The currently active run configuration's name."), [this] { return displayName(); }, false); for (const AspectFactory &factory : theAspectFactories) m_aspects.append(factory(target)); + + m_executableGetter = [this] { + if (const auto executableAspect = aspect<ExecutableAspect>()) + return executableAspect->executable(); + return FilePath(); + }; } RunConfiguration::~RunConfiguration() = default; @@ -212,12 +215,25 @@ bool RunConfiguration::isActive() const return target()->isActive() && target()->activeRunConfiguration() == this; } +void RunConfiguration::setEnabled(bool enabled) +{ + if (enabled == m_isEnabled) + return; + m_isEnabled = enabled; + emit enabledChanged(); +} + QString RunConfiguration::disabledReason() const { if (target()->project()->isParsing()) return tr("The Project is currently being parsed."); - if (!target()->project()->hasParsingData()) - return tr("The project could not be fully parsed."); + if (!target()->project()->hasParsingData()) { + QString msg = tr("The project could not be fully parsed."); + const FilePath projectFilePath = buildTargetInfo().projectFilePath; + if (!projectFilePath.exists()) + msg += '\n' + tr("The project file \"%1\" does not exist.").arg(projectFilePath.toString()); + return msg; + } return QString(); } @@ -311,9 +327,19 @@ QVariantMap RunConfiguration::toMap() const return map; } +void RunConfiguration::setExecutableGetter(const RunConfiguration::ExecutableGetter &exeGetter) +{ + m_executableGetter = exeGetter; +} + +FilePath RunConfiguration::executable() const +{ + return m_executableGetter(); +} + BuildTargetInfo RunConfiguration::buildTargetInfo() const { - return target()->applicationTargets().buildTargetInfo(m_buildKey); + return target()->buildTarget(m_buildKey); } bool RunConfiguration::fromMap(const QVariantMap &map) @@ -366,8 +392,7 @@ bool RunConfiguration::fromMap(const QVariantMap &map) Runnable RunConfiguration::runnable() const { Runnable r; - if (auto executableAspect = aspect<ExecutableAspect>()) - r.executable = executableAspect->executable().toString(); + r.executable = executable().toString(); if (auto argumentsAspect = aspect<ArgumentsAspect>()) r.commandLineArguments = argumentsAspect->arguments(macroExpander()); if (auto workingDirectoryAspect = aspect<WorkingDirectoryAspect>()) @@ -426,8 +451,6 @@ RunConfigurationFactory::RunConfigurationFactory() RunConfigurationFactory::~RunConfigurationFactory() { g_runConfigurationFactories.removeOne(this); - qDeleteAll(m_ownedRunWorkerFactories); - m_ownedRunWorkerFactories.clear(); } QString RunConfigurationFactory::decoratedTargetName(const QString &targetName, Target *target) @@ -435,9 +458,9 @@ QString RunConfigurationFactory::decoratedTargetName(const QString &targetName, QString displayName; if (!targetName.isEmpty()) displayName = QFileInfo(targetName).completeBaseName(); - Core::Id devType = DeviceTypeKitInformation::deviceTypeId(target->kit()); + Core::Id devType = DeviceTypeKitAspect::deviceTypeId(target->kit()); if (devType != Constants::DESKTOP_DEVICE_TYPE) { - if (IDevice::ConstPtr dev = DeviceKitInformation::device(target->kit())) { + if (IDevice::ConstPtr dev = DeviceKitAspect::device(target->kit())) { if (displayName.isEmpty()) { //: Shown in Run configuration if no executable is given, %1 is device name displayName = RunConfiguration::tr("Run on %1").arg(dev->displayName()); @@ -453,7 +476,7 @@ QString RunConfigurationFactory::decoratedTargetName(const QString &targetName, QList<RunConfigurationCreationInfo> RunConfigurationFactory::availableCreators(Target *parent) const { - const QList<BuildTargetInfo> buildTargets = parent->applicationTargets().list; + const QList<BuildTargetInfo> buildTargets = parent->applicationTargets(); const bool hasAnyQtcRunnable = Utils::anyOf(buildTargets, Utils::equal(&BuildTargetInfo::isQtcRunnable, true)); return Utils::transform(buildTargets, [&](const BuildTargetInfo &ti) { @@ -466,6 +489,7 @@ RunConfigurationFactory::availableCreators(Target *parent) const rci.factory = this; rci.id = m_runConfigBaseId; rci.buildKey = ti.buildKey; + rci.projectFilePath = ti.projectFilePath; rci.displayName = displayName; rci.displayNameUniquifier = ti.displayNameUniquifier; rci.creationMode = ti.isQtcRunnable || !hasAnyQtcRunnable @@ -495,16 +519,6 @@ void RunConfigurationFactory::setDecorateDisplayNames(bool on) m_decorateDisplayNames = on; } -RunWorkerFactory *RunConfigurationFactory::addRunWorkerFactoryHelper - (Core::Id runMode, const std::function<RunWorker *(RunControl *)> &creator) -{ - auto factory = new RunWorkerFactory; - factory->addConstraint(m_ownTypeChecker); - factory->addSupportedRunMode(runMode); - factory->setProducer(creator); - return factory; -} - void RunConfigurationFactory::addSupportedProjectType(Core::Id id) { m_supportedProjectTypes.append(id); @@ -524,7 +538,7 @@ bool RunConfigurationFactory::canHandle(Target *target) const if (!m_supportedTargetDeviceTypes.isEmpty()) if (!m_supportedTargetDeviceTypes.contains( - DeviceTypeKitInformation::deviceTypeId(kit))) + DeviceTypeKitAspect::deviceTypeId(kit))) return false; return true; @@ -540,6 +554,7 @@ RunConfiguration *RunConfigurationCreationInfo::create(Target *target) const if (!rc) return nullptr; + rc->acquaintAspects(); rc->m_buildKey = buildKey; rc->doAdditionalSetup(*this); rc->setDisplayName(displayName); @@ -555,6 +570,7 @@ RunConfiguration *RunConfigurationFactory::restore(Target *parent, const QVarian if (id.name().startsWith(factory->m_runConfigBaseId.name())) { QTC_ASSERT(factory->m_creator, continue); RunConfiguration *rc = factory->m_creator(parent); + rc->acquaintAspects(); if (rc->fromMap(map)) return rc; delete rc; @@ -607,1413 +623,4 @@ FixedRunConfigurationFactory::availableCreators(Target *parent) const return {rci}; } - -// RunWorkerFactory - -static QList<RunWorkerFactory *> g_runWorkerFactories; - -RunWorkerFactory::RunWorkerFactory() -{ - g_runWorkerFactories.append(this); -} - -RunWorkerFactory::~RunWorkerFactory() -{ - g_runWorkerFactories.removeOne(this); -} - -bool RunWorkerFactory::canRun(RunConfiguration *runConfiguration, Core::Id runMode) const -{ - if (!m_supportedRunModes.contains(runMode)) - return false; - - for (const Constraint &constraint : m_constraints) { - if (!constraint(runConfiguration)) - return false; - } - - return true; -} - -void RunWorkerFactory::setProducer(const WorkerCreator &producer) -{ - m_producer = producer; -} - -void RunWorkerFactory::addConstraint(const Constraint &constraint) -{ - // Default constructed Constraints are not worth keeping. - // FIXME: Make it a QTC_ASSERT once there is no code path - // using this "feature" anymore. - if (!constraint) - return; - m_constraints.append(constraint); -} - -void RunWorkerFactory::addSupportedRunMode(Core::Id runMode) -{ - m_supportedRunModes.append(runMode); -} - -void RunWorkerFactory::destroyRemainingRunWorkerFactories() -{ - qDeleteAll(g_runWorkerFactories); -} - -/*! - \class ProjectExplorer::RunControl - \brief The RunControl class instances represent one item that is run. -*/ - -/*! - \fn QIcon ProjectExplorer::RunControl::icon() const - Returns the icon to be shown in the Outputwindow. - - TODO the icon differs currently only per "mode", so this is more flexible - than it needs to be. -*/ - - -namespace Internal { - -enum class RunWorkerState -{ - Initialized, Starting, Running, Stopping, Done -}; - -static QString stateName(RunWorkerState s) -{ -# define SN(x) case x: return QLatin1String(#x); - switch (s) { - SN(RunWorkerState::Initialized) - SN(RunWorkerState::Starting) - SN(RunWorkerState::Running) - SN(RunWorkerState::Stopping) - SN(RunWorkerState::Done) - } - return QString("<unknown: %1>").arg(int(s)); -# undef SN -} - -class RunWorkerPrivate : public QObject -{ -public: - RunWorkerPrivate(RunWorker *runWorker, RunControl *runControl); - - bool canStart() const; - bool canStop() const; - void timerEvent(QTimerEvent *ev) override; - - void killStartWatchdog() - { - if (startWatchdogTimerId != -1) { - killTimer(startWatchdogTimerId); - startWatchdogTimerId = -1; - } - } - - void killStopWatchdog() - { - if (stopWatchdogTimerId != -1) { - killTimer(stopWatchdogTimerId); - stopWatchdogTimerId = -1; - } - } - - void startStartWatchdog() - { - killStartWatchdog(); - killStopWatchdog(); - - if (startWatchdogInterval != 0) - startWatchdogTimerId = startTimer(startWatchdogInterval); - } - - void startStopWatchdog() - { - killStopWatchdog(); - killStartWatchdog(); - - if (stopWatchdogInterval != 0) - stopWatchdogTimerId = startTimer(stopWatchdogInterval); - } - - RunWorker *q; - RunWorkerState state = RunWorkerState::Initialized; - const QPointer<RunControl> runControl; - QList<RunWorker *> startDependencies; - QList<RunWorker *> stopDependencies; - QString id; - - QVariantMap data; - int startWatchdogInterval = 0; - int startWatchdogTimerId = -1; - std::function<void()> startWatchdogCallback; - int stopWatchdogInterval = 0; // 5000; - int stopWatchdogTimerId = -1; - std::function<void()> stopWatchdogCallback; - bool supportsReRunning = true; - bool essential = false; -}; - -enum class RunControlState -{ - Initialized, // Default value after creation. - Starting, // Actual process/tool starts. - Running, // All good and running. - Stopping, // initiateStop() was called, stop application/tool - Stopped, // all good, but stopped. Can possibly be re-started - Finishing, // Application tab manually closed - Finished // Final state, will self-destruct with deleteLater() -}; - -static QString stateName(RunControlState s) -{ -# define SN(x) case x: return QLatin1String(#x); - switch (s) { - SN(RunControlState::Initialized) - SN(RunControlState::Starting) - SN(RunControlState::Running) - SN(RunControlState::Stopping) - SN(RunControlState::Stopped) - SN(RunControlState::Finishing) - SN(RunControlState::Finished) - } - return QString("<unknown: %1>").arg(int(s)); -# undef SN -} - -class RunControlPrivate : public QObject -{ -public: - RunControlPrivate(RunControl *parent, RunConfiguration *runConfiguration, Core::Id mode) - : q(parent), runMode(mode), runConfiguration(runConfiguration) - { - icon = Icons::RUN_SMALL_TOOLBAR; - if (runConfiguration) { - runnable = runConfiguration->runnable(); - displayName = runConfiguration->displayName(); - outputFormatter = runConfiguration->createOutputFormatter(); - device = runnable.device; - if (!device) - device = DeviceKitInformation::device(runConfiguration->target()->kit()); - project = runConfiguration->target()->project(); - } else { - outputFormatter = new OutputFormatter(); - } - } - - ~RunControlPrivate() override - { - QTC_CHECK(state == RunControlState::Finished || state == RunControlState::Initialized); - disconnect(); - q = nullptr; - qDeleteAll(m_workers); - m_workers.clear(); - delete outputFormatter; - } - - Q_ENUM(RunControlState) - - void checkState(RunControlState expectedState); - void setState(RunControlState state); - - void debugMessage(const QString &msg); - - void initiateStart(); - void initiateReStart(); - void continueStart(); - void initiateStop(); - void forceStop(); - void continueStopOrFinish(); - void initiateFinish(); - - void onWorkerStarted(RunWorker *worker); - void onWorkerStopped(RunWorker *worker); - void onWorkerFailed(RunWorker *worker, const QString &msg); - - void showError(const QString &msg); - - static bool isAllowedTransition(RunControlState from, RunControlState to); - bool supportsReRunning() const; - - RunControl *q; - QString displayName; - Runnable runnable; - IDevice::ConstPtr device; - Core::Id runMode; - Utils::Icon icon; - const QPointer<RunConfiguration> runConfiguration; // Not owned. - QPointer<Project> project; // Not owned. - QPointer<Utils::OutputFormatter> outputFormatter = nullptr; - std::function<bool(bool*)> promptToStop; - std::vector<RunWorkerFactory> m_factories; - - // A handle to the actual application process. - Utils::ProcessHandle applicationProcessHandle; - - RunControlState state = RunControlState::Initialized; - - QList<QPointer<RunWorker>> m_workers; -}; - -} // Internal - -using namespace Internal; - -RunControl::RunControl(RunConfiguration *runConfiguration, Core::Id mode) : - d(std::make_unique<RunControlPrivate>(this, runConfiguration, mode)) -{ -#ifdef WITH_JOURNALD - if (!device().isNull() && device()->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) { - JournaldWatcher::instance()->subscribe(this, [this](const JournaldWatcher::LogEntry &entry) { - - if (entry.value("_MACHINE_ID") != JournaldWatcher::instance()->machineId()) - return; - - const QByteArray pid = entry.value("_PID"); - if (pid.isEmpty()) - return; - - const qint64 pidNum = static_cast<qint64>(QString::fromLatin1(pid).toInt()); - if (pidNum != d->applicationProcessHandle.pid()) - return; - - const QString message = QString::fromUtf8(entry.value("MESSAGE")) + "\n"; - appendMessageRequested(this, message, Utils::OutputFormat::LogMessageFormat); - }); - } -#endif -} - -RunControl::RunControl(const IDevice::ConstPtr &device, Core::Id mode) - : RunControl(nullptr, mode) -{ - d->device = device; -} - -RunControl::~RunControl() -{ -#ifdef WITH_JOURNALD - JournaldWatcher::instance()->unsubscribe(this); -#endif -} - -void RunControl::initiateStart() -{ - emit aboutToStart(); - d->initiateStart(); -} - -void RunControl::initiateReStart() -{ - emit aboutToStart(); - d->initiateReStart(); -} - -void RunControl::initiateStop() -{ - d->initiateStop(); -} - -void RunControl::forceStop() -{ - d->forceStop(); -} - -void RunControl::initiateFinish() -{ - QTimer::singleShot(0, d.get(), &RunControlPrivate::initiateFinish); -} - -using WorkerCreators = QHash<Core::Id, RunControl::WorkerCreator>; - -static WorkerCreators &theWorkerCreators() -{ - static WorkerCreators creators; - return creators; -} - -void RunControl::registerWorkerCreator(Core::Id id, const WorkerCreator &workerCreator) -{ - theWorkerCreators().insert(id, workerCreator); - auto keys = theWorkerCreators().keys(); - Q_UNUSED(keys); -} - -RunWorker *RunControl::createWorker(Core::Id id) -{ - auto keys = theWorkerCreators().keys(); - Q_UNUSED(keys); - WorkerCreator creator = theWorkerCreators().value(id); - if (creator) - return creator(this); - creator = device()->workerCreator(id); - if (creator) - return creator(this); - return nullptr; -} - -RunWorkerFactory::WorkerCreator RunControl::producer(RunConfiguration *runConfig, Core::Id runMode) -{ - const auto canRun = std::bind(&RunWorkerFactory::canRun, std::placeholders::_1, runConfig, runMode); - const QList<RunWorkerFactory *> candidates = Utils::filtered(g_runWorkerFactories, canRun); - - // This is legit, there might be combinations that cannot run. - if (candidates.empty()) - return {}; - - // There should be at most one top-level producer feeling responsible per combination. - // Breaking a tie should be done by tightening the restrictions on one of them. - QTC_CHECK(candidates.size() == 1); - return candidates.front()->producer(); -} - -void RunControlPrivate::initiateStart() -{ - checkState(RunControlState::Initialized); - setState(RunControlState::Starting); - debugMessage("Queue: Starting"); - - continueStart(); -} - -void RunControlPrivate::initiateReStart() -{ - checkState(RunControlState::Stopped); - - // Re-set worked on re-runs. - for (RunWorker *worker : m_workers) { - if (worker->d->state == RunWorkerState::Done) - worker->d->state = RunWorkerState::Initialized; - } - - setState(RunControlState::Starting); - debugMessage("Queue: ReStarting"); - - continueStart(); -} - -void RunControlPrivate::continueStart() -{ - checkState(RunControlState::Starting); - bool allDone = true; - debugMessage("Looking for next worker"); - for (RunWorker *worker : m_workers) { - if (worker) { - const QString &workerId = worker->d->id; - debugMessage(" Examining worker " + workerId); - switch (worker->d->state) { - case RunWorkerState::Initialized: - debugMessage(" " + workerId + " is not done yet."); - if (worker->d->canStart()) { - debugMessage("Starting " + workerId); - worker->d->state = RunWorkerState::Starting; - QTimer::singleShot(0, worker, &RunWorker::initiateStart); - return; - } - allDone = false; - debugMessage(" " + workerId + " cannot start."); - break; - case RunWorkerState::Starting: - debugMessage(" " + workerId + " currently starting"); - allDone = false; - break; - case RunWorkerState::Running: - debugMessage(" " + workerId + " currently running"); - break; - case RunWorkerState::Stopping: - debugMessage(" " + workerId + " currently stopping"); - continue; - case RunWorkerState::Done: - debugMessage(" " + workerId + " was done before"); - break; - } - } else { - debugMessage("Found unknown deleted worker while starting"); - } - } - if (allDone) - setState(RunControlState::Running); -} - -void RunControlPrivate::initiateStop() -{ - if (state != RunControlState::Starting && state != RunControlState::Running) - qDebug() << "Unexpected initiateStop() in state" << stateName(state); - - setState(RunControlState::Stopping); - debugMessage("Queue: Stopping for all workers"); - - continueStopOrFinish(); -} - -void RunControlPrivate::continueStopOrFinish() -{ - bool allDone = true; - - auto queueStop = [this](RunWorker *worker, const QString &message) { - if (worker->d->canStop()) { - debugMessage(message); - worker->d->state = RunWorkerState::Stopping; - QTimer::singleShot(0, worker, &RunWorker::initiateStop); - } else { - debugMessage(" " + worker->d->id + " is waiting for dependent workers to stop"); - } - }; - - for (RunWorker *worker : m_workers) { - if (worker) { - const QString &workerId = worker->d->id; - debugMessage(" Examining worker " + workerId); - switch (worker->d->state) { - case RunWorkerState::Initialized: - debugMessage(" " + workerId + " was Initialized, setting to Done"); - worker->d->state = RunWorkerState::Done; - break; - case RunWorkerState::Stopping: - debugMessage(" " + workerId + " was already Stopping. Keeping it that way"); - allDone = false; - break; - case RunWorkerState::Starting: - queueStop(worker, " " + workerId + " was Starting, queuing stop"); - allDone = false; - break; - case RunWorkerState::Running: - queueStop(worker, " " + workerId + " was Running, queuing stop"); - allDone = false; - break; - case RunWorkerState::Done: - debugMessage(" " + workerId + " was Done. Good."); - break; - } - } else { - debugMessage("Found unknown deleted worker"); - } - } - - RunControlState targetState; - if (state == RunControlState::Finishing) { - targetState = RunControlState::Finished; - } else { - checkState(RunControlState::Stopping); - targetState = RunControlState::Stopped; - } - - if (allDone) { - debugMessage("All Stopped"); - setState(targetState); - } else { - debugMessage("Not all workers Stopped. Waiting..."); - } -} - -void RunControlPrivate::forceStop() -{ - if (state == RunControlState::Finished) { - debugMessage("Was finished, too late to force Stop"); - return; - } - for (RunWorker *worker : m_workers) { - if (worker) { - const QString &workerId = worker->d->id; - debugMessage(" Examining worker " + workerId); - switch (worker->d->state) { - case RunWorkerState::Initialized: - debugMessage(" " + workerId + " was Initialized, setting to Done"); - break; - case RunWorkerState::Stopping: - debugMessage(" " + workerId + " was already Stopping. Set it forcefully to Done."); - break; - case RunWorkerState::Starting: - debugMessage(" " + workerId + " was Starting. Set it forcefully to Done."); - break; - case RunWorkerState::Running: - debugMessage(" " + workerId + " was Running. Set it forcefully to Done."); - break; - case RunWorkerState::Done: - debugMessage(" " + workerId + " was Done. Good."); - break; - } - worker->d->state = RunWorkerState::Done; - } else { - debugMessage("Found unknown deleted worker"); - } - } - - setState(RunControlState::Stopped); - debugMessage("All Stopped"); -} - -void RunControlPrivate::initiateFinish() -{ - setState(RunControlState::Finishing); - debugMessage("Ramping down"); - - continueStopOrFinish(); -} - -void RunControlPrivate::onWorkerStarted(RunWorker *worker) -{ - worker->d->state = RunWorkerState::Running; - - if (state == RunControlState::Starting) { - debugMessage(worker->d->id + " start succeeded"); - continueStart(); - return; - } - showError(RunControl::tr("Unexpected run control state %1 when worker %2 started.") - .arg(stateName(state)) - .arg(worker->d->id)); -} - -void RunControlPrivate::onWorkerFailed(RunWorker *worker, const QString &msg) -{ - worker->d->state = RunWorkerState::Done; - - showError(msg); - switch (state) { - case RunControlState::Initialized: - // FIXME 1: We don't have an output pane yet, so use some other mechanism for now. - // FIXME 2: Translation... - QMessageBox::critical(Core::ICore::dialogParent(), - QCoreApplication::translate("TaskHub", "Error"), - QString("Failure during startup. Aborting.") + "<p>" + msg); - continueStopOrFinish(); - break; - case RunControlState::Starting: - case RunControlState::Running: - initiateStop(); - break; - case RunControlState::Stopping: - case RunControlState::Finishing: - continueStopOrFinish(); - break; - case RunControlState::Stopped: - case RunControlState::Finished: - QTC_CHECK(false); // Should not happen. - continueStopOrFinish(); - break; - } -} - -void RunControlPrivate::onWorkerStopped(RunWorker *worker) -{ - const QString &workerId = worker->d->id; - switch (worker->d->state) { - case RunWorkerState::Running: - // That was a spontaneous stop. - worker->d->state = RunWorkerState::Done; - debugMessage(workerId + " stopped spontaneously."); - break; - case RunWorkerState::Stopping: - worker->d->state = RunWorkerState::Done; - debugMessage(workerId + " stopped expectedly."); - break; - case RunWorkerState::Done: - worker->d->state = RunWorkerState::Done; - debugMessage(workerId + " stopped twice. Huh? But harmless."); - return; // Sic! - default: - debugMessage(workerId + " stopped unexpectedly in state" - + stateName(worker->d->state)); - worker->d->state = RunWorkerState::Done; - break; - } - - if (state == RunControlState::Finishing || state == RunControlState::Stopping) { - continueStopOrFinish(); - return; - } else if (worker->isEssential()) { - debugMessage(workerId + " is essential. Stopping all others."); - initiateStop(); - return; - } - - for (RunWorker *dependent : worker->d->stopDependencies) { - switch (dependent->d->state) { - case RunWorkerState::Done: - break; - case RunWorkerState::Initialized: - dependent->d->state = RunWorkerState::Done; - break; - default: - debugMessage("Killing " + dependent->d->id + " as it depends on stopped " + workerId); - dependent->d->state = RunWorkerState::Stopping; - QTimer::singleShot(0, dependent, &RunWorker::initiateStop); - break; - } - } - - debugMessage("Checking whether all stopped"); - bool allDone = true; - for (RunWorker *worker : m_workers) { - if (worker) { - const QString &workerId = worker->d->id; - debugMessage(" Examining worker " + workerId); - switch (worker->d->state) { - case RunWorkerState::Initialized: - debugMessage(" " + workerId + " was Initialized."); - break; - case RunWorkerState::Starting: - debugMessage(" " + workerId + " was Starting, waiting for its response"); - allDone = false; - break; - case RunWorkerState::Running: - debugMessage(" " + workerId + " was Running, waiting for its response"); - allDone = false; - break; - case RunWorkerState::Stopping: - debugMessage(" " + workerId + " was already Stopping. Keeping it that way"); - allDone = false; - break; - case RunWorkerState::Done: - debugMessage(" " + workerId + " was Done. Good."); - break; - } - } else { - debugMessage("Found unknown deleted worker"); - } - } - - if (allDone) { - if (state == RunControlState::Stopped) { - debugMessage("All workers stopped, but runControl was already stopped."); - } else { - debugMessage("All workers stopped. Set runControl to Stopped"); - setState(RunControlState::Stopped); - } - } else { - debugMessage("Not all workers stopped. Waiting..."); - } -} - -void RunControlPrivate::showError(const QString &msg) -{ - if (!msg.isEmpty()) - q->appendMessage(msg + '\n', ErrorMessageFormat); -} - -Utils::OutputFormatter *RunControl::outputFormatter() const -{ - return d->outputFormatter; -} - -Core::Id RunControl::runMode() const -{ - return d->runMode; -} - -const Runnable &RunControl::runnable() const -{ - return d->runnable; -} - -void RunControl::setRunnable(const Runnable &runnable) -{ - d->runnable = runnable; -} - -QString RunControl::displayName() const -{ - return d->displayName; -} - -void RunControl::setDisplayName(const QString &displayName) -{ - d->displayName = displayName; -} - -void RunControl::setIcon(const Utils::Icon &icon) -{ - d->icon = icon; -} - -Utils::Icon RunControl::icon() const -{ - return d->icon; -} - -IDevice::ConstPtr RunControl::device() const -{ - return d->device; -} - -RunConfiguration *RunControl::runConfiguration() const -{ - return d->runConfiguration.data(); -} - -Project *RunControl::project() const -{ - return d->project.data(); -} - -/*! - A handle to the application process. - - This is typically a process id, but should be treated as - opaque handle to the process controled by this \c RunControl. -*/ - -ProcessHandle RunControl::applicationProcessHandle() const -{ - return d->applicationProcessHandle; -} - -void RunControl::setApplicationProcessHandle(const ProcessHandle &handle) -{ - if (d->applicationProcessHandle != handle) { - d->applicationProcessHandle = handle; - emit applicationProcessHandleChanged(QPrivateSignal()); - } -} - -/*! - Prompts to stop. If \a optionalPrompt is passed, a \gui {Do not ask again} - checkbox is displayed and the result is returned in \a *optionalPrompt. -*/ - -bool RunControl::promptToStop(bool *optionalPrompt) const -{ - QTC_ASSERT(isRunning(), return true); - if (optionalPrompt && !*optionalPrompt) - return true; - - // Overridden. - if (d->promptToStop) - return d->promptToStop(optionalPrompt); - - const QString msg = tr("<html><head/><body><center><i>%1</i> is still running.<center/>" - "<center>Force it to quit?</center></body></html>").arg(displayName()); - return showPromptToStopDialog(tr("Application Still Running"), msg, - tr("Force &Quit"), tr("&Keep Running"), - optionalPrompt); -} - -void RunControl::setPromptToStop(const std::function<bool (bool *)> &promptToStop) -{ - d->promptToStop = promptToStop; -} - -bool RunControl::supportsReRunning() const -{ - return d->supportsReRunning(); -} - -bool RunControlPrivate::supportsReRunning() const -{ - for (RunWorker *worker : m_workers) { - if (!worker->d->supportsReRunning) - return false; - if (worker->d->state != RunWorkerState::Done) - return false; - } - return true; -} - -bool RunControl::isRunning() const -{ - return d->state == RunControlState::Running; -} - -bool RunControl::isStarting() const -{ - return d->state == RunControlState::Starting; -} - -bool RunControl::isStopping() const -{ - return d->state == RunControlState::Stopping; -} - -bool RunControl::isStopped() const -{ - return d->state == RunControlState::Stopped; -} - -/*! - Prompts to terminate the application with the \gui {Do not ask again} - checkbox. -*/ - -bool RunControl::showPromptToStopDialog(const QString &title, - const QString &text, - const QString &stopButtonText, - const QString &cancelButtonText, - bool *prompt) -{ - // Show a question message box where user can uncheck this - // question for this class. - Utils::CheckableMessageBox messageBox(Core::ICore::mainWindow()); - messageBox.setWindowTitle(title); - messageBox.setText(text); - messageBox.setStandardButtons(QDialogButtonBox::Yes|QDialogButtonBox::Cancel); - if (!stopButtonText.isEmpty()) - messageBox.button(QDialogButtonBox::Yes)->setText(stopButtonText); - if (!cancelButtonText.isEmpty()) - messageBox.button(QDialogButtonBox::Cancel)->setText(cancelButtonText); - messageBox.setDefaultButton(QDialogButtonBox::Yes); - if (prompt) { - messageBox.setCheckBoxText(Utils::CheckableMessageBox::msgDoNotAskAgain()); - messageBox.setChecked(false); - } else { - messageBox.setCheckBoxVisible(false); - } - messageBox.exec(); - const bool close = messageBox.clickedStandardButton() == QDialogButtonBox::Yes; - if (close && prompt && messageBox.isChecked()) - *prompt = false; - return close; -} - -bool RunControlPrivate::isAllowedTransition(RunControlState from, RunControlState to) -{ - switch (from) { - case RunControlState::Initialized: - return to == RunControlState::Starting - || to == RunControlState::Finishing; - case RunControlState::Starting: - return to == RunControlState::Running - || to == RunControlState::Stopping - || to == RunControlState::Finishing; - case RunControlState::Running: - return to == RunControlState::Stopping - || to == RunControlState::Stopped - || to == RunControlState::Finishing; - case RunControlState::Stopping: - return to == RunControlState::Stopped - || to == RunControlState::Finishing; - case RunControlState::Stopped: - return to == RunControlState::Finishing; - case RunControlState::Finishing: - return to == RunControlState::Finished; - case RunControlState::Finished: - return false; - } - return false; -} - -void RunControlPrivate::checkState(RunControlState expectedState) -{ - if (state != expectedState) - qDebug() << "Unexpected run control state " << stateName(expectedState) - << " have: " << stateName(state); -} - -void RunControlPrivate::setState(RunControlState newState) -{ - if (!isAllowedTransition(state, newState)) - qDebug() << "Invalid run control state transition from " << stateName(state) - << " to " << stateName(newState); - - state = newState; - - debugMessage("Entering state " + stateName(newState)); - - // Extra reporting. - switch (state) { - case RunControlState::Running: - emit q->started(); - break; - case RunControlState::Stopped: - q->setApplicationProcessHandle(Utils::ProcessHandle()); - emit q->stopped(); - break; - case RunControlState::Finished: - emit q->finished(); - debugMessage("All finished. Deleting myself"); - q->deleteLater(); - break; - default: - break; - } -} - -void RunControlPrivate::debugMessage(const QString &msg) -{ - qCDebug(statesLog()) << msg; -} - -void RunControl::appendMessage(const QString &msg, Utils::OutputFormat format) -{ - emit appendMessageRequested(this, msg, format); -} - -// SimpleTargetRunner - -SimpleTargetRunner::SimpleTargetRunner(RunControl *runControl) - : RunWorker(runControl) -{ - setId("SimpleTargetRunner"); - m_runnable = runControl->runnable(); // Default value. Can be overridden using setRunnable. - m_device = runControl->device(); // Default value. Can be overridden using setDevice. - if (auto runConfig = runControl->runConfiguration()) { - if (auto terminalAspect = runConfig->aspect<TerminalAspect>()) - m_useTerminal = terminalAspect->useTerminal(); - } -} - -void SimpleTargetRunner::start() -{ - m_stopReported = false; - m_launcher.disconnect(this); - m_launcher.setUseTerminal(m_useTerminal); - - const bool isDesktop = m_device.isNull() - || m_device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE; - const QString rawDisplayName = m_runnable.displayName(); - const QString displayName = isDesktop - ? QDir::toNativeSeparators(rawDisplayName) - : rawDisplayName; - const QString msg = RunControl::tr("Starting %1 %2...") - .arg(displayName).arg(m_runnable.commandLineArguments); - appendMessage(msg, Utils::NormalMessageFormat); - - if (isDesktop) { - - connect(&m_launcher, &ApplicationLauncher::appendMessage, - this, &SimpleTargetRunner::appendMessage); - connect(&m_launcher, &ApplicationLauncher::processStarted, - this, &SimpleTargetRunner::onProcessStarted); - connect(&m_launcher, &ApplicationLauncher::processExited, - this, &SimpleTargetRunner::onProcessFinished); - connect(&m_launcher, &ApplicationLauncher::error, - this, &SimpleTargetRunner::onProcessError); - - const QString executable = m_runnable.executable; - if (executable.isEmpty()) { - reportFailure(RunControl::tr("No executable specified.")); - } else { - m_launcher.start(m_runnable); - } - - } else { - - connect(&m_launcher, &ApplicationLauncher::reportError, - this, [this](const QString &msg) { - reportFailure(msg); - }); - - connect(&m_launcher, &ApplicationLauncher::remoteStderr, - this, [this](const QString &output) { - appendMessage(output, Utils::StdErrFormatSameLine, false); - }); - - connect(&m_launcher, &ApplicationLauncher::remoteStdout, - this, [this](const QString &output) { - appendMessage(output, Utils::StdOutFormatSameLine, false); - }); - - connect(&m_launcher, &ApplicationLauncher::finished, - this, [this] { - m_launcher.disconnect(this); - reportStopped(); - }); - - connect(&m_launcher, &ApplicationLauncher::processStarted, - this, [this] { - appendMessage("Application launcher started", Utils::NormalMessageFormat); -// reportStarted(); - }); - - connect(&m_launcher, &ApplicationLauncher::processExited, - this, [this] { - m_launcher.disconnect(this); - reportStopped(); - }); - - connect(&m_launcher, &ApplicationLauncher::remoteProcessStarted, - this, [this] { - reportStarted(); - }); - - connect(&m_launcher, &ApplicationLauncher::reportProgress, - this, [this](const QString &progressString) { - appendMessage(progressString, Utils::NormalMessageFormat); - }); - - m_launcher.start(m_runnable, device()); - } -} - -void SimpleTargetRunner::stop() -{ - m_launcher.stop(); -} - -void SimpleTargetRunner::onProcessStarted() -{ - // Console processes only know their pid after being started - ProcessHandle pid = m_launcher.applicationPID(); - runControl()->setApplicationProcessHandle(pid); - pid.activate(); - reportStarted(); -} - -void SimpleTargetRunner::onProcessFinished(int exitCode, QProcess::ExitStatus status) -{ - QString msg; - if (status == QProcess::CrashExit) - msg = tr("%1 crashed."); - else - msg = tr("%2 exited with code %1").arg(exitCode); - appendMessage(msg.arg(m_runnable.displayName()), Utils::NormalMessageFormat); - if (!m_stopReported) { - m_stopReported = true; - reportStopped(); - } -} - -void SimpleTargetRunner::onProcessError(QProcess::ProcessError error) -{ - if (error == QProcess::Timedout) - return; // No actual change on the process side. - QString msg = userMessageForProcessError(error, m_runnable.displayName()); - appendMessage(msg, Utils::NormalMessageFormat); - if (!m_stopReported) { - m_stopReported = true; - reportStopped(); - } -} - -IDevice::ConstPtr SimpleTargetRunner::device() const -{ - return m_device; -} - -void SimpleTargetRunner::setRunnable(const Runnable &runnable) -{ - m_runnable = runnable; -} - -void SimpleTargetRunner::setDevice(const IDevice::ConstPtr &device) -{ - m_device = device; -} - -// RunWorkerPrivate - -RunWorkerPrivate::RunWorkerPrivate(RunWorker *runWorker, RunControl *runControl) - : q(runWorker), runControl(runControl) -{ - runControl->d->m_workers.append(runWorker); -} - -bool RunWorkerPrivate::canStart() const -{ - if (state != RunWorkerState::Initialized) - return false; - for (RunWorker *worker : startDependencies) { - QTC_ASSERT(worker, continue); - if (worker->d->state != RunWorkerState::Done - && worker->d->state != RunWorkerState::Running) - return false; - } - return true; -} - -bool RunWorkerPrivate::canStop() const -{ - if (state != RunWorkerState::Starting && state != RunWorkerState::Running) - return false; - for (RunWorker *worker : stopDependencies) { - QTC_ASSERT(worker, continue); - if (worker->d->state != RunWorkerState::Done) - return false; - } - return true; -} - -void RunWorkerPrivate::timerEvent(QTimerEvent *ev) -{ - if (ev->timerId() == startWatchdogTimerId) { - if (startWatchdogCallback) { - killStartWatchdog(); - startWatchdogCallback(); - } else { - q->reportFailure(RunWorker::tr("Worker start timed out.")); - } - return; - } - if (ev->timerId() == stopWatchdogTimerId) { - if (stopWatchdogCallback) { - killStopWatchdog(); - stopWatchdogCallback(); - } else { - q->reportFailure(RunWorker::tr("Worker stop timed out.")); - } - return; - } -} - -/*! - \class ProjectExplorer::RunWorker - - \brief The RunWorker class encapsulates a task that forms part, or - the whole of the operation of a tool for a certain \c RunConfiguration - according to some \c RunMode. - - A typical example for a \c RunWorker is a process, either the - application process itself, or a helper process, such as a watchdog - or a log parser. - - A \c RunWorker has a simple state model covering the \c Initialized, - \c Starting, \c Running, \c Stopping, and \c Done states. - - In the course of the operation of tools several \c RunWorkers - may co-operate and form a combined state that is presented - to the user as \c RunControl, with direct interaction made - possible through the buttons in the \uicontrol{Application Output} - pane. - - RunWorkers are typically created together with their RunControl. - The startup order of RunWorkers under a RunControl can be - specified by making a RunWorker dependent on others. - - When a RunControl starts, it calls \c initiateStart() on RunWorkers - with fulfilled dependencies until all workers are \c Running, or in case - of short-lived helper tasks, \c Done. - - A RunWorker can stop spontaneously, for example when the main application - process ends. In this case, it typically calls \c initiateStop() - on its RunControl, which in turn passes this to all sibling - RunWorkers. - - Pressing the stop button in the \uicontrol{Application Output} pane - also calls \c initiateStop on the RunControl. -*/ - -RunWorker::RunWorker(RunControl *runControl) - : d(std::make_unique<RunWorkerPrivate>(this, runControl)) -{ } - -RunWorker::~RunWorker() = default; - -/*! - * This function is called by the RunControl once all dependencies - * are fulfilled. - */ -void RunWorker::initiateStart() -{ - d->startStartWatchdog(); - d->runControl->d->debugMessage("Initiate start for " + d->id); - start(); -} - -/*! - * This function has to be called by a RunWorker implementation - * to notify its RunControl about the successful start of this RunWorker. - * - * The RunControl may start other RunWorkers in response. - */ -void RunWorker::reportStarted() -{ - d->killStartWatchdog(); - d->runControl->d->onWorkerStarted(this); - emit started(); -} - -/*! - * This function is called by the RunControl in its own \c initiateStop - * implementation, which is triggered in response to pressing the - * stop button in the \uicontrol{Application Output} pane or on direct - * request of one of the sibling RunWorkers. - */ -void RunWorker::initiateStop() -{ - d->startStopWatchdog(); - d->runControl->d->debugMessage("Initiate stop for " + d->id); - stop(); -} - -/*! - * This function has to be called by a RunWorker implementation - * to notify its RunControl about this RunWorker having stopped. - * - * The stop can be spontaneous, or in response to an initiateStop() - * or an initiateFinish() call. - * - * The RunControl will adjust its global state in response. - */ -void RunWorker::reportStopped() -{ - d->killStopWatchdog(); - d->runControl->d->onWorkerStopped(this); - emit stopped(); -} - -/*! - * This function can be called by a RunWorker implementation for short-lived - * tasks to notify its RunControl about this task being successful finished. - * Dependent startup tasks can proceed, in cases of spontaneous or scheduled - * stops, the effect is the same as \c reportStopped(). - * - */ -void RunWorker::reportDone() -{ - d->killStartWatchdog(); - d->killStopWatchdog(); - switch (d->state) { - case RunWorkerState::Initialized: - QTC_CHECK(false); - d->state = RunWorkerState::Done; - break; - case RunWorkerState::Starting: - reportStarted(); - reportStopped(); - break; - case RunWorkerState::Running: - case RunWorkerState::Stopping: - reportStopped(); - break; - case RunWorkerState::Done: - break; - } -} - -/*! - * This function can be called by a RunWorker implementation to - * signal a problem in the operation in this worker. The - * RunControl will start to ramp down through initiateStop(). - */ -void RunWorker::reportFailure(const QString &msg) -{ - d->killStartWatchdog(); - d->killStopWatchdog(); - d->runControl->d->onWorkerFailed(this, msg); -} - -/*! - * Appends a message in the specified \a format to - * the owning RunControl's \uicontrol{Application Output} pane. - */ -void RunWorker::appendMessage(const QString &msg, OutputFormat format, bool appendNewLine) -{ - if (!appendNewLine || msg.endsWith('\n')) - d->runControl->appendMessage(msg, format); - else - d->runControl->appendMessage(msg + '\n', format); -} - -IDevice::ConstPtr RunWorker::device() const -{ - return d->runControl->device(); -} - -const Runnable &RunWorker::runnable() const -{ - return d->runControl->runnable(); -} - -Core::Id RunWorker::runMode() const -{ - return d->runControl->runMode(); -} - -void RunWorker::addStartDependency(RunWorker *dependency) -{ - d->startDependencies.append(dependency); -} - -void RunWorker::addStopDependency(RunWorker *dependency) -{ - d->stopDependencies.append(dependency); -} - -RunControl *RunWorker::runControl() const -{ - return d->runControl; -} - -void RunWorker::setId(const QString &id) -{ - d->id = id; -} - -void RunWorker::setStartTimeout(int ms, const std::function<void()> &callback) -{ - d->startWatchdogInterval = ms; - d->startWatchdogCallback = callback; -} - -void RunWorker::setStopTimeout(int ms, const std::function<void()> &callback) -{ - d->stopWatchdogInterval = ms; - d->stopWatchdogCallback = callback; -} - -void RunWorker::recordData(const QString &channel, const QVariant &data) -{ - d->data[channel] = data; -} - -QVariant RunWorker::recordedData(const QString &channel) const -{ - return d->data[channel]; -} - -void RunWorker::setSupportsReRunning(bool reRunningSupported) -{ - d->supportsReRunning = reRunningSupported; -} - -bool RunWorker::supportsReRunning() const -{ - return d->supportsReRunning; -} - -QString RunWorker::userMessageForProcessError(QProcess::ProcessError error, const QString &program) -{ - QString failedToStart = tr("The process failed to start."); - QString msg = tr("An unknown error in the process occurred."); - switch (error) { - case QProcess::FailedToStart: - msg = failedToStart + ' ' + tr("Either the " - "invoked program \"%1\" is missing, or you may have insufficient " - "permissions to invoke the program.").arg(program); - break; - case QProcess::Crashed: - msg = tr("The process was ended forcefully."); - break; - case QProcess::Timedout: - // "The last waitFor...() function timed out. " - // "The state of QProcess is unchanged, and you can try calling " - // "waitFor...() again." - return QString(); // sic! - case QProcess::WriteError: - msg = tr("An error occurred when attempting to write " - "to the process. For example, the process may not be running, " - "or it may have closed its input channel."); - break; - case QProcess::ReadError: - msg = tr("An error occurred when attempting to read from " - "the process. For example, the process may not be running."); - break; - case QProcess::UnknownError: - break; - } - return msg; -} - -bool RunWorker::isEssential() const -{ - return d->essential; -} - -void RunWorker::setEssential(bool essential) -{ - d->essential = essential; -} - -void RunWorker::start() -{ - reportStarted(); -} - -void RunWorker::stop() -{ - reportStopped(); -} - } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h index c0443ae07e..3e5cbc6b15 100644 --- a/src/plugins/projectexplorer/runconfiguration.h +++ b/src/plugins/projectexplorer/runconfiguration.h @@ -50,7 +50,7 @@ namespace Utils { class OutputFormatter; } namespace ProjectExplorer { class BuildConfiguration; class GlobalOrProjectAspect; -class Node; +class Runnable; class RunConfigurationFactory; class RunConfiguration; class RunConfigurationCreationInfo; @@ -129,24 +129,8 @@ private: ISettingsAspect *m_globalSettings = nullptr; // Not owned. }; -class PROJECTEXPLORER_EXPORT Runnable -{ -public: - Runnable() = default; - - QString executable; - QString commandLineArguments; - QString workingDirectory; - Utils::Environment environment; - IDevice::ConstPtr device; // Override the kit's device. Keep unset by default. - QHash<Core::Id, QVariant> extraData; - - // FIXME: Not necessarily a display name - QString displayName() const { return executable; } -}; - // Documentation inside. -class PROJECTEXPLORER_EXPORT RunConfiguration : public StatefulProjectConfiguration +class PROJECTEXPLORER_EXPORT RunConfiguration : public ProjectConfiguration { Q_OBJECT @@ -155,7 +139,10 @@ public: bool isActive() const override; - QString disabledReason() const override; + bool isEnabled() const { return m_isEnabled; } + void setEnabled(bool enabled); + + virtual QString disabledReason() const; virtual QWidget *createConfigurationWidget(); @@ -173,6 +160,10 @@ public: bool fromMap(const QVariantMap &map) override; QVariantMap toMap() const override; + using ExecutableGetter = std::function<Utils::FilePath()>; + void setExecutableGetter(const ExecutableGetter &exeGetter); + Utils::FilePath executable() const; + virtual Runnable runnable() const; // Return a handle to the build system target that created this run configuration. @@ -182,7 +173,6 @@ public: BuildTargetInfo buildTargetInfo() const; static RunConfiguration *startupRunConfiguration(); - virtual bool canRunForNode(const ProjectExplorer::Node *) const { return false; } template <class T = ISettingsAspect> T *currentSettings(Core::Id id) const { @@ -200,6 +190,7 @@ public: signals: void requestRunActionsUpdate(); void configurationFinished(); + void enabledChanged(); protected: RunConfiguration(Target *target, Core::Id id); @@ -221,7 +212,9 @@ private: friend class RunConfigurationCreationInfo; QString m_buildKey; + bool m_isEnabled = false; std::function<Utils::OutputFormatter *(Project *)> m_outputFormatterCreator; + ExecutableGetter m_executableGetter; }; class RunConfigurationCreationInfo @@ -235,6 +228,7 @@ public: QString buildKey; QString displayName; QString displayNameUniquifier; + Utils::FilePath projectFilePath; CreationMode creationMode = AlwaysCreate; bool useTerminal = false; }; @@ -267,25 +261,13 @@ protected: return new RunConfig(t, runConfigBaseId); }; m_runConfigBaseId = runConfigBaseId; - m_ownTypeChecker = [](RunConfiguration *runConfig) { - return qobject_cast<RunConfig *>(runConfig) != nullptr; - }; } void addSupportedProjectType(Core::Id id); void addSupportedTargetDeviceType(Core::Id id); void setDecorateDisplayNames(bool on); - template<class Worker> - RunWorkerFactory *addRunWorkerFactory(Core::Id runMode) - { - return addRunWorkerFactoryHelper(runMode, [](RunControl *rc) { return new Worker(rc); }); - } - private: - RunWorkerFactory *addRunWorkerFactoryHelper - (Core::Id runMode, const std::function<RunWorker *(RunControl *)> &creator); - bool canHandle(Target *target) const; friend class RunConfigurationCreationInfo; @@ -294,8 +276,6 @@ private: QList<Core::Id> m_supportedProjectTypes; QList<Core::Id> m_supportedTargetDeviceTypes; bool m_decorateDisplayNames = false; - QList<RunWorkerFactory *> m_ownedRunWorkerFactories; - std::function<bool(RunConfiguration *)> m_ownTypeChecker; }; class PROJECTEXPLORER_EXPORT FixedRunConfigurationFactory : public RunConfigurationFactory @@ -311,227 +291,4 @@ private: const bool m_decorateTargetName; }; -class PROJECTEXPLORER_EXPORT RunWorker : public QObject -{ - Q_OBJECT - -public: - explicit RunWorker(RunControl *runControl); - ~RunWorker() override; - - RunControl *runControl() const; - - void addStartDependency(RunWorker *dependency); - void addStopDependency(RunWorker *dependency); - - void setId(const QString &id); - - void setStartTimeout(int ms, const std::function<void()> &callback = {}); - void setStopTimeout(int ms, const std::function<void()> &callback = {}); - - void recordData(const QString &channel, const QVariant &data); - QVariant recordedData(const QString &channel) const; - - // Part of read-only interface of RunControl for convenience. - void appendMessage(const QString &msg, Utils::OutputFormat format, bool appendNewLine = true); - IDevice::ConstPtr device() const; - const Runnable &runnable() const; - Core::Id runMode() const; - - // States - void initiateStart(); - void reportStarted(); - - void initiateStop(); - void reportStopped(); - - void reportDone(); - - void reportFailure(const QString &msg = QString()); - void setSupportsReRunning(bool reRunningSupported); - bool supportsReRunning() const; - - static QString userMessageForProcessError(QProcess::ProcessError, const QString &programName); - - bool isEssential() const; - void setEssential(bool essential); - -signals: - void started(); - void stopped(); - -protected: - void virtual start(); - void virtual stop(); - void virtual onFinished() {} - -private: - friend class Internal::RunControlPrivate; - friend class Internal::RunWorkerPrivate; - const std::unique_ptr<Internal::RunWorkerPrivate> d; -}; - -class PROJECTEXPLORER_EXPORT RunWorkerFactory -{ -public: - using WorkerCreator = std::function<RunWorker *(RunControl *)>; - using Constraint = std::function<bool(RunConfiguration *)>; - - RunWorkerFactory(); - virtual ~RunWorkerFactory(); - - bool canRun(RunConfiguration *runConfiguration, Core::Id runMode) const; - - void setProducer(const WorkerCreator &producer); - void addConstraint(const Constraint &constraint); - void addSupportedRunMode(Core::Id runMode); - - WorkerCreator producer() const { return m_producer; } - -private: - // FIXME: That's temporary until ownership has been transferred to - // the individual plugins. - friend class ProjectExplorerPlugin; - static void destroyRemainingRunWorkerFactories(); - - QList<Core::Id> m_supportedRunModes; - QList<Constraint> m_constraints; - WorkerCreator m_producer; -}; - -/** - * A RunControl controls the running of an application or tool - * on a target device. It controls start and stop, and handles - * application output. - * - * RunControls are created by RunControlFactories. - */ - -class PROJECTEXPLORER_EXPORT RunControl : public QObject -{ - Q_OBJECT - -public: - RunControl(RunConfiguration *runConfiguration, Core::Id mode); - RunControl(const IDevice::ConstPtr &device, Core::Id mode); - ~RunControl() override; - - void initiateStart(); - void initiateReStart(); - void initiateStop(); - void forceStop(); - void initiateFinish(); - - bool promptToStop(bool *optionalPrompt = nullptr) const; - void setPromptToStop(const std::function<bool(bool *)> &promptToStop); - - bool supportsReRunning() const; - - virtual QString displayName() const; - void setDisplayName(const QString &displayName); - - bool isRunning() const; - bool isStarting() const; - bool isStopping() const; - bool isStopped() const; - - void setIcon(const Utils::Icon &icon); - Utils::Icon icon() const; - - Utils::ProcessHandle applicationProcessHandle() const; - void setApplicationProcessHandle(const Utils::ProcessHandle &handle); - IDevice::ConstPtr device() const; - - RunConfiguration *runConfiguration() const; - Project *project() const; - - Utils::OutputFormatter *outputFormatter() const; - Core::Id runMode() const; - - const Runnable &runnable() const; - void setRunnable(const Runnable &runnable); - - virtual void appendMessage(const QString &msg, Utils::OutputFormat format); - - static bool showPromptToStopDialog(const QString &title, const QString &text, - const QString &stopButtonText = QString(), - const QString &cancelButtonText = QString(), - bool *prompt = nullptr); - - RunWorker *createWorker(Core::Id id); - - using WorkerCreator = RunWorkerFactory::WorkerCreator; - using Constraint = RunWorkerFactory::Constraint; - - static void registerWorkerCreator(Core::Id id, const WorkerCreator &workerCreator); - - static void registerWorker(Core::Id runMode, const WorkerCreator &producer, - const Constraint &constraint = {}) - { - auto factory = new RunWorkerFactory; - factory->setProducer(producer); - factory->addSupportedRunMode(runMode); - factory->addConstraint(constraint); - } - template <class Worker> - static void registerWorker(Core::Id runMode, const Constraint &constraint) - { - auto factory = new RunWorkerFactory; - factory->setProducer([](RunControl *rc) { return new Worker(rc); }); - factory->addSupportedRunMode(runMode); - factory->addConstraint(constraint); - } - - static WorkerCreator producer(RunConfiguration *runConfiguration, Core::Id runMode); - -signals: - void appendMessageRequested(ProjectExplorer::RunControl *runControl, - const QString &msg, Utils::OutputFormat format); - void aboutToStart(); - void started(); - void stopped(); - void finished(); - void applicationProcessHandleChanged(QPrivateSignal); // Use setApplicationProcessHandle - -private: - friend class RunWorker; - friend class Internal::RunWorkerPrivate; - - const std::unique_ptr<Internal::RunControlPrivate> d; -}; - - -/** - * A simple TargetRunner for cases where a plain ApplicationLauncher is - * sufficient for running purposes. - */ - -class PROJECTEXPLORER_EXPORT SimpleTargetRunner : public RunWorker -{ - Q_OBJECT - -public: - explicit SimpleTargetRunner(RunControl *runControl); - - void setRunnable(const Runnable &runnable); - - void setDevice(const IDevice::ConstPtr &device); - IDevice::ConstPtr device() const; - -protected: - void start() override; - void stop() override; - -private: - void onProcessStarted(); - void onProcessFinished(int exitCode, QProcess::ExitStatus status); - void onProcessError(QProcess::ProcessError error); - - ApplicationLauncher m_launcher; - Runnable m_runnable; - IDevice::ConstPtr m_device; - bool m_stopReported = false; - bool m_useTerminal = false; -}; - } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp index 06aa1e583e..fe890b3a29 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp @@ -56,6 +56,9 @@ TerminalAspect::TerminalAspect() setDisplayName(tr("Terminal")); setId("TerminalAspect"); setSettingsKey("RunConfiguration.UseTerminal"); + calculateUseTerminal(); + connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged, + this, &TerminalAspect::calculateUseTerminal); } void TerminalAspect::addToConfigurationLayout(QFormLayout *layout) @@ -90,13 +93,16 @@ void TerminalAspect::toMap(QVariantMap &data) const data.insert(settingsKey(), m_useTerminal); } -bool TerminalAspect::useTerminal() const -{ - return m_useTerminal; -} - -void TerminalAspect::setUseTerminal(bool useTerminal) +void TerminalAspect::calculateUseTerminal() { + if (m_userSet) + return; + bool useTerminal; + switch (ProjectExplorerPlugin::projectExplorerSettings().terminalMode) { + case Internal::TerminalMode::On: useTerminal = true; break; + case Internal::TerminalMode::Off: useTerminal = false; break; + case Internal::TerminalMode::Smart: useTerminal = m_useTerminalHint; + } if (m_useTerminal != useTerminal) { m_useTerminal = useTerminal; emit changed(); @@ -105,6 +111,17 @@ void TerminalAspect::setUseTerminal(bool useTerminal) m_checkBox->setChecked(m_useTerminal); } +bool TerminalAspect::useTerminal() const +{ + return m_useTerminal; +} + +void TerminalAspect::setUseTerminalHint(bool hint) +{ + m_useTerminalHint = hint; + calculateUseTerminal(); +} + bool TerminalAspect::isUserSet() const { return m_userSet; @@ -114,8 +131,7 @@ bool TerminalAspect::isUserSet() const \class ProjectExplorer::WorkingDirectoryAspect */ -WorkingDirectoryAspect::WorkingDirectoryAspect(EnvironmentAspect *envAspect) - : m_envAspect(envAspect) +WorkingDirectoryAspect::WorkingDirectoryAspect() { setDisplayName(tr("Working Directory")); setId("WorkingDirectoryAspect"); @@ -125,11 +141,6 @@ WorkingDirectoryAspect::WorkingDirectoryAspect(EnvironmentAspect *envAspect) void WorkingDirectoryAspect::addToConfigurationLayout(QFormLayout *layout) { QTC_CHECK(!m_chooser); - m_resetButton = new QToolButton(layout->parentWidget()); - m_resetButton->setToolTip(tr("Reset to Default")); - m_resetButton->setIcon(Utils::Icons::RESET.icon()); - connect(m_resetButton.data(), &QAbstractButton::clicked, this, &WorkingDirectoryAspect::resetPath); - m_chooser = new PathChooser(layout->parentWidget()); m_chooser->setHistoryCompleter(settingsKey()); m_chooser->setExpectedKind(Utils::PathChooser::Directory); @@ -142,6 +153,10 @@ void WorkingDirectoryAspect::addToConfigurationLayout(QFormLayout *layout) m_resetButton->setEnabled(m_workingDirectory != m_defaultWorkingDirectory); }); + m_resetButton = new QToolButton(layout->parentWidget()); + m_resetButton->setToolTip(tr("Reset to Default")); + m_resetButton->setIcon(Utils::Icons::RESET.icon()); + connect(m_resetButton.data(), &QAbstractButton::clicked, this, &WorkingDirectoryAspect::resetPath); m_resetButton->setEnabled(m_workingDirectory != m_defaultWorkingDirectory); if (m_envAspect) { @@ -157,6 +172,11 @@ void WorkingDirectoryAspect::addToConfigurationLayout(QFormLayout *layout) layout->addRow(tr("Working directory:"), hbox); } +void WorkingDirectoryAspect::acquaintSiblings(const ProjectConfigurationAspects &siblings) +{ + m_envAspect = siblings.aspect<EnvironmentAspect>(); +} + QString WorkingDirectoryAspect::keyForDefaultWd() const { return settingsKey() + ".default"; @@ -169,8 +189,8 @@ void WorkingDirectoryAspect::resetPath() void WorkingDirectoryAspect::fromMap(const QVariantMap &map) { - m_workingDirectory = FileName::fromString(map.value(settingsKey()).toString()); - m_defaultWorkingDirectory = FileName::fromString(map.value(keyForDefaultWd()).toString()); + m_workingDirectory = FilePath::fromString(map.value(settingsKey()).toString()); + m_defaultWorkingDirectory = FilePath::fromString(map.value(keyForDefaultWd()).toString()); if (m_workingDirectory.isEmpty()) m_workingDirectory = m_defaultWorkingDirectory; @@ -187,32 +207,32 @@ void WorkingDirectoryAspect::toMap(QVariantMap &data) const data.insert(keyForDefaultWd(), m_defaultWorkingDirectory.toString()); } -FileName WorkingDirectoryAspect::workingDirectory(const MacroExpander *expander) const +FilePath WorkingDirectoryAspect::workingDirectory(const MacroExpander *expander) const { const Utils::Environment env = m_envAspect ? m_envAspect->environment() : Utils::Environment::systemEnvironment(); QString workingDir = m_workingDirectory.toUserOutput(); if (expander) workingDir = expander->expandProcessArgs(workingDir); - return FileName::fromString(PathChooser::expandedDirectory(workingDir, env, QString())); + return FilePath::fromString(PathChooser::expandedDirectory(workingDir, env, QString())); } -FileName WorkingDirectoryAspect::defaultWorkingDirectory() const +FilePath WorkingDirectoryAspect::defaultWorkingDirectory() const { return m_defaultWorkingDirectory; } -FileName WorkingDirectoryAspect::unexpandedWorkingDirectory() const +FilePath WorkingDirectoryAspect::unexpandedWorkingDirectory() const { return m_workingDirectory; } -void WorkingDirectoryAspect::setDefaultWorkingDirectory(const FileName &defaultWorkingDir) +void WorkingDirectoryAspect::setDefaultWorkingDirectory(const FilePath &defaultWorkingDir) { if (defaultWorkingDir == m_defaultWorkingDirectory) return; - Utils::FileName oldDefaultDir = m_defaultWorkingDirectory; + Utils::FilePath oldDefaultDir = m_defaultWorkingDirectory; m_defaultWorkingDirectory = defaultWorkingDir; if (m_chooser) m_chooser->setBaseFileName(m_defaultWorkingDirectory); @@ -360,7 +380,7 @@ void ExecutableAspect::makeOverridable(const QString &overridingKey, const QStri this, &ExecutableAspect::changed); } -FileName ExecutableAspect::executable() const +FilePath ExecutableAspect::executable() const { if (m_alternativeExecutable && m_alternativeExecutable->isChecked()) return m_alternativeExecutable->fileName(); @@ -385,7 +405,7 @@ void ExecutableAspect::setPlaceHolderText(const QString &placeHolderText) m_executable.setPlaceHolderText(placeHolderText); } -void ExecutableAspect::setExecutable(const FileName &executable) +void ExecutableAspect::setExecutable(const FilePath &executable) { m_executable.setValue(executable.toString()); } diff --git a/src/plugins/projectexplorer/runconfigurationaspects.h b/src/plugins/projectexplorer/runconfigurationaspects.h index be40af6d29..77973f65f4 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.h +++ b/src/plugins/projectexplorer/runconfigurationaspects.h @@ -46,7 +46,7 @@ public: void addToConfigurationLayout(QFormLayout *layout) override; bool useTerminal() const; - void setUseTerminal(bool useTerminal); + void setUseTerminalHint(bool useTerminal); bool isUserSet() const; @@ -54,6 +54,9 @@ private: void fromMap(const QVariantMap &map) override; void toMap(QVariantMap &map) const override; + void calculateUseTerminal(); + + bool m_useTerminalHint = false; bool m_useTerminal = false; bool m_userSet = false; QPointer<QCheckBox> m_checkBox; // Owned by RunConfigWidget @@ -64,14 +67,15 @@ class PROJECTEXPLORER_EXPORT WorkingDirectoryAspect : public ProjectConfiguratio Q_OBJECT public: - explicit WorkingDirectoryAspect(EnvironmentAspect *envAspect = nullptr); + WorkingDirectoryAspect(); void addToConfigurationLayout(QFormLayout *layout) override; + void acquaintSiblings(const ProjectConfigurationAspects &) override; - Utils::FileName workingDirectory(const Utils::MacroExpander *expander) const; - Utils::FileName defaultWorkingDirectory() const; - Utils::FileName unexpandedWorkingDirectory() const; - void setDefaultWorkingDirectory(const Utils::FileName &defaultWorkingDir); + Utils::FilePath workingDirectory(const Utils::MacroExpander *expander) const; + Utils::FilePath defaultWorkingDirectory() const; + Utils::FilePath unexpandedWorkingDirectory() const; + void setDefaultWorkingDirectory(const Utils::FilePath &defaultWorkingDir); Utils::PathChooser *pathChooser() const; private: @@ -81,9 +85,9 @@ private: void resetPath(); QString keyForDefaultWd() const; - EnvironmentAspect * const m_envAspect = nullptr; - Utils::FileName m_workingDirectory; - Utils::FileName m_defaultWorkingDirectory; + EnvironmentAspect *m_envAspect = nullptr; + Utils::FilePath m_workingDirectory; + Utils::FilePath m_defaultWorkingDirectory; QPointer<Utils::PathChooser> m_chooser; QPointer<QToolButton> m_resetButton; }; @@ -137,8 +141,8 @@ public: ExecutableAspect(); ~ExecutableAspect() override; - Utils::FileName executable() const; - void setExecutable(const Utils::FileName &executable); + Utils::FilePath executable() const; + void setExecutable(const Utils::FilePath &executable); void setSettingsKey(const QString &key); void makeOverridable(const QString &overridingKey, const QString &useOverridableKey); diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp new file mode 100644 index 0000000000..f90a8fd48c --- /dev/null +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -0,0 +1,1563 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "runcontrol.h" + +#include "project.h" +#include "target.h" +#include "toolchain.h" +#include "abi.h" +#include "buildconfiguration.h" +#include "environmentaspect.h" +#include "kitinformation.h" +#include "runconfigurationaspects.h" +#include "session.h" +#include "kitinformation.h" + +#include <utils/algorithm.h> +#include <utils/checkablemessagebox.h> +#include <utils/detailswidget.h> +#include <utils/outputformatter.h> +#include <utils/qtcassert.h> +#include <utils/utilsicons.h> + +#include <coreplugin/icontext.h> +#include <coreplugin/icore.h> +#include <coreplugin/variablechooser.h> + +#include <QDir> +#include <QFormLayout> +#include <QHash> +#include <QPushButton> +#include <QTimer> +#include <QLoggingCategory> +#include <QSettings> + +#if defined (WITH_JOURNALD) +#include "journaldwatcher.h" +#endif + +using namespace Utils; +using namespace ProjectExplorer::Internal; + +namespace { +Q_LOGGING_CATEGORY(statesLog, "qtc.projectmanager.states", QtWarningMsg) +} + +namespace ProjectExplorer { + +// RunWorkerFactory + +static QList<RunWorkerFactory *> g_runWorkerFactories; + +RunWorkerFactory::RunWorkerFactory() +{ + g_runWorkerFactories.append(this); +} + +RunWorkerFactory::~RunWorkerFactory() +{ + g_runWorkerFactories.removeOne(this); +} + +bool RunWorkerFactory::canRun(RunConfiguration *runConfiguration, Core::Id runMode) const +{ + if (!m_supportedRunModes.contains(runMode)) + return false; + + if (!m_supportedRunConfigurations.isEmpty()) { + if (!m_supportedRunConfigurations.contains(runConfiguration->id())) + return false; + } + + for (const Constraint &constraint : m_constraints) { + if (!constraint(runConfiguration)) + return false; + } + + return true; +} + +void RunWorkerFactory::setProducer(const WorkerCreator &producer) +{ + m_producer = producer; +} + +void RunWorkerFactory::addConstraint(const Constraint &constraint) +{ + // Default constructed Constraints are not worth keeping. + // FIXME: Make it a QTC_ASSERT once there is no code path + // using this "feature" anymore. + if (!constraint) + return; + m_constraints.append(constraint); +} + +void RunWorkerFactory::addSupportedRunMode(Core::Id runMode) +{ + m_supportedRunModes.append(runMode); +} + +void RunWorkerFactory::setSupportedRunConfigurations(const QList<Core::Id> &ids) +{ + m_supportedRunConfigurations = ids; +} + +void RunWorkerFactory::addSupportedRunConfiguration(Core::Id id) +{ + m_supportedRunConfigurations.append(id); +} + +void RunWorkerFactory::destroyRemainingRunWorkerFactories() +{ + qDeleteAll(g_runWorkerFactories); +} + +/*! + \class ProjectExplorer::RunControl + \brief The RunControl class instances represent one item that is run. +*/ + +/*! + \fn QIcon ProjectExplorer::RunControl::icon() const + Returns the icon to be shown in the Outputwindow. + + TODO the icon differs currently only per "mode", so this is more flexible + than it needs to be. +*/ + + +namespace Internal { + +enum class RunWorkerState +{ + Initialized, Starting, Running, Stopping, Done +}; + +static QString stateName(RunWorkerState s) +{ +# define SN(x) case x: return QLatin1String(#x); + switch (s) { + SN(RunWorkerState::Initialized) + SN(RunWorkerState::Starting) + SN(RunWorkerState::Running) + SN(RunWorkerState::Stopping) + SN(RunWorkerState::Done) + } + return QString("<unknown: %1>").arg(int(s)); +# undef SN +} + +class RunWorkerPrivate : public QObject +{ +public: + RunWorkerPrivate(RunWorker *runWorker, RunControl *runControl); + + bool canStart() const; + bool canStop() const; + void timerEvent(QTimerEvent *ev) override; + + void killStartWatchdog() + { + if (startWatchdogTimerId != -1) { + killTimer(startWatchdogTimerId); + startWatchdogTimerId = -1; + } + } + + void killStopWatchdog() + { + if (stopWatchdogTimerId != -1) { + killTimer(stopWatchdogTimerId); + stopWatchdogTimerId = -1; + } + } + + void startStartWatchdog() + { + killStartWatchdog(); + killStopWatchdog(); + + if (startWatchdogInterval != 0) + startWatchdogTimerId = startTimer(startWatchdogInterval); + } + + void startStopWatchdog() + { + killStopWatchdog(); + killStartWatchdog(); + + if (stopWatchdogInterval != 0) + stopWatchdogTimerId = startTimer(stopWatchdogInterval); + } + + RunWorker *q; + RunWorkerState state = RunWorkerState::Initialized; + const QPointer<RunControl> runControl; + QList<RunWorker *> startDependencies; + QList<RunWorker *> stopDependencies; + QString id; + + QVariantMap data; + int startWatchdogInterval = 0; + int startWatchdogTimerId = -1; + std::function<void()> startWatchdogCallback; + int stopWatchdogInterval = 0; // 5000; + int stopWatchdogTimerId = -1; + std::function<void()> stopWatchdogCallback; + bool supportsReRunning = true; + bool essential = false; +}; + +enum class RunControlState +{ + Initialized, // Default value after creation. + Starting, // Actual process/tool starts. + Running, // All good and running. + Stopping, // initiateStop() was called, stop application/tool + Stopped, // all good, but stopped. Can possibly be re-started + Finishing, // Application tab manually closed + Finished // Final state, will self-destruct with deleteLater() +}; + +static QString stateName(RunControlState s) +{ +# define SN(x) case x: return QLatin1String(#x); + switch (s) { + SN(RunControlState::Initialized) + SN(RunControlState::Starting) + SN(RunControlState::Running) + SN(RunControlState::Stopping) + SN(RunControlState::Stopped) + SN(RunControlState::Finishing) + SN(RunControlState::Finished) + } + return QString("<unknown: %1>").arg(int(s)); +# undef SN +} + +class RunControlPrivate : public QObject +{ +public: + RunControlPrivate(RunControl *parent, Core::Id mode) + : q(parent), runMode(mode) + { + icon = Icons::RUN_SMALL_TOOLBAR; + outputFormatter = new OutputFormatter(); + } + + ~RunControlPrivate() override + { + QTC_CHECK(state == RunControlState::Finished || state == RunControlState::Initialized); + disconnect(); + q = nullptr; + qDeleteAll(m_workers); + m_workers.clear(); + delete outputFormatter; + } + + Q_ENUM(RunControlState) + + void checkState(RunControlState expectedState); + void setState(RunControlState state); + + void debugMessage(const QString &msg); + + void initiateStart(); + void initiateReStart(); + void continueStart(); + void initiateStop(); + void forceStop(); + void continueStopOrFinish(); + void initiateFinish(); + + void onWorkerStarted(RunWorker *worker); + void onWorkerStopped(RunWorker *worker); + void onWorkerFailed(RunWorker *worker, const QString &msg); + + void showError(const QString &msg); + + static bool isAllowedTransition(RunControlState from, RunControlState to); + bool supportsReRunning() const; + + RunControl *q; + QString displayName; + Runnable runnable; + IDevice::ConstPtr device; + Core::Id runMode; + Utils::Icon icon; + MacroExpander *macroExpander; + QPointer<RunConfiguration> runConfiguration; // Not owned. Avoid use. + Kit *kit = nullptr; // Not owned. + QPointer<Target> target; // Not owned. + QPointer<Project> project; // Not owned. + QPointer<Utils::OutputFormatter> outputFormatter = nullptr; + std::function<bool(bool*)> promptToStop; + std::vector<RunWorkerFactory> m_factories; + + // A handle to the actual application process. + Utils::ProcessHandle applicationProcessHandle; + + RunControlState state = RunControlState::Initialized; + + QList<QPointer<RunWorker>> m_workers; +}; + +} // Internal + +using namespace Internal; + +RunControl::RunControl(Core::Id mode) : + d(std::make_unique<RunControlPrivate>(this, mode)) +{ +} + +void RunControl::setRunConfiguration(RunConfiguration *runConfig) +{ + QTC_ASSERT(runConfig, return); + QTC_CHECK(!d->runConfiguration); + d->runConfiguration = runConfig; + d->runnable = runConfig->runnable(); + d->displayName = runConfig->displayName(); + if (auto outputFormatter = runConfig->createOutputFormatter()) { + delete d->outputFormatter; + d->outputFormatter = outputFormatter; + } + d->macroExpander = runConfig->macroExpander(); + setTarget(runConfig->target()); +} + +void RunControl::setTarget(Target *target) +{ + QTC_ASSERT(target, return); + QTC_CHECK(!d->target); + d->target = target; + setKit(target->kit()); + d->project = target->project(); +} + +void RunControl::setKit(Kit *kit) +{ + QTC_ASSERT(kit, return); + QTC_CHECK(!d->kit); + d->kit = kit; + + if (d->runnable.device) + setDevice(d->runnable.device); + else + setDevice(DeviceKitAspect::device(kit)); +} + +void RunControl::setDevice(const IDevice::ConstPtr &device) +{ + QTC_CHECK(!d->device); + d->device = device; +#ifdef WITH_JOURNALD + if (!device.isNull() && device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) { + JournaldWatcher::instance()->subscribe(this, [this](const JournaldWatcher::LogEntry &entry) { + + if (entry.value("_MACHINE_ID") != JournaldWatcher::instance()->machineId()) + return; + + const QByteArray pid = entry.value("_PID"); + if (pid.isEmpty()) + return; + + const qint64 pidNum = static_cast<qint64>(QString::fromLatin1(pid).toInt()); + if (pidNum != d->applicationProcessHandle.pid()) + return; + + const QString message = QString::fromUtf8(entry.value("MESSAGE")) + "\n"; + appendMessage(message, Utils::OutputFormat::LogMessageFormat); + }); + } +#endif +} + +RunControl::~RunControl() +{ +#ifdef WITH_JOURNALD + JournaldWatcher::instance()->unsubscribe(this); +#endif +} + +void RunControl::initiateStart() +{ + emit aboutToStart(); + d->initiateStart(); +} + +void RunControl::initiateReStart() +{ + emit aboutToStart(); + d->initiateReStart(); +} + +void RunControl::initiateStop() +{ + d->initiateStop(); +} + +void RunControl::forceStop() +{ + d->forceStop(); +} + +void RunControl::initiateFinish() +{ + QTimer::singleShot(0, d.get(), &RunControlPrivate::initiateFinish); +} + +using WorkerCreators = QHash<Core::Id, RunControl::WorkerCreator>; + +static WorkerCreators &theWorkerCreators() +{ + static WorkerCreators creators; + return creators; +} + +void RunControl::registerWorkerCreator(Core::Id id, const WorkerCreator &workerCreator) +{ + theWorkerCreators().insert(id, workerCreator); + auto keys = theWorkerCreators().keys(); + Q_UNUSED(keys); +} + +RunWorker *RunControl::createWorker(Core::Id id) +{ + auto keys = theWorkerCreators().keys(); + Q_UNUSED(keys); + WorkerCreator creator = theWorkerCreators().value(id); + if (creator) + return creator(this); + creator = device()->workerCreator(id); + if (creator) + return creator(this); + return nullptr; +} + +bool RunControl::createMainWorker() +{ + const auto canRun = std::bind(&RunWorkerFactory::canRun, std::placeholders::_1, + d->runConfiguration, d->runMode); + const QList<RunWorkerFactory *> candidates = Utils::filtered(g_runWorkerFactories, canRun); + // There might be combinations that cannot run. But that should have been checked + // with canRun below. + QTC_ASSERT(!candidates.empty(), return false); + + // There should be at most one top-level producer feeling responsible per combination. + // Breaking a tie should be done by tightening the restrictions on one of them. + QTC_CHECK(candidates.size() == 1); + return candidates.front()->producer()(this) != nullptr; +} + +bool RunControl::canRun(RunConfiguration *runConfig, Core::Id runMode) +{ + const auto check = std::bind(&RunWorkerFactory::canRun, std::placeholders::_1, runConfig, runMode); + return Utils::contains(g_runWorkerFactories, check); +} + +void RunControlPrivate::initiateStart() +{ + checkState(RunControlState::Initialized); + setState(RunControlState::Starting); + debugMessage("Queue: Starting"); + + continueStart(); +} + +void RunControlPrivate::initiateReStart() +{ + checkState(RunControlState::Stopped); + + // Re-set worked on re-runs. + for (RunWorker *worker : m_workers) { + if (worker->d->state == RunWorkerState::Done) + worker->d->state = RunWorkerState::Initialized; + } + + setState(RunControlState::Starting); + debugMessage("Queue: ReStarting"); + + continueStart(); +} + +void RunControlPrivate::continueStart() +{ + checkState(RunControlState::Starting); + bool allDone = true; + debugMessage("Looking for next worker"); + for (RunWorker *worker : m_workers) { + if (worker) { + const QString &workerId = worker->d->id; + debugMessage(" Examining worker " + workerId); + switch (worker->d->state) { + case RunWorkerState::Initialized: + debugMessage(" " + workerId + " is not done yet."); + if (worker->d->canStart()) { + debugMessage("Starting " + workerId); + worker->d->state = RunWorkerState::Starting; + QTimer::singleShot(0, worker, &RunWorker::initiateStart); + return; + } + allDone = false; + debugMessage(" " + workerId + " cannot start."); + break; + case RunWorkerState::Starting: + debugMessage(" " + workerId + " currently starting"); + allDone = false; + break; + case RunWorkerState::Running: + debugMessage(" " + workerId + " currently running"); + break; + case RunWorkerState::Stopping: + debugMessage(" " + workerId + " currently stopping"); + continue; + case RunWorkerState::Done: + debugMessage(" " + workerId + " was done before"); + break; + } + } else { + debugMessage("Found unknown deleted worker while starting"); + } + } + if (allDone) + setState(RunControlState::Running); +} + +void RunControlPrivate::initiateStop() +{ + if (state != RunControlState::Starting && state != RunControlState::Running) + qDebug() << "Unexpected initiateStop() in state" << stateName(state); + + setState(RunControlState::Stopping); + debugMessage("Queue: Stopping for all workers"); + + continueStopOrFinish(); +} + +void RunControlPrivate::continueStopOrFinish() +{ + bool allDone = true; + + auto queueStop = [this](RunWorker *worker, const QString &message) { + if (worker->d->canStop()) { + debugMessage(message); + worker->d->state = RunWorkerState::Stopping; + QTimer::singleShot(0, worker, &RunWorker::initiateStop); + } else { + debugMessage(" " + worker->d->id + " is waiting for dependent workers to stop"); + } + }; + + for (RunWorker *worker : m_workers) { + if (worker) { + const QString &workerId = worker->d->id; + debugMessage(" Examining worker " + workerId); + switch (worker->d->state) { + case RunWorkerState::Initialized: + debugMessage(" " + workerId + " was Initialized, setting to Done"); + worker->d->state = RunWorkerState::Done; + break; + case RunWorkerState::Stopping: + debugMessage(" " + workerId + " was already Stopping. Keeping it that way"); + allDone = false; + break; + case RunWorkerState::Starting: + queueStop(worker, " " + workerId + " was Starting, queuing stop"); + allDone = false; + break; + case RunWorkerState::Running: + queueStop(worker, " " + workerId + " was Running, queuing stop"); + allDone = false; + break; + case RunWorkerState::Done: + debugMessage(" " + workerId + " was Done. Good."); + break; + } + } else { + debugMessage("Found unknown deleted worker"); + } + } + + RunControlState targetState; + if (state == RunControlState::Finishing) { + targetState = RunControlState::Finished; + } else { + checkState(RunControlState::Stopping); + targetState = RunControlState::Stopped; + } + + if (allDone) { + debugMessage("All Stopped"); + setState(targetState); + } else { + debugMessage("Not all workers Stopped. Waiting..."); + } +} + +void RunControlPrivate::forceStop() +{ + if (state == RunControlState::Finished) { + debugMessage("Was finished, too late to force Stop"); + return; + } + for (RunWorker *worker : m_workers) { + if (worker) { + const QString &workerId = worker->d->id; + debugMessage(" Examining worker " + workerId); + switch (worker->d->state) { + case RunWorkerState::Initialized: + debugMessage(" " + workerId + " was Initialized, setting to Done"); + break; + case RunWorkerState::Stopping: + debugMessage(" " + workerId + " was already Stopping. Set it forcefully to Done."); + break; + case RunWorkerState::Starting: + debugMessage(" " + workerId + " was Starting. Set it forcefully to Done."); + break; + case RunWorkerState::Running: + debugMessage(" " + workerId + " was Running. Set it forcefully to Done."); + break; + case RunWorkerState::Done: + debugMessage(" " + workerId + " was Done. Good."); + break; + } + worker->d->state = RunWorkerState::Done; + } else { + debugMessage("Found unknown deleted worker"); + } + } + + setState(RunControlState::Stopped); + debugMessage("All Stopped"); +} + +void RunControlPrivate::initiateFinish() +{ + setState(RunControlState::Finishing); + debugMessage("Ramping down"); + + continueStopOrFinish(); +} + +void RunControlPrivate::onWorkerStarted(RunWorker *worker) +{ + worker->d->state = RunWorkerState::Running; + + if (state == RunControlState::Starting) { + debugMessage(worker->d->id + " start succeeded"); + continueStart(); + return; + } + showError(RunControl::tr("Unexpected run control state %1 when worker %2 started.") + .arg(stateName(state)) + .arg(worker->d->id)); +} + +void RunControlPrivate::onWorkerFailed(RunWorker *worker, const QString &msg) +{ + worker->d->state = RunWorkerState::Done; + + showError(msg); + switch (state) { + case RunControlState::Initialized: + // FIXME 1: We don't have an output pane yet, so use some other mechanism for now. + // FIXME 2: Translation... + QMessageBox::critical(Core::ICore::dialogParent(), + QCoreApplication::translate("TaskHub", "Error"), + QString("Failure during startup. Aborting.") + "<p>" + msg); + continueStopOrFinish(); + break; + case RunControlState::Starting: + case RunControlState::Running: + initiateStop(); + break; + case RunControlState::Stopping: + case RunControlState::Finishing: + continueStopOrFinish(); + break; + case RunControlState::Stopped: + case RunControlState::Finished: + QTC_CHECK(false); // Should not happen. + continueStopOrFinish(); + break; + } +} + +void RunControlPrivate::onWorkerStopped(RunWorker *worker) +{ + const QString &workerId = worker->d->id; + switch (worker->d->state) { + case RunWorkerState::Running: + // That was a spontaneous stop. + worker->d->state = RunWorkerState::Done; + debugMessage(workerId + " stopped spontaneously."); + break; + case RunWorkerState::Stopping: + worker->d->state = RunWorkerState::Done; + debugMessage(workerId + " stopped expectedly."); + break; + case RunWorkerState::Done: + worker->d->state = RunWorkerState::Done; + debugMessage(workerId + " stopped twice. Huh? But harmless."); + return; // Sic! + default: + debugMessage(workerId + " stopped unexpectedly in state" + + stateName(worker->d->state)); + worker->d->state = RunWorkerState::Done; + break; + } + + if (state == RunControlState::Finishing || state == RunControlState::Stopping) { + continueStopOrFinish(); + return; + } else if (worker->isEssential()) { + debugMessage(workerId + " is essential. Stopping all others."); + initiateStop(); + return; + } + + for (RunWorker *dependent : worker->d->stopDependencies) { + switch (dependent->d->state) { + case RunWorkerState::Done: + break; + case RunWorkerState::Initialized: + dependent->d->state = RunWorkerState::Done; + break; + default: + debugMessage("Killing " + dependent->d->id + " as it depends on stopped " + workerId); + dependent->d->state = RunWorkerState::Stopping; + QTimer::singleShot(0, dependent, &RunWorker::initiateStop); + break; + } + } + + debugMessage("Checking whether all stopped"); + bool allDone = true; + for (RunWorker *worker : m_workers) { + if (worker) { + const QString &workerId = worker->d->id; + debugMessage(" Examining worker " + workerId); + switch (worker->d->state) { + case RunWorkerState::Initialized: + debugMessage(" " + workerId + " was Initialized."); + break; + case RunWorkerState::Starting: + debugMessage(" " + workerId + " was Starting, waiting for its response"); + allDone = false; + break; + case RunWorkerState::Running: + debugMessage(" " + workerId + " was Running, waiting for its response"); + allDone = false; + break; + case RunWorkerState::Stopping: + debugMessage(" " + workerId + " was already Stopping. Keeping it that way"); + allDone = false; + break; + case RunWorkerState::Done: + debugMessage(" " + workerId + " was Done. Good."); + break; + } + } else { + debugMessage("Found unknown deleted worker"); + } + } + + if (allDone) { + if (state == RunControlState::Stopped) { + debugMessage("All workers stopped, but runControl was already stopped."); + } else { + debugMessage("All workers stopped. Set runControl to Stopped"); + setState(RunControlState::Stopped); + } + } else { + debugMessage("Not all workers stopped. Waiting..."); + } +} + +void RunControlPrivate::showError(const QString &msg) +{ + if (!msg.isEmpty()) + q->appendMessage(msg + '\n', ErrorMessageFormat); +} + +Utils::OutputFormatter *RunControl::outputFormatter() const +{ + return d->outputFormatter; +} + +Core::Id RunControl::runMode() const +{ + return d->runMode; +} + +const Runnable &RunControl::runnable() const +{ + return d->runnable; +} + +void RunControl::setRunnable(const Runnable &runnable) +{ + d->runnable = runnable; +} + +QString RunControl::displayName() const +{ + return d->displayName; +} + +void RunControl::setDisplayName(const QString &displayName) +{ + d->displayName = displayName; +} + +void RunControl::setIcon(const Utils::Icon &icon) +{ + d->icon = icon; +} + +Utils::Icon RunControl::icon() const +{ + return d->icon; +} + +IDevice::ConstPtr RunControl::device() const +{ + return d->device; +} + +RunConfiguration *RunControl::runConfiguration() const +{ + return d->runConfiguration.data(); +} + +Target *RunControl::target() const +{ + return d->target; +} + +Project *RunControl::project() const +{ + return d->project; +} + +Kit *RunControl::kit() const +{ + return d->kit; +} + +MacroExpander *RunControl::macroExpander() const +{ + return d->macroExpander; +} + +ProjectConfigurationAspect *RunControl::aspect(Core::Id id) const +{ + return d->runConfiguration ? d->runConfiguration->aspect(id) : nullptr; +} + +ISettingsAspect *RunControl::settings(Core::Id id) const +{ + return d->runConfiguration ? d->runConfiguration->currentSettings(id) : nullptr; +} + +QString RunControl::buildKey() const +{ + return d->runConfiguration ? d->runConfiguration->buildKey() : QString(); +} + +BuildTargetInfo RunControl::buildTargetInfo() const +{ + return d->runConfiguration->buildTargetInfo(); +} + +/*! + A handle to the application process. + + This is typically a process id, but should be treated as + opaque handle to the process controled by this \c RunControl. +*/ + +ProcessHandle RunControl::applicationProcessHandle() const +{ + return d->applicationProcessHandle; +} + +void RunControl::setApplicationProcessHandle(const ProcessHandle &handle) +{ + if (d->applicationProcessHandle != handle) { + d->applicationProcessHandle = handle; + emit applicationProcessHandleChanged(QPrivateSignal()); + } +} + +/*! + Prompts to stop. If \a optionalPrompt is passed, a \gui {Do not ask again} + checkbox is displayed and the result is returned in \a *optionalPrompt. +*/ + +bool RunControl::promptToStop(bool *optionalPrompt) const +{ + QTC_ASSERT(isRunning(), return true); + if (optionalPrompt && !*optionalPrompt) + return true; + + // Overridden. + if (d->promptToStop) + return d->promptToStop(optionalPrompt); + + const QString msg = tr("<html><head/><body><center><i>%1</i> is still running.<center/>" + "<center>Force it to quit?</center></body></html>").arg(displayName()); + return showPromptToStopDialog(tr("Application Still Running"), msg, + tr("Force &Quit"), tr("&Keep Running"), + optionalPrompt); +} + +void RunControl::setPromptToStop(const std::function<bool (bool *)> &promptToStop) +{ + d->promptToStop = promptToStop; +} + +bool RunControl::supportsReRunning() const +{ + return d->supportsReRunning(); +} + +bool RunControlPrivate::supportsReRunning() const +{ + for (RunWorker *worker : m_workers) { + if (!worker->d->supportsReRunning) + return false; + if (worker->d->state != RunWorkerState::Done) + return false; + } + return true; +} + +bool RunControl::isRunning() const +{ + return d->state == RunControlState::Running; +} + +bool RunControl::isStarting() const +{ + return d->state == RunControlState::Starting; +} + +bool RunControl::isStopping() const +{ + return d->state == RunControlState::Stopping; +} + +bool RunControl::isStopped() const +{ + return d->state == RunControlState::Stopped; +} + +/*! + Prompts to terminate the application with the \gui {Do not ask again} + checkbox. +*/ + +bool RunControl::showPromptToStopDialog(const QString &title, + const QString &text, + const QString &stopButtonText, + const QString &cancelButtonText, + bool *prompt) +{ + // Show a question message box where user can uncheck this + // question for this class. + Utils::CheckableMessageBox messageBox(Core::ICore::mainWindow()); + messageBox.setWindowTitle(title); + messageBox.setText(text); + messageBox.setStandardButtons(QDialogButtonBox::Yes|QDialogButtonBox::Cancel); + if (!stopButtonText.isEmpty()) + messageBox.button(QDialogButtonBox::Yes)->setText(stopButtonText); + if (!cancelButtonText.isEmpty()) + messageBox.button(QDialogButtonBox::Cancel)->setText(cancelButtonText); + messageBox.setDefaultButton(QDialogButtonBox::Yes); + if (prompt) { + messageBox.setCheckBoxText(Utils::CheckableMessageBox::msgDoNotAskAgain()); + messageBox.setChecked(false); + } else { + messageBox.setCheckBoxVisible(false); + } + messageBox.exec(); + const bool close = messageBox.clickedStandardButton() == QDialogButtonBox::Yes; + if (close && prompt && messageBox.isChecked()) + *prompt = false; + return close; +} + +bool RunControlPrivate::isAllowedTransition(RunControlState from, RunControlState to) +{ + switch (from) { + case RunControlState::Initialized: + return to == RunControlState::Starting + || to == RunControlState::Finishing; + case RunControlState::Starting: + return to == RunControlState::Running + || to == RunControlState::Stopping + || to == RunControlState::Finishing; + case RunControlState::Running: + return to == RunControlState::Stopping + || to == RunControlState::Stopped + || to == RunControlState::Finishing; + case RunControlState::Stopping: + return to == RunControlState::Stopped + || to == RunControlState::Finishing; + case RunControlState::Stopped: + return to == RunControlState::Finishing; + case RunControlState::Finishing: + return to == RunControlState::Finished; + case RunControlState::Finished: + return false; + } + return false; +} + +void RunControlPrivate::checkState(RunControlState expectedState) +{ + if (state != expectedState) + qDebug() << "Unexpected run control state " << stateName(expectedState) + << " have: " << stateName(state); +} + +void RunControlPrivate::setState(RunControlState newState) +{ + if (!isAllowedTransition(state, newState)) + qDebug() << "Invalid run control state transition from " << stateName(state) + << " to " << stateName(newState); + + state = newState; + + debugMessage("Entering state " + stateName(newState)); + + // Extra reporting. + switch (state) { + case RunControlState::Running: + emit q->started(); + break; + case RunControlState::Stopped: + q->setApplicationProcessHandle(Utils::ProcessHandle()); + emit q->stopped(); + break; + case RunControlState::Finished: + emit q->finished(); + debugMessage("All finished. Deleting myself"); + q->deleteLater(); + break; + default: + break; + } +} + +void RunControlPrivate::debugMessage(const QString &msg) +{ + qCDebug(statesLog()) << msg; +} + +// SimpleTargetRunner + +SimpleTargetRunner::SimpleTargetRunner(RunControl *runControl) + : RunWorker(runControl) +{ + setId("SimpleTargetRunner"); + m_runnable = runControl->runnable(); // Default value. Can be overridden using setRunnable. + m_device = runControl->device(); // Default value. Can be overridden using setDevice. + if (auto terminalAspect = runControl->aspect<TerminalAspect>()) + m_useTerminal = terminalAspect->useTerminal(); +} + +void SimpleTargetRunner::start() +{ + m_stopReported = false; + m_launcher.disconnect(this); + m_launcher.setUseTerminal(m_useTerminal); + + const bool isDesktop = m_device.isNull() + || m_device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE; + const QString rawDisplayName = m_runnable.displayName(); + const QString displayName = isDesktop + ? QDir::toNativeSeparators(rawDisplayName) + : rawDisplayName; + const QString msg = RunControl::tr("Starting %1 %2...") + .arg(displayName).arg(m_runnable.commandLineArguments); + appendMessage(msg, Utils::NormalMessageFormat); + + if (isDesktop) { + + connect(&m_launcher, &ApplicationLauncher::appendMessage, + this, &SimpleTargetRunner::appendMessage); + connect(&m_launcher, &ApplicationLauncher::processStarted, + this, &SimpleTargetRunner::onProcessStarted); + connect(&m_launcher, &ApplicationLauncher::processExited, + this, &SimpleTargetRunner::onProcessFinished); + connect(&m_launcher, &ApplicationLauncher::error, + this, &SimpleTargetRunner::onProcessError); + + const QString executable = m_runnable.executable; + if (executable.isEmpty()) { + reportFailure(RunControl::tr("No executable specified.")); + } else { + m_launcher.start(m_runnable); + } + + } else { + + connect(&m_launcher, &ApplicationLauncher::reportError, + this, [this](const QString &msg) { + reportFailure(msg); + }); + + connect(&m_launcher, &ApplicationLauncher::remoteStderr, + this, [this](const QString &output) { + appendMessage(output, Utils::StdErrFormatSameLine, false); + }); + + connect(&m_launcher, &ApplicationLauncher::remoteStdout, + this, [this](const QString &output) { + appendMessage(output, Utils::StdOutFormatSameLine, false); + }); + + connect(&m_launcher, &ApplicationLauncher::finished, + this, [this] { + m_launcher.disconnect(this); + reportStopped(); + }); + + connect(&m_launcher, &ApplicationLauncher::processStarted, + this, [this] { + appendMessage("Application launcher started", Utils::NormalMessageFormat); +// reportStarted(); + }); + + connect(&m_launcher, &ApplicationLauncher::processExited, + this, [this] { + m_launcher.disconnect(this); + reportStopped(); + }); + + connect(&m_launcher, &ApplicationLauncher::remoteProcessStarted, + this, [this] { + reportStarted(); + }); + + connect(&m_launcher, &ApplicationLauncher::reportProgress, + this, [this](const QString &progressString) { + appendMessage(progressString, Utils::NormalMessageFormat); + }); + + m_launcher.start(m_runnable, device()); + } +} + +void SimpleTargetRunner::stop() +{ + m_launcher.stop(); +} + +void SimpleTargetRunner::onProcessStarted() +{ + // Console processes only know their pid after being started + ProcessHandle pid = m_launcher.applicationPID(); + runControl()->setApplicationProcessHandle(pid); + pid.activate(); + reportStarted(); +} + +void SimpleTargetRunner::onProcessFinished(int exitCode, QProcess::ExitStatus status) +{ + QString msg; + if (status == QProcess::CrashExit) + msg = tr("%1 crashed."); + else + msg = tr("%2 exited with code %1").arg(exitCode); + appendMessage(msg.arg(m_runnable.displayName()), Utils::NormalMessageFormat); + if (!m_stopReported) { + m_stopReported = true; + reportStopped(); + } +} + +void SimpleTargetRunner::onProcessError(QProcess::ProcessError error) +{ + if (error == QProcess::Timedout) + return; // No actual change on the process side. + QString msg = userMessageForProcessError(error, m_runnable.displayName()); + appendMessage(msg, Utils::NormalMessageFormat); + if (!m_stopReported) { + m_stopReported = true; + reportStopped(); + } +} + +IDevice::ConstPtr SimpleTargetRunner::device() const +{ + return m_device; +} + +void SimpleTargetRunner::setRunnable(const Runnable &runnable) +{ + m_runnable = runnable; +} + +void SimpleTargetRunner::setDevice(const IDevice::ConstPtr &device) +{ + m_device = device; +} + +// RunWorkerPrivate + +RunWorkerPrivate::RunWorkerPrivate(RunWorker *runWorker, RunControl *runControl) + : q(runWorker), runControl(runControl) +{ + runControl->d->m_workers.append(runWorker); +} + +bool RunWorkerPrivate::canStart() const +{ + if (state != RunWorkerState::Initialized) + return false; + for (RunWorker *worker : startDependencies) { + QTC_ASSERT(worker, continue); + if (worker->d->state != RunWorkerState::Done + && worker->d->state != RunWorkerState::Running) + return false; + } + return true; +} + +bool RunWorkerPrivate::canStop() const +{ + if (state != RunWorkerState::Starting && state != RunWorkerState::Running) + return false; + for (RunWorker *worker : stopDependencies) { + QTC_ASSERT(worker, continue); + if (worker->d->state != RunWorkerState::Done) + return false; + } + return true; +} + +void RunWorkerPrivate::timerEvent(QTimerEvent *ev) +{ + if (ev->timerId() == startWatchdogTimerId) { + if (startWatchdogCallback) { + killStartWatchdog(); + startWatchdogCallback(); + } else { + q->reportFailure(RunWorker::tr("Worker start timed out.")); + } + return; + } + if (ev->timerId() == stopWatchdogTimerId) { + if (stopWatchdogCallback) { + killStopWatchdog(); + stopWatchdogCallback(); + } else { + q->reportFailure(RunWorker::tr("Worker stop timed out.")); + } + return; + } +} + +/*! + \class ProjectExplorer::RunWorker + + \brief The RunWorker class encapsulates a task that forms part, or + the whole of the operation of a tool for a certain \c RunConfiguration + according to some \c RunMode. + + A typical example for a \c RunWorker is a process, either the + application process itself, or a helper process, such as a watchdog + or a log parser. + + A \c RunWorker has a simple state model covering the \c Initialized, + \c Starting, \c Running, \c Stopping, and \c Done states. + + In the course of the operation of tools several \c RunWorkers + may co-operate and form a combined state that is presented + to the user as \c RunControl, with direct interaction made + possible through the buttons in the \uicontrol{Application Output} + pane. + + RunWorkers are typically created together with their RunControl. + The startup order of RunWorkers under a RunControl can be + specified by making a RunWorker dependent on others. + + When a RunControl starts, it calls \c initiateStart() on RunWorkers + with fulfilled dependencies until all workers are \c Running, or in case + of short-lived helper tasks, \c Done. + + A RunWorker can stop spontaneously, for example when the main application + process ends. In this case, it typically calls \c initiateStop() + on its RunControl, which in turn passes this to all sibling + RunWorkers. + + Pressing the stop button in the \uicontrol{Application Output} pane + also calls \c initiateStop on the RunControl. +*/ + +RunWorker::RunWorker(RunControl *runControl) + : d(std::make_unique<RunWorkerPrivate>(this, runControl)) +{ } + +RunWorker::~RunWorker() = default; + +/*! + * This function is called by the RunControl once all dependencies + * are fulfilled. + */ +void RunWorker::initiateStart() +{ + d->startStartWatchdog(); + d->runControl->d->debugMessage("Initiate start for " + d->id); + start(); +} + +/*! + * This function has to be called by a RunWorker implementation + * to notify its RunControl about the successful start of this RunWorker. + * + * The RunControl may start other RunWorkers in response. + */ +void RunWorker::reportStarted() +{ + d->killStartWatchdog(); + d->runControl->d->onWorkerStarted(this); + emit started(); +} + +/*! + * This function is called by the RunControl in its own \c initiateStop + * implementation, which is triggered in response to pressing the + * stop button in the \uicontrol{Application Output} pane or on direct + * request of one of the sibling RunWorkers. + */ +void RunWorker::initiateStop() +{ + d->startStopWatchdog(); + d->runControl->d->debugMessage("Initiate stop for " + d->id); + stop(); +} + +/*! + * This function has to be called by a RunWorker implementation + * to notify its RunControl about this RunWorker having stopped. + * + * The stop can be spontaneous, or in response to an initiateStop() + * or an initiateFinish() call. + * + * The RunControl will adjust its global state in response. + */ +void RunWorker::reportStopped() +{ + d->killStopWatchdog(); + d->runControl->d->onWorkerStopped(this); + emit stopped(); +} + +/*! + * This function can be called by a RunWorker implementation for short-lived + * tasks to notify its RunControl about this task being successful finished. + * Dependent startup tasks can proceed, in cases of spontaneous or scheduled + * stops, the effect is the same as \c reportStopped(). + * + */ +void RunWorker::reportDone() +{ + d->killStartWatchdog(); + d->killStopWatchdog(); + switch (d->state) { + case RunWorkerState::Initialized: + QTC_CHECK(false); + d->state = RunWorkerState::Done; + break; + case RunWorkerState::Starting: + reportStarted(); + reportStopped(); + break; + case RunWorkerState::Running: + case RunWorkerState::Stopping: + reportStopped(); + break; + case RunWorkerState::Done: + break; + } +} + +/*! + * This function can be called by a RunWorker implementation to + * signal a problem in the operation in this worker. The + * RunControl will start to ramp down through initiateStop(). + */ +void RunWorker::reportFailure(const QString &msg) +{ + d->killStartWatchdog(); + d->killStopWatchdog(); + d->runControl->d->onWorkerFailed(this, msg); +} + +/*! + * Appends a message in the specified \a format to + * the owning RunControl's \uicontrol{Application Output} pane. + */ +void RunWorker::appendMessage(const QString &msg, OutputFormat format, bool appendNewLine) +{ + if (!appendNewLine || msg.endsWith('\n')) + d->runControl->appendMessage(msg, format); + else + d->runControl->appendMessage(msg + '\n', format); +} + +IDevice::ConstPtr RunWorker::device() const +{ + return d->runControl->device(); +} + +const Runnable &RunWorker::runnable() const +{ + return d->runControl->runnable(); +} + +void RunWorker::addStartDependency(RunWorker *dependency) +{ + d->startDependencies.append(dependency); +} + +void RunWorker::addStopDependency(RunWorker *dependency) +{ + d->stopDependencies.append(dependency); +} + +RunControl *RunWorker::runControl() const +{ + return d->runControl; +} + +void RunWorker::setId(const QString &id) +{ + d->id = id; +} + +void RunWorker::setStartTimeout(int ms, const std::function<void()> &callback) +{ + d->startWatchdogInterval = ms; + d->startWatchdogCallback = callback; +} + +void RunWorker::setStopTimeout(int ms, const std::function<void()> &callback) +{ + d->stopWatchdogInterval = ms; + d->stopWatchdogCallback = callback; +} + +void RunWorker::recordData(const QString &channel, const QVariant &data) +{ + d->data[channel] = data; +} + +QVariant RunWorker::recordedData(const QString &channel) const +{ + return d->data[channel]; +} + +void RunWorker::setSupportsReRunning(bool reRunningSupported) +{ + d->supportsReRunning = reRunningSupported; +} + +bool RunWorker::supportsReRunning() const +{ + return d->supportsReRunning; +} + +QString RunWorker::userMessageForProcessError(QProcess::ProcessError error, const QString &program) +{ + QString failedToStart = tr("The process failed to start."); + QString msg = tr("An unknown error in the process occurred."); + switch (error) { + case QProcess::FailedToStart: + msg = failedToStart + ' ' + tr("Either the " + "invoked program \"%1\" is missing, or you may have insufficient " + "permissions to invoke the program.").arg(program); + break; + case QProcess::Crashed: + msg = tr("The process was ended forcefully."); + break; + case QProcess::Timedout: + // "The last waitFor...() function timed out. " + // "The state of QProcess is unchanged, and you can try calling " + // "waitFor...() again." + return QString(); // sic! + case QProcess::WriteError: + msg = tr("An error occurred when attempting to write " + "to the process. For example, the process may not be running, " + "or it may have closed its input channel."); + break; + case QProcess::ReadError: + msg = tr("An error occurred when attempting to read from " + "the process. For example, the process may not be running."); + break; + case QProcess::UnknownError: + break; + } + return msg; +} + +bool RunWorker::isEssential() const +{ + return d->essential; +} + +void RunWorker::setEssential(bool essential) +{ + d->essential = essential; +} + +void RunWorker::start() +{ + reportStarted(); +} + +void RunWorker::stop() +{ + reportStopped(); +} + +CommandLine Runnable::commandLine() const +{ + return CommandLine(FilePath::fromString(executable), commandLineArguments); +} + +void Runnable::setCommandLine(const CommandLine &cmdLine) +{ + executable = cmdLine.executable().toString(); + commandLineArguments = cmdLine.arguments(); +} + +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h new file mode 100644 index 0000000000..fbb1c151ff --- /dev/null +++ b/src/plugins/projectexplorer/runcontrol.h @@ -0,0 +1,346 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "applicationlauncher.h" +#include "buildtargetinfo.h" +#include "devicesupport/idevice.h" +#include "projectexplorerconstants.h" +#include "runconfiguration.h" + +#include <utils/environment.h> +#include <utils/port.h> +#include <utils/processhandle.h> +#include <utils/qtcassert.h> +#include <utils/qtcprocess.h> +#include <utils/icon.h> + +#include <QHash> +#include <QPointer> +#include <QVariant> +#include <QWidget> + +#include <functional> +#include <memory> + +namespace Utils { +class MacroExpander; +class OutputFormatter; +} // Utils + +namespace ProjectExplorer { +class BuildConfiguration; +class GlobalOrProjectAspect; +class Node; +class RunConfigurationFactory; +class RunConfiguration; +class RunConfigurationCreationInfo; +class RunControl; +class RunWorkerFactory; +class Target; + +namespace Internal { +class RunControlPrivate; +class RunWorkerPrivate; +} // Internal + + +class PROJECTEXPLORER_EXPORT Runnable +{ +public: + Runnable() = default; + + Utils::CommandLine commandLine() const; + void setCommandLine(const Utils::CommandLine &cmdLine); + + QString executable; + QString commandLineArguments; + QString workingDirectory; + Utils::Environment environment; + IDevice::ConstPtr device; // Override the kit's device. Keep unset by default. + QHash<Core::Id, QVariant> extraData; + + // FIXME: Not necessarily a display name + QString displayName() const { return executable; } +}; + +class PROJECTEXPLORER_EXPORT RunWorker : public QObject +{ + Q_OBJECT + +public: + explicit RunWorker(RunControl *runControl); + ~RunWorker() override; + + RunControl *runControl() const; + + void addStartDependency(RunWorker *dependency); + void addStopDependency(RunWorker *dependency); + + void setId(const QString &id); + + void setStartTimeout(int ms, const std::function<void()> &callback = {}); + void setStopTimeout(int ms, const std::function<void()> &callback = {}); + + void recordData(const QString &channel, const QVariant &data); + QVariant recordedData(const QString &channel) const; + + // Part of read-only interface of RunControl for convenience. + void appendMessage(const QString &msg, Utils::OutputFormat format, bool appendNewLine = true); + IDevice::ConstPtr device() const; + const Runnable &runnable() const; + + // States + void initiateStart(); + void reportStarted(); + + void initiateStop(); + void reportStopped(); + + void reportDone(); + + void reportFailure(const QString &msg = QString()); + void setSupportsReRunning(bool reRunningSupported); + bool supportsReRunning() const; + + static QString userMessageForProcessError(QProcess::ProcessError, const QString &programName); + + bool isEssential() const; + void setEssential(bool essential); + +signals: + void started(); + void stopped(); + +protected: + void virtual start(); + void virtual stop(); + void virtual onFinished() {} + +private: + friend class Internal::RunControlPrivate; + friend class Internal::RunWorkerPrivate; + const std::unique_ptr<Internal::RunWorkerPrivate> d; +}; + +class PROJECTEXPLORER_EXPORT RunWorkerFactory +{ +public: + using WorkerCreator = std::function<RunWorker *(RunControl *)>; + using Constraint = std::function<bool(RunConfiguration *)>; + + RunWorkerFactory(); + virtual ~RunWorkerFactory(); + + bool canRun(RunConfiguration *runConfiguration, Core::Id runMode) const; + + void setProducer(const WorkerCreator &producer); + void addConstraint(const Constraint &constraint); + void addSupportedRunMode(Core::Id runMode); + + void setSupportedRunConfigurations(const QList<Core::Id> &ids); + void addSupportedRunConfiguration(Core::Id id); + + WorkerCreator producer() const { return m_producer; } + +private: + // FIXME: That's temporary until ownership has been transferred to + // the individual plugins. + friend class ProjectExplorerPlugin; + static void destroyRemainingRunWorkerFactories(); + + QList<Core::Id> m_supportedRunModes; + QList<Core::Id> m_supportedRunConfigurations; + QList<Constraint> m_constraints; + WorkerCreator m_producer; +}; + +/** + * A RunControl controls the running of an application or tool + * on a target device. It controls start and stop, and handles + * application output. + * + * RunControls are created by RunControlFactories. + */ + +class PROJECTEXPLORER_EXPORT RunControl : public QObject +{ + Q_OBJECT + +public: + explicit RunControl(Core::Id mode); + ~RunControl() override; + + void setRunConfiguration(RunConfiguration *runConfig); + void setTarget(Target *target); + void setKit(Kit *kit); + + void initiateStart(); + void initiateReStart(); + void initiateStop(); + void forceStop(); + void initiateFinish(); + + bool promptToStop(bool *optionalPrompt = nullptr) const; + void setPromptToStop(const std::function<bool(bool *)> &promptToStop); + + bool supportsReRunning() const; + + virtual QString displayName() const; + void setDisplayName(const QString &displayName); + + bool isRunning() const; + bool isStarting() const; + bool isStopping() const; + bool isStopped() const; + + void setIcon(const Utils::Icon &icon); + Utils::Icon icon() const; + + Utils::ProcessHandle applicationProcessHandle() const; + void setApplicationProcessHandle(const Utils::ProcessHandle &handle); + IDevice::ConstPtr device() const; + + RunConfiguration *runConfiguration() const; // FIXME: Remove. + // FIXME: Try to cut down to amount of functions. + Target *target() const; + Project *project() const; + Kit *kit() const; + Utils::MacroExpander *macroExpander() const; + ProjectConfigurationAspect *aspect(Core::Id id) const; + template <typename T> T *aspect() const { + return runConfiguration() ? runConfiguration()->aspect<T>() : nullptr; + } + + template <typename T> + auto aspectData() -> decltype(T::runData(nullptr, this)) { + if (T *asp = aspect<T>()) + return T::runData(asp, this); + return {}; + } + + ISettingsAspect *settings(Core::Id id) const; + QString buildKey() const; + BuildTargetInfo buildTargetInfo() const; + + Utils::OutputFormatter *outputFormatter() const; + Core::Id runMode() const; + + const Runnable &runnable() const; + void setRunnable(const Runnable &runnable); + + static bool showPromptToStopDialog(const QString &title, const QString &text, + const QString &stopButtonText = QString(), + const QString &cancelButtonText = QString(), + bool *prompt = nullptr); + + RunWorker *createWorker(Core::Id id); + + using WorkerCreator = RunWorkerFactory::WorkerCreator; + using Constraint = RunWorkerFactory::Constraint; + + static void registerWorkerCreator(Core::Id id, const WorkerCreator &workerCreator); + + template <class Worker> + static void registerWorker(Core::Id runMode, const Constraint &constraint) + { + auto factory = new RunWorkerFactory; + factory->setProducer([](RunControl *rc) { return new Worker(rc); }); + factory->addSupportedRunMode(runMode); + factory->addConstraint(constraint); + } + + bool createMainWorker(); + static bool canRun(RunConfiguration *runConfig, Core::Id runMode); + +signals: + void appendMessage(const QString &msg, Utils::OutputFormat format); + void aboutToStart(); + void started(); + void stopped(); + void finished(); + void applicationProcessHandleChanged(QPrivateSignal); // Use setApplicationProcessHandle + +private: + void setDevice(const IDevice::ConstPtr &device); + + friend class RunWorker; + friend class Internal::RunWorkerPrivate; + + const std::unique_ptr<Internal::RunControlPrivate> d; +}; + + +/** + * A simple TargetRunner for cases where a plain ApplicationLauncher is + * sufficient for running purposes. + */ + +class PROJECTEXPLORER_EXPORT SimpleTargetRunner : public RunWorker +{ + Q_OBJECT + +public: + explicit SimpleTargetRunner(RunControl *runControl); + + void setRunnable(const Runnable &runnable); + + void setDevice(const IDevice::ConstPtr &device); + IDevice::ConstPtr device() const; + +protected: + void start() override; + void stop() override; + +private: + void onProcessStarted(); + void onProcessFinished(int exitCode, QProcess::ExitStatus status); + void onProcessError(QProcess::ProcessError error); + + ApplicationLauncher m_launcher; + Runnable m_runnable; + IDevice::ConstPtr m_device; + bool m_stopReported = false; + bool m_useTerminal = false; +}; + +template <class RunWorker, class RunConfig> +class SimpleRunWorkerFactory : public RunWorkerFactory +{ +public: + SimpleRunWorkerFactory(Core::Id runMode = ProjectExplorer::Constants::NORMAL_RUN_MODE) + { + addSupportedRunMode(runMode); + addConstraint([](RunConfiguration *runConfig) { + return qobject_cast<RunConfig *>(runConfig) != nullptr; + }); + setProducer([](RunControl *runControl) { + return new RunWorker(runControl); + }); + } +}; + +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/runsettingspropertiespage.cpp b/src/plugins/projectexplorer/runsettingspropertiespage.cpp index 3b30bf63ad..39b2f01af6 100644 --- a/src/plugins/projectexplorer/runsettingspropertiespage.cpp +++ b/src/plugins/projectexplorer/runsettingspropertiespage.cpp @@ -25,6 +25,7 @@ #include "runsettingspropertiespage.h" +#include "addrunconfigdialog.h" #include "buildstepspage.h" #include "deployconfiguration.h" #include "runconfiguration.h" @@ -92,7 +93,7 @@ RunSettingsWidget::RunSettingsWidget(Target *target) : m_runConfigurationCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); m_runConfigurationCombo->setMinimumContentsLength(15); - m_addRunToolButton = new QPushButton(tr("Add"), this); + m_addRunToolButton = new QPushButton(tr("Add..."), this); m_removeRunToolButton = new QPushButton(tr("Remove"), this); m_renameRunButton = new QPushButton(tr("Rename..."), this); m_cloneRunButton = new QPushButton(tr("Clone..."), this); @@ -161,7 +162,7 @@ RunSettingsWidget::RunSettingsWidget(Target *target) : connect(m_addDeployMenu, &QMenu::aboutToShow, this, &RunSettingsWidget::aboutToShowDeployMenu); - connect(m_deployConfigurationCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(m_deployConfigurationCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RunSettingsWidget::currentDeployConfigurationChanged); connect(m_removeDeployToolButton, &QAbstractButton::clicked, this, &RunSettingsWidget::removeDeployConfiguration); @@ -187,8 +188,6 @@ RunSettingsWidget::RunSettingsWidget(Target *target) : m_runLayout->addLayout(disabledHBox); - m_addRunMenu = new QMenu(m_addRunToolButton); - m_addRunToolButton->setMenu(m_addRunMenu); RunConfiguration *rc = m_target->activeRunConfiguration(); m_runConfigurationCombo->setModel(m_runConfigurationsModel); m_runConfigurationCombo->setCurrentIndex( @@ -200,9 +199,9 @@ RunSettingsWidget::RunSettingsWidget(Target *target) : setConfigurationWidget(rc); - connect(m_addRunMenu, &QMenu::aboutToShow, - this, &RunSettingsWidget::aboutToShowAddMenu); - connect(m_runConfigurationCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(m_addRunToolButton, &QAbstractButton::clicked, + this, &RunSettingsWidget::showAddRunConfigDialog); + connect(m_runConfigurationCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RunSettingsWidget::currentRunConfigurationChanged); connect(m_removeRunToolButton, &QAbstractButton::clicked, this, &RunSettingsWidget::removeRunConfiguration); @@ -225,28 +224,20 @@ RunSettingsWidget::RunSettingsWidget(Target *target) : this, &RunSettingsWidget::activeRunConfigurationChanged); } -void RunSettingsWidget::aboutToShowAddMenu() +void RunSettingsWidget::showAddRunConfigDialog() { - m_addRunMenu->clear(); - QList<QAction *> menuActions; - for (const RunConfigurationCreationInfo &item : - RunConfigurationFactory::creatorsForTarget(m_target)) { - auto action = new QAction(item.displayName, m_addRunMenu); - connect(action, &QAction::triggered, [item, this] { - RunConfiguration *newRC = item.create(m_target); - if (!newRC) - return; - QTC_CHECK(newRC->id() == item.id); - m_target->addRunConfiguration(newRC); - m_target->setActiveRunConfiguration(newRC); - m_removeRunToolButton->setEnabled(m_target->runConfigurations().size() > 1); - }); - menuActions.append(action); - } - - Utils::sort(menuActions, &QAction::text); - foreach (QAction *action, menuActions) - m_addRunMenu->addAction(action); + AddRunConfigDialog dlg(m_target, this); + if (dlg.exec() != QDialog::Accepted) + return; + RunConfigurationCreationInfo rci = dlg.creationInfo(); + QTC_ASSERT(rci.id.isValid(), return); + RunConfiguration *newRC = rci.create(m_target); + if (!newRC) + return; + QTC_CHECK(newRC->id() == rci.id); + m_target->addRunConfiguration(newRC); + m_target->setActiveRunConfiguration(newRC); + m_removeRunToolButton->setEnabled(m_target->runConfigurations().size() > 1); } void RunSettingsWidget::cloneRunConfiguration() diff --git a/src/plugins/projectexplorer/runsettingspropertiespage.h b/src/plugins/projectexplorer/runsettingspropertiespage.h index 1857fea945..bf86fe56ba 100644 --- a/src/plugins/projectexplorer/runsettingspropertiespage.h +++ b/src/plugins/projectexplorer/runsettingspropertiespage.h @@ -58,7 +58,7 @@ public: private: void currentRunConfigurationChanged(int index); - void aboutToShowAddMenu(); + void showAddRunConfigDialog(); void cloneRunConfiguration(); void removeRunConfiguration(); void activeRunConfigurationChanged(); @@ -91,7 +91,6 @@ private: NamedWidget *m_deployConfigurationWidget = nullptr; QVBoxLayout *m_deployLayout = nullptr; BuildStepListWidget *m_deploySteps = nullptr; - QMenu *m_addRunMenu; QMenu *m_addDeployMenu; bool m_ignoreChange = false; using RunConfigItem = QPair<QWidget *, QLabel *>; diff --git a/src/plugins/projectexplorer/selectablefilesmodel.cpp b/src/plugins/projectexplorer/selectablefilesmodel.cpp index b8925e4f57..28d6f4ef23 100644 --- a/src/plugins/projectexplorer/selectablefilesmodel.cpp +++ b/src/plugins/projectexplorer/selectablefilesmodel.cpp @@ -53,13 +53,13 @@ SelectableFilesModel::SelectableFilesModel(QObject *parent) : QAbstractItemModel m_root = new Tree; } -void SelectableFilesModel::setInitialMarkedFiles(const Utils::FileNameList &files) +void SelectableFilesModel::setInitialMarkedFiles(const Utils::FilePathList &files) { m_files = files.toSet(); m_allFiles = files.isEmpty(); } -void SelectableFilesFromDirModel::startParsing(const Utils::FileName &baseDir) +void SelectableFilesFromDirModel::startParsing(const Utils::FilePath &baseDir) { m_watcher.cancel(); m_watcher.waitForFinished(); @@ -87,7 +87,7 @@ void SelectableFilesFromDirModel::buildTreeFinished() m_root = m_rootForFuture; m_rootForFuture = nullptr; m_outOfBaseDirFiles - = Utils::filtered(m_files, [this](const Utils::FileName &fn) { return !fn.isChildOf(m_baseDir); }); + = Utils::filtered(m_files, [this](const Utils::FilePath &fn) { return !fn.isChildOf(m_baseDir); }); endResetModel(); emit parsingFinished(); @@ -116,7 +116,7 @@ SelectableFilesModel::FilterState SelectableFilesModel::filter(Tree *t) return Utils::anyOf(m_hideFilesFilter, matchesTreeName) ? FilterState::HIDDEN : FilterState::SHOWN; } -void SelectableFilesFromDirModel::buildTree(const Utils::FileName &baseDir, Tree *tree, +void SelectableFilesFromDirModel::buildTree(const Utils::FilePath &baseDir, Tree *tree, QFutureInterface<void> &fi, int symlinkDepth) { if (symlinkDepth == 0) @@ -128,7 +128,7 @@ void SelectableFilesFromDirModel::buildTree(const Utils::FileName &baseDir, Tree bool allChecked = true; bool allUnchecked = true; for (const QFileInfo &fileInfo : fileInfoList) { - Utils::FileName fn = Utils::FileName(fileInfo); + Utils::FilePath fn = Utils::FilePath::fromFileInfo(fileInfo); if (m_futureCount % 100) { emit parsingProgress(fn); if (fi.isCanceled()) @@ -302,14 +302,14 @@ Qt::ItemFlags SelectableFilesModel::flags(const QModelIndex &index) const return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; } -Utils::FileNameList SelectableFilesModel::selectedPaths() const +Utils::FilePathList SelectableFilesModel::selectedPaths() const { - Utils::FileNameList result; + Utils::FilePathList result; collectPaths(m_root, &result); return result; } -void SelectableFilesModel::collectPaths(Tree *root, Utils::FileNameList *result) const +void SelectableFilesModel::collectPaths(Tree *root, Utils::FilePathList *result) const { if (root->checked == Qt::Unchecked) return; @@ -318,14 +318,14 @@ void SelectableFilesModel::collectPaths(Tree *root, Utils::FileNameList *result) collectPaths(t, result); } -Utils::FileNameList SelectableFilesModel::selectedFiles() const +Utils::FilePathList SelectableFilesModel::selectedFiles() const { - Utils::FileNameList result = m_outOfBaseDirFiles.toList(); + Utils::FilePathList result = m_outOfBaseDirFiles.toList(); collectFiles(m_root, &result); return result; } -Utils::FileNameList SelectableFilesModel::preservedFiles() const +Utils::FilePathList SelectableFilesModel::preservedFiles() const { return m_outOfBaseDirFiles.toList(); } @@ -335,7 +335,7 @@ bool SelectableFilesModel::hasCheckedFiles() const return m_root->checked != Qt::Unchecked; } -void SelectableFilesModel::collectFiles(Tree *root, Utils::FileNameList *result) const +void SelectableFilesModel::collectFiles(Tree *root, Utils::FilePathList *result) const { if (root->checked == Qt::Unchecked) return; @@ -591,8 +591,8 @@ SelectableFilesWidget::SelectableFilesWidget(QWidget *parent) : layout->addWidget(m_progressLabel, static_cast<int>(SelectableFilesWidgetRows::Progress), 0, 1, 4); } -SelectableFilesWidget::SelectableFilesWidget(const Utils::FileName &path, - const Utils::FileNameList &files, QWidget *parent) : +SelectableFilesWidget::SelectableFilesWidget(const Utils::FilePath &path, + const Utils::FilePathList &files, QWidget *parent) : SelectableFilesWidget(parent) { resetModel(path, files); @@ -601,6 +601,10 @@ SelectableFilesWidget::SelectableFilesWidget(const Utils::FileName &path, void SelectableFilesWidget::setAddFileFilter(const QString &filter) { m_selectFilesFilterEdit->setText(filter); + if (m_applyFiltersButton->isEnabled()) + applyFilter(); + else + m_filteringScheduled = true; } void SelectableFilesWidget::setBaseDirEditable(bool edit) @@ -611,14 +615,14 @@ void SelectableFilesWidget::setBaseDirEditable(bool edit) m_startParsingButton->setVisible(edit); } -Utils::FileNameList SelectableFilesWidget::selectedFiles() const +Utils::FilePathList SelectableFilesWidget::selectedFiles() const { - return m_model ? m_model->selectedFiles() : Utils::FileNameList(); + return m_model ? m_model->selectedFiles() : Utils::FilePathList(); } -Utils::FileNameList SelectableFilesWidget::selectedPaths() const +Utils::FilePathList SelectableFilesWidget::selectedPaths() const { - return m_model ? m_model->selectedPaths() : Utils::FileNameList(); + return m_model ? m_model->selectedPaths() : Utils::FilePathList(); } bool SelectableFilesWidget::hasFilesSelected() const @@ -626,7 +630,7 @@ bool SelectableFilesWidget::hasFilesSelected() const return m_model ? m_model->hasCheckedFiles() : false; } -void SelectableFilesWidget::resetModel(const Utils::FileName &path, const Utils::FileNameList &files) +void SelectableFilesWidget::resetModel(const Utils::FilePath &path, const Utils::FilePathList &files) { m_view->setModel(nullptr); @@ -675,6 +679,7 @@ void SelectableFilesWidget::enableWidgets(bool enabled) void SelectableFilesWidget::applyFilter() { + m_filteringScheduled = false; if (m_model) m_model->applyFilter(m_selectFilesFilterEdit->text(), m_hideFilesFilterEdit->text()); } @@ -684,7 +689,7 @@ void SelectableFilesWidget::baseDirectoryChanged(bool validState) m_startParsingButton->setEnabled(validState); } -void SelectableFilesWidget::startParsing(const Utils::FileName &baseDir) +void SelectableFilesWidget::startParsing(const Utils::FilePath &baseDir) { if (!m_model) return; @@ -694,7 +699,7 @@ void SelectableFilesWidget::startParsing(const Utils::FileName &baseDir) m_model->startParsing(baseDir); } -void SelectableFilesWidget::parsingProgress(const Utils::FileName &fileName) +void SelectableFilesWidget::parsingProgress(const Utils::FilePath &fileName) { m_progressLabel->setText(tr("Generating file list...\n\n%1").arg(fileName.toUserOutput())); } @@ -706,11 +711,13 @@ void SelectableFilesWidget::parsingFinished() smartExpand(m_model->index(0,0, QModelIndex())); - const Utils::FileNameList preservedFiles = m_model->preservedFiles(); + const Utils::FilePathList preservedFiles = m_model->preservedFiles(); m_preservedFilesLabel->setText(tr("Not showing %n files that are outside of the base directory.\n" "These files are preserved.", nullptr, preservedFiles.count())); enableWidgets(true); + if (m_filteringScheduled) + applyFilter(); } void SelectableFilesWidget::smartExpand(const QModelIndex &idx) @@ -728,8 +735,8 @@ void SelectableFilesWidget::smartExpand(const QModelIndex &idx) // SelectableFilesDialogs ////////// -SelectableFilesDialogEditFiles::SelectableFilesDialogEditFiles(const Utils::FileName &path, - const Utils::FileNameList &files, +SelectableFilesDialogEditFiles::SelectableFilesDialogEditFiles(const Utils::FilePath &path, + const Utils::FilePathList &files, QWidget *parent) : QDialog(parent), m_filesWidget(new SelectableFilesWidget(path, files)) @@ -752,7 +759,7 @@ SelectableFilesDialogEditFiles::SelectableFilesDialogEditFiles(const Utils::File layout->addWidget(buttonBox); } -Utils::FileNameList SelectableFilesDialogEditFiles::selectedFiles() const +Utils::FilePathList SelectableFilesDialogEditFiles::selectedFiles() const { return m_filesWidget->selectedFiles(); } @@ -763,8 +770,8 @@ Utils::FileNameList SelectableFilesDialogEditFiles::selectedFiles() const ////////// -SelectableFilesDialogAddDirectory::SelectableFilesDialogAddDirectory(const Utils::FileName &path, - const Utils::FileNameList &files, +SelectableFilesDialogAddDirectory::SelectableFilesDialogAddDirectory(const Utils::FilePath &path, + const Utils::FilePathList &files, QWidget *parent) : SelectableFilesDialogEditFiles(path, files, parent) { diff --git a/src/plugins/projectexplorer/selectablefilesmodel.h b/src/plugins/projectexplorer/selectablefilesmodel.h index 698ae03d05..a04a059ffc 100644 --- a/src/plugins/projectexplorer/selectablefilesmodel.h +++ b/src/plugins/projectexplorer/selectablefilesmodel.h @@ -64,7 +64,7 @@ public: QList<Tree *> files; QList<Tree *> visibleFiles; QIcon icon; - Utils::FileName fullPath; + Utils::FilePath fullPath; Tree *parent = nullptr; }; @@ -107,7 +107,7 @@ public: SelectableFilesModel(QObject *parent); ~SelectableFilesModel() override; - void setInitialMarkedFiles(const Utils::FileNameList &files); + void setInitialMarkedFiles(const Utils::FilePathList &files); int columnCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override; @@ -118,9 +118,9 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; - Utils::FileNameList selectedFiles() const; - Utils::FileNameList selectedPaths() const; - Utils::FileNameList preservedFiles() const; + Utils::FilePathList selectedFiles() const; + Utils::FilePathList selectedPaths() const; + Utils::FilePathList preservedFiles() const; bool hasCheckedFiles() const; @@ -141,14 +141,14 @@ protected: private: QList<Glob> parseFilter(const QString &filter); Qt::CheckState applyFilter(const QModelIndex &idx); - void collectFiles(Tree *root, Utils::FileNameList *result) const; - void collectPaths(Tree *root, Utils::FileNameList *result) const; + void collectFiles(Tree *root, Utils::FilePathList *result) const; + void collectPaths(Tree *root, Utils::FilePathList *result) const; void selectAllFiles(Tree *root); protected: bool m_allFiles = true; - QSet<Utils::FileName> m_outOfBaseDirFiles; - QSet<Utils::FileName> m_files; + QSet<Utils::FilePath> m_outOfBaseDirFiles; + QSet<Utils::FilePath> m_files; Tree *m_root = nullptr; private: @@ -164,15 +164,15 @@ public: SelectableFilesFromDirModel(QObject *parent); ~SelectableFilesFromDirModel() override; - void startParsing(const Utils::FileName &baseDir); + void startParsing(const Utils::FilePath &baseDir); void cancel(); signals: void parsingFinished(); - void parsingProgress(const Utils::FileName &fileName); + void parsingProgress(const Utils::FilePath &fileName); private: - void buildTree(const Utils::FileName &baseDir, + void buildTree(const Utils::FilePath &baseDir, Tree *tree, QFutureInterface<void> &fi, int symlinkDepth); @@ -180,7 +180,7 @@ private: void buildTreeFinished(); // Used in the future thread need to all not used after calling startParsing - Utils::FileName m_baseDir; + Utils::FilePath m_baseDir; QFutureWatcher<void> m_watcher; Tree *m_rootForFuture = nullptr; int m_futureCount = 0; @@ -192,18 +192,18 @@ class PROJECTEXPLORER_EXPORT SelectableFilesWidget : public QWidget public: explicit SelectableFilesWidget(QWidget *parent = nullptr); - SelectableFilesWidget(const Utils::FileName &path, const Utils::FileNameList &files, + SelectableFilesWidget(const Utils::FilePath &path, const Utils::FilePathList &files, QWidget *parent = nullptr); void setAddFileFilter(const QString &filter); void setBaseDirEditable(bool edit); - Utils::FileNameList selectedFiles() const; - Utils::FileNameList selectedPaths() const; + Utils::FilePathList selectedFiles() const; + Utils::FilePathList selectedPaths() const; bool hasFilesSelected() const; - void resetModel(const Utils::FileName &path, const Utils::FileNameList &files); + void resetModel(const Utils::FilePath &path, const Utils::FilePathList &files); void cancelParsing(); void enableFilterHistoryCompletion(const QString &keyPrefix); @@ -216,8 +216,8 @@ private: void applyFilter(); void baseDirectoryChanged(bool validState); - void startParsing(const Utils::FileName &baseDir); - void parsingProgress(const Utils::FileName &fileName); + void startParsing(const Utils::FilePath &baseDir); + void parsingProgress(const Utils::FilePath &fileName); void parsingFinished(); void smartExpand(const QModelIndex &idx); @@ -241,6 +241,7 @@ private: QLabel *m_preservedFilesLabel; QLabel *m_progressLabel; + bool m_filteringScheduled = false; }; class PROJECTEXPLORER_EXPORT SelectableFilesDialogEditFiles : public QDialog @@ -248,9 +249,9 @@ class PROJECTEXPLORER_EXPORT SelectableFilesDialogEditFiles : public QDialog Q_OBJECT public: - SelectableFilesDialogEditFiles(const Utils::FileName &path, const Utils::FileNameList &files, + SelectableFilesDialogEditFiles(const Utils::FilePath &path, const Utils::FilePathList &files, QWidget *parent); - Utils::FileNameList selectedFiles() const; + Utils::FilePathList selectedFiles() const; void setAddFileFilter(const QString &filter) { m_filesWidget->setAddFileFilter(filter); } @@ -263,7 +264,7 @@ class SelectableFilesDialogAddDirectory : public SelectableFilesDialogEditFiles Q_OBJECT public: - SelectableFilesDialogAddDirectory(const Utils::FileName &path, const Utils::FileNameList &files, + SelectableFilesDialogAddDirectory(const Utils::FilePath &path, const Utils::FilePathList &files, QWidget *parent); }; diff --git a/src/plugins/projectexplorer/session.cpp b/src/plugins/projectexplorer/session.cpp index 26a70669d4..95a8fbd4e6 100644 --- a/src/plugins/projectexplorer/session.cpp +++ b/src/plugins/projectexplorer/session.cpp @@ -55,6 +55,12 @@ #include <QMessageBox> #include <QPushButton> +#ifdef WITH_TESTS +#include <QTemporaryFile> +#include <QTest> +#include <vector> +#endif + using namespace Core; using namespace Utils; using namespace ProjectExplorer::Internal; @@ -204,7 +210,7 @@ QList<Project *> SessionManager::dependencies(const Project *project) QList<Project *> projects; foreach (const QString &dep, proDeps) { - const Utils::FileName fn = Utils::FileName::fromString(dep); + const Utils::FilePath fn = Utils::FilePath::fromString(dep); Project *pro = Utils::findOrDefault(d->m_projects, [&fn](Project *p) { return p->projectFilePath() == fn; }); if (pro) projects += pro; @@ -552,17 +558,17 @@ QString SessionManagerPrivate::sessionTitle(const QString &filePath) } QString SessionManagerPrivate::locationInProject(const QString &filePath) { - const Project *project = SessionManager::projectForFile(Utils::FileName::fromString(filePath)); + const Project *project = SessionManager::projectForFile(Utils::FilePath::fromString(filePath)); if (!project) return QString(); - const Utils::FileName file = Utils::FileName::fromString(filePath); - const Utils::FileName parentDir = file.parentDir(); + const Utils::FilePath file = Utils::FilePath::fromString(filePath); + const Utils::FilePath parentDir = file.parentDir(); if (parentDir == project->projectDirectory()) return "@ " + project->displayName(); if (file.isChildOf(project->projectDirectory())) { - const Utils::FileName dirInProject = parentDir.relativeChildPath(project->projectDirectory()); + const Utils::FilePath dirInProject = parentDir.relativeChildPath(project->projectDirectory()); return "(" + dirInProject.toUserOutput() + " @ " + project->displayName() + ")"; } @@ -632,7 +638,7 @@ QList<Project *> SessionManager::projectOrder(const Project *project) return result; } -Project *SessionManager::projectForFile(const Utils::FileName &fileName) +Project *SessionManager::projectForFile(const Utils::FilePath &fileName) { return Utils::findOrDefault(SessionManager::projects(), [&fileName](const Project *p) { return p->isKnownFile(fileName); }); @@ -641,7 +647,7 @@ Project *SessionManager::projectForFile(const Utils::FileName &fileName) void SessionManager::configureEditor(IEditor *editor, const QString &fileName) { if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor)) { - Project *project = projectForFile(Utils::FileName::fromString(fileName)); + Project *project = projectForFile(Utils::FilePath::fromString(fileName)); // Global settings are the default. if (project) project->editorConfiguration()->configureEditor(textEditor); @@ -738,7 +744,7 @@ QStringList SessionManager::sessions() if (d->m_sessions.isEmpty()) { // We are not initialized yet, so do that now QDir sessionDir(ICore::userResourcePath()); - QList<QFileInfo> sessionFiles = sessionDir.entryInfoList(QStringList() << QLatin1String("*.qws"), QDir::NoFilter, QDir::Time); + QFileInfoList sessionFiles = sessionDir.entryInfoList(QStringList() << QLatin1String("*.qws"), QDir::NoFilter, QDir::Time); foreach (const QFileInfo &fileInfo, sessionFiles) { const QString &name = fileInfo.completeBaseName(); d->m_sessionDateTimes.insert(name, fileInfo.lastModified()); @@ -755,9 +761,9 @@ QDateTime SessionManager::sessionDateTime(const QString &session) return d->m_sessionDateTimes.value(session); } -FileName SessionManager::sessionNameToFileName(const QString &session) +FilePath SessionManager::sessionNameToFileName(const QString &session) { - return FileName::fromString(ICore::userResourcePath() + QLatin1Char('/') + session + QLatin1String(".qws")); + return FilePath::fromString(ICore::userResourcePath() + QLatin1Char('/') + session + QLatin1String(".qws")); } /*! @@ -786,11 +792,15 @@ bool SessionManager::renameSession(const QString &original, const QString &newNa /*! \brief Shows a dialog asking the user to confirm deleting the session \p session */ -bool SessionManager::confirmSessionDelete(const QString &session) +bool SessionManager::confirmSessionDelete(const QStringList &sessions) { + const QString title = sessions.size() == 1 ? tr("Delete Session") : tr("Delete Sessions"); + const QString question = sessions.size() == 1 + ? tr("Delete session %1?").arg(sessions.first()) + : tr("Delete these sessions?\n %1").arg(sessions.join("\n ")); return QMessageBox::question(ICore::mainWindow(), - tr("Delete Session"), - tr("Delete session %1?").arg(session), + title, + question, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes; } @@ -808,6 +818,12 @@ bool SessionManager::deleteSession(const QString &session) return false; } +void SessionManager::deleteSessions(const QStringList &sessions) +{ + for (const QString &session : sessions) + deleteSession(session); +} + bool SessionManager::cloneSession(const QString &original, const QString &clone) { if (!d->m_sessions.contains(original)) @@ -931,7 +947,7 @@ bool SessionManager::loadSession(const QString &session) QStringList fileList; // Try loading the file - FileName fileName = sessionNameToFileName(session); + FilePath fileName = sessionNameToFileName(session); PersistentSettingsReader reader; if (fileName.exists()) { if (!reader.load(fileName)) { @@ -1002,11 +1018,8 @@ bool SessionManager::loadSession(const QString &session) d->m_future.setProgressRange(0, projectPathsToLoad.count() + 1/*initialization above*/ + 1/*editors*/); d->m_future.setProgressValue(1); - - // if one processEvents doesn't get the job done - // just use two! - QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + d->restoreProjects(projectPathsToLoad); d->sessionLoadingProgress(); d->restoreDependencies(reader); @@ -1065,7 +1078,7 @@ void SessionManagerPrivate::sessionLoadingProgress() QStringList SessionManager::projectsForSessionName(const QString &session) { - const FileName fileName = sessionNameToFileName(session); + const FilePath fileName = sessionNameToFileName(session); PersistentSettingsReader reader; if (fileName.exists()) { if (!reader.load(fileName)) { @@ -1076,4 +1089,58 @@ QStringList SessionManager::projectsForSessionName(const QString &session) return reader.restoreValue(QLatin1String("ProjectList")).toStringList(); } +#ifdef WITH_TESTS + +void ProjectExplorerPlugin::testSessionSwitch() +{ + QVERIFY(SessionManager::createSession("session1")); + QVERIFY(SessionManager::createSession("session2")); + QTemporaryFile cppFile("main.cpp"); + QVERIFY(cppFile.open()); + cppFile.close(); + QTemporaryFile projectFile1("XXXXXX.pro"); + QTemporaryFile projectFile2("XXXXXX.pro"); + struct SessionSpec { + SessionSpec(const QString &n, QTemporaryFile &f) : name(n), projectFile(f) {} + const QString name; + QTemporaryFile &projectFile; + }; + std::vector<SessionSpec> sessionSpecs{SessionSpec("session1", projectFile1), + SessionSpec("session2", projectFile2)}; + for (const SessionSpec &sessionSpec : sessionSpecs) { + static const QByteArray proFileContents + = "TEMPLATE = app\n" + "CONFIG -= qt\n" + "SOURCES = " + cppFile.fileName().toLocal8Bit(); + QVERIFY(sessionSpec.projectFile.open()); + sessionSpec.projectFile.write(proFileContents); + sessionSpec.projectFile.close(); + QVERIFY(SessionManager::loadSession(sessionSpec.name)); + const OpenProjectResult openResult + = ProjectExplorerPlugin::openProject(sessionSpec.projectFile.fileName()); + if (openResult.errorMessage().contains("text/plain")) + QSKIP("This test requires the presence of QmakeProjectManager to be fully functional"); + QVERIFY(openResult); + QCOMPARE(openResult.projects().count(), 1); + QVERIFY(openResult.project()); + QCOMPARE(SessionManager::projects().count(), 1); + } + for (int i = 0; i < 30; ++i) { + QVERIFY(SessionManager::loadSession("session1")); + QCOMPARE(SessionManager::activeSession(), "session1"); + QCOMPARE(SessionManager::projects().count(), 1); + QVERIFY(SessionManager::loadSession("session2")); + QCOMPARE(SessionManager::activeSession(), "session2"); + QCOMPARE(SessionManager::projects().count(), 1); + } + QVERIFY(SessionManager::loadSession("session1")); + SessionManager::closeAllProjects(); + QVERIFY(SessionManager::loadSession("session2")); + SessionManager::closeAllProjects(); + QVERIFY(SessionManager::deleteSession("session1")); + QVERIFY(SessionManager::deleteSession("session2")); +} + +#endif // WITH_TESTS + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/session.h b/src/plugins/projectexplorer/session.h index 3a49be8308..782079f9d7 100644 --- a/src/plugins/projectexplorer/session.h +++ b/src/plugins/projectexplorer/session.h @@ -63,8 +63,9 @@ public: static bool createSession(const QString &session); - static bool confirmSessionDelete(const QString &session); + static bool confirmSessionDelete(const QStringList &sessions); static bool deleteSession(const QString &session); + static void deleteSessions(const QStringList &sessions); static bool cloneSession(const QString &original, const QString &clone); static bool renameSession(const QString &original, const QString &newName); @@ -93,7 +94,7 @@ public: static void setActiveBuildConfiguration(Target *t, BuildConfiguration *bc, SetActive cascade); static void setActiveDeployConfiguration(Target *t, DeployConfiguration *dc, SetActive cascade); - static Utils::FileName sessionNameToFileName(const QString &session); + static Utils::FilePath sessionNameToFileName(const QString &session); static Project *startupProject(); static const QList<Project *> projects(); @@ -110,7 +111,7 @@ public: // NBS rewrite projectOrder (dependency management) static QList<Project *> projectOrder(const Project *project = nullptr); - static Project *projectForFile(const Utils::FileName &fileName); + static Project *projectForFile(const Utils::FilePath &fileName); static QStringList projectsForSessionName(const QString &session); diff --git a/src/plugins/projectexplorer/sessiondialog.cpp b/src/plugins/projectexplorer/sessiondialog.cpp index 2dfb944a35..6cda77927b 100644 --- a/src/plugins/projectexplorer/sessiondialog.cpp +++ b/src/plugins/projectexplorer/sessiondialog.cpp @@ -26,6 +26,8 @@ #include "sessiondialog.h" #include "session.h" +#include <utils/algorithm.h> + #include <QInputDialog> #include <QValidator> @@ -131,7 +133,7 @@ SessionDialog::SessionDialog(QWidget *parent) : QDialog(parent) connect(m_ui.btClone, &QAbstractButton::clicked, m_ui.sessionView, &SessionView::cloneCurrentSession); connect(m_ui.btDelete, &QAbstractButton::clicked, - m_ui.sessionView, &SessionView::deleteCurrentSession); + m_ui.sessionView, &SessionView::deleteSelectedSessions); connect(m_ui.btSwitch, &QAbstractButton::clicked, m_ui.sessionView, &SessionView::switchToCurrentSession); connect(m_ui.btRename, &QAbstractButton::clicked, @@ -157,21 +159,23 @@ bool SessionDialog::autoLoadSession() const return m_ui.autoLoadCheckBox->checkState() == Qt::Checked; } -void SessionDialog::updateActions(const QString &session) +void SessionDialog::updateActions(const QStringList &sessions) { - if (session.isEmpty()) { + if (sessions.isEmpty()) { m_ui.btDelete->setEnabled(false); m_ui.btRename->setEnabled(false); m_ui.btClone->setEnabled(false); m_ui.btSwitch->setEnabled(false); - } else { - bool isDefault = (session == QLatin1String("default")); - bool isActive = (session == SessionManager::activeSession()); - m_ui.btDelete->setEnabled(!isActive && !isDefault); - m_ui.btRename->setEnabled(!isDefault); - m_ui.btClone->setEnabled(true); - m_ui.btSwitch->setEnabled(true); + return; } + const bool defaultIsSelected = sessions.contains("default"); + const bool activeIsSelected = Utils::anyOf(sessions, [](const QString &session) { + return session == SessionManager::activeSession(); + }); + m_ui.btDelete->setEnabled(!defaultIsSelected && !activeIsSelected); + m_ui.btRename->setEnabled(sessions.size() == 1 && !defaultIsSelected); + m_ui.btClone->setEnabled(sessions.size() == 1); + m_ui.btSwitch->setEnabled(sessions.size() == 1); } } // namespace Internal diff --git a/src/plugins/projectexplorer/sessiondialog.h b/src/plugins/projectexplorer/sessiondialog.h index 654736694e..701748b55c 100644 --- a/src/plugins/projectexplorer/sessiondialog.h +++ b/src/plugins/projectexplorer/sessiondialog.h @@ -49,7 +49,7 @@ public: bool autoLoadSession() const; private: - void updateActions(const QString &session); + void updateActions(const QStringList &sessions); Ui::SessionDialog m_ui; }; diff --git a/src/plugins/projectexplorer/sessionmodel.cpp b/src/plugins/projectexplorer/sessionmodel.cpp index 21b3ec3f48..bcf13e96dd 100644 --- a/src/plugins/projectexplorer/sessionmodel.cpp +++ b/src/plugins/projectexplorer/sessionmodel.cpp @@ -55,7 +55,7 @@ int SessionModel::indexOfSession(const QString &session) return SessionManager::sessions().indexOf(session); } -QString SessionModel::sessionAt(int row) +QString SessionModel::sessionAt(int row) const { return SessionManager::sessions().value(row, QString()); } @@ -209,12 +209,12 @@ void SessionModel::cloneSession(QWidget *parent, const QString &session) }); } -void SessionModel::deleteSession(const QString &session) +void SessionModel::deleteSessions(const QStringList &sessions) { - if (!SessionManager::confirmSessionDelete(session)) + if (!SessionManager::confirmSessionDelete(sessions)) return; beginResetModel(); - SessionManager::deleteSession(session); + SessionManager::deleteSessions(sessions); endResetModel(); } diff --git a/src/plugins/projectexplorer/sessionmodel.h b/src/plugins/projectexplorer/sessionmodel.h index 7feff2b433..63f9984215 100644 --- a/src/plugins/projectexplorer/sessionmodel.h +++ b/src/plugins/projectexplorer/sessionmodel.h @@ -53,7 +53,7 @@ public: explicit SessionModel(QObject *parent = nullptr); int indexOfSession(const QString &session); - QString sessionAt(int row); + QString sessionAt(int row) const; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; @@ -72,7 +72,7 @@ public slots: void resetSessions(); void newSession(QWidget *parent); void cloneSession(QWidget *parent, const QString &session); - void deleteSession(const QString &session); + void deleteSessions(const QStringList &sessions); void renameSession(QWidget *parent, const QString &session); void switchToSession(const QString &session); diff --git a/src/plugins/projectexplorer/sessionview.cpp b/src/plugins/projectexplorer/sessionview.cpp index 4e3bda6510..07d918ca77 100644 --- a/src/plugins/projectexplorer/sessionview.cpp +++ b/src/plugins/projectexplorer/sessionview.cpp @@ -27,9 +27,12 @@ #include "session.h" +#include <utils/algorithm.h> + +#include <QHeaderView> #include <QItemSelection> +#include <QStringList> #include <QStyledItemDelegate> -#include <QHeaderView> namespace ProjectExplorer { namespace Internal { @@ -57,7 +60,7 @@ SessionView::SessionView(QWidget *parent) { setItemDelegate(new RemoveItemFocusDelegate(this)); setSelectionBehavior(QAbstractItemView::SelectRows); - setSelectionMode(QAbstractItemView::SingleSelection); + setSelectionMode(QAbstractItemView::ExtendedSelection); setWordWrap(false); setRootIsDecorated(false); @@ -74,9 +77,8 @@ SessionView::SessionView(QWidget *parent) connect(this, &Utils::TreeView::activated, [this](const QModelIndex &index){ emit activated(m_sessionModel.sessionAt(index.row())); }); - connect(selectionModel(), &QItemSelectionModel::currentRowChanged, [this] - (const QModelIndex &index) { - emit selected(m_sessionModel.sessionAt(index.row())); + connect(selectionModel(), &QItemSelectionModel::selectionChanged, [this] { + emit selected(selectedSessions()); }); connect(&m_sessionModel, &SessionModel::sessionSwitched, @@ -92,9 +94,14 @@ void SessionView::createNewSession() m_sessionModel.newSession(this); } -void SessionView::deleteCurrentSession() +void SessionView::deleteSelectedSessions() +{ + deleteSessions(selectedSessions()); +} + +void SessionView::deleteSessions(const QStringList &sessions) { - m_sessionModel.deleteSession(currentSession()); + m_sessionModel.deleteSessions(sessions); } void SessionView::cloneCurrentSession() @@ -141,5 +148,25 @@ void SessionView::showEvent(QShowEvent *event) setFocus(); } +void SessionView::keyPressEvent(QKeyEvent *event) +{ + if (event->key() != Qt::Key_Delete) { + TreeView::keyPressEvent(event); + return; + } + const QStringList sessions = selectedSessions(); + if (!sessions.contains("default") && !Utils::anyOf(sessions, + [](const QString &session) { return session == SessionManager::activeSession(); })) { + deleteSessions(sessions); + } +} + +QStringList SessionView::selectedSessions() const +{ + return Utils::transform(selectionModel()->selectedRows(), [this](const QModelIndex &index) { + return m_sessionModel.sessionAt(index.row()); + }); +} + } // namespace Internal } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/sessionview.h b/src/plugins/projectexplorer/sessionview.h index 0f85e6ed21..21988c3838 100644 --- a/src/plugins/projectexplorer/sessionview.h +++ b/src/plugins/projectexplorer/sessionview.h @@ -41,7 +41,7 @@ public: explicit SessionView(QWidget *parent = nullptr); void createNewSession(); - void deleteCurrentSession(); + void deleteSelectedSessions(); void cloneCurrentSession(); void renameCurrentSession(); void switchToCurrentSession(); @@ -51,15 +51,18 @@ public: void selectActiveSession(); void selectSession(const QString &sessionName); -protected: - void showEvent(QShowEvent* event) override; - signals: void activated(const QString &session); - void selected(const QString &session); + void selected(const QStringList &sessions); void sessionSwitched(); private: + void showEvent(QShowEvent* event) override; + void keyPressEvent(QKeyEvent *event) override; + + void deleteSessions(const QStringList &sessions); + QStringList selectedSessions() const; + SessionModel m_sessionModel; }; diff --git a/src/plugins/projectexplorer/target.cpp b/src/plugins/projectexplorer/target.cpp index 70d742e34e..d02dc8c6e8 100644 --- a/src/plugins/projectexplorer/target.cpp +++ b/src/plugins/projectexplorer/target.cpp @@ -102,7 +102,7 @@ public: QList<RunConfiguration *> m_runConfigurations; RunConfiguration* m_activeRunConfiguration = nullptr; DeploymentData m_deploymentData; - BuildTargetInfoList m_appTargets; + QList<BuildTargetInfo> m_appTargets; QVariantMap m_pluginSettings; Kit *const m_kit; @@ -345,19 +345,26 @@ DeploymentData Target::deploymentData() const return d->m_deploymentData; } -void Target::setApplicationTargets(const BuildTargetInfoList &appTargets) +void Target::setApplicationTargets(const QList<BuildTargetInfo> &appTargets) { - if (d->m_appTargets != appTargets) { + if (appTargets.toSet() != d->m_appTargets.toSet()) { d->m_appTargets = appTargets; emit applicationTargetsChanged(); } } -BuildTargetInfoList Target::applicationTargets() const +const QList<BuildTargetInfo> Target::applicationTargets() const { return d->m_appTargets; } +BuildTargetInfo Target::buildTarget(const QString &buildKey) const +{ + return Utils::findOrDefault(d->m_appTargets, [&buildKey](const BuildTargetInfo &ti) { + return ti.buildKey == buildKey; + }); +} + QList<ProjectConfiguration *> Target::projectConfigurations() const { QList<ProjectConfiguration *> result; @@ -452,7 +459,7 @@ void Target::setOverlayIcon(const QIcon &icon) QString Target::overlayIconToolTip() { - IDevice::ConstPtr current = DeviceKitInformation::device(kit()); + IDevice::ConstPtr current = DeviceKitAspect::device(kit()); return current.isNull() ? QString() : formatDeviceInfo(current->deviceInformation()); } @@ -680,9 +687,14 @@ QVariant Target::additionalData(Core::Id id) const return project()->additionalData(id, this); } +MakeInstallCommand Target::makeInstallCommand(const QString &installRoot) const +{ + return project()->makeInstallCommand(this, installRoot); +} + void Target::updateDeviceState() { - IDevice::ConstPtr current = DeviceKitInformation::device(kit()); + IDevice::ConstPtr current = DeviceKitAspect::device(kit()); QIcon overlay; static const QIcon disconnected = Icons::DEVICE_DISCONNECTED_INDICATOR_OVERLAY.icon(); diff --git a/src/plugins/projectexplorer/target.h b/src/plugins/projectexplorer/target.h index 18eb92d273..a630e28468 100644 --- a/src/plugins/projectexplorer/target.h +++ b/src/plugins/projectexplorer/target.h @@ -34,18 +34,13 @@ QT_FORWARD_DECLARE_CLASS(QIcon) -namespace Utils { class Environment; } - namespace ProjectExplorer { class BuildConfiguration; -class BuildTargetInfoList; +class BuildTargetInfo; class DeployConfiguration; -class DeployConfigurationFactory; class DeploymentData; -class BuildConfigurationFactory; -class RunConfigurationFactory; class Kit; -class NamedWidget; +class MakeInstallCommand; class Project; class RunConfiguration; @@ -86,8 +81,9 @@ public: void setDeploymentData(const DeploymentData &deploymentData); DeploymentData deploymentData() const; - void setApplicationTargets(const BuildTargetInfoList &appTargets); - BuildTargetInfoList applicationTargets() const; + void setApplicationTargets(const QList<BuildTargetInfo> &appTargets); + const QList<BuildTargetInfo> applicationTargets() const; + BuildTargetInfo buildTarget(const QString &buildKey) const; QList<ProjectConfiguration *> projectConfigurations() const; @@ -121,6 +117,7 @@ public: void setNamedSettings(const QString &name, const QVariant &value); QVariant additionalData(Core::Id id) const; + MakeInstallCommand makeInstallCommand(const QString &installRoot) const; template<typename S, typename R, typename T> void subscribeSignal(void (S::*sig)(), R*recv, T (R::*sl)()) { diff --git a/src/plugins/projectexplorer/targetsettingspanel.cpp b/src/plugins/projectexplorer/targetsettingspanel.cpp index 78d7daabc8..adaba34842 100644 --- a/src/plugins/projectexplorer/targetsettingspanel.cpp +++ b/src/plugins/projectexplorer/targetsettingspanel.cpp @@ -80,6 +80,12 @@ class TargetSetupPageWrapper : public QWidget public: explicit TargetSetupPageWrapper(Project *project); + void ensureSetupPage() + { + if (!m_targetSetupPage) + addTargetSetupPage(); + } + protected: void keyReleaseEvent(QKeyEvent *event) override { @@ -89,24 +95,26 @@ protected: void keyPressEvent(QKeyEvent *event) override { + if (m_targetSetupPage && m_targetSetupPage->importLineEditHasFocus()) + return; if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { event->accept(); - done(); + if (m_targetSetupPage) + done(); } } private: void done() { + QTC_ASSERT(m_targetSetupPage, return); + m_targetSetupPage->disconnect(); m_targetSetupPage->setupProject(m_project); + m_targetSetupPage->deleteLater(); + m_targetSetupPage = nullptr; Core::ModeManager::activateMode(Core::Constants::MODE_EDIT); } - void cancel() - { - ProjectExplorerPlugin::unloadProject(m_project); - } - void kitUpdated(ProjectExplorer::Kit *k) { if (k == KitManager::defaultKit()) @@ -115,29 +123,21 @@ private: void completeChanged() { - m_configureButton->setEnabled(m_targetSetupPage->isComplete()); + m_configureButton->setEnabled(m_targetSetupPage && m_targetSetupPage->isComplete()); } void updateNoteText(); + void addTargetSetupPage(); - Project *m_project; - TargetSetupPage *m_targetSetupPage; - QPushButton *m_configureButton; + Project * const m_project; + TargetSetupPage *m_targetSetupPage = nullptr; + QPushButton *m_configureButton = nullptr; + QVBoxLayout *m_setupPageContainer = nullptr; }; TargetSetupPageWrapper::TargetSetupPageWrapper(Project *project) : m_project(project) { - m_targetSetupPage = new TargetSetupPage(this); - m_targetSetupPage->setUseScrollArea(false); - m_targetSetupPage->setProjectPath(project->projectFilePath().toString()); - m_targetSetupPage->setRequiredKitPredicate(project->requiredKitPredicate()); - m_targetSetupPage->setPreferredKitPredicate(project->preferredKitPredicate()); - m_targetSetupPage->setProjectImporter(project->projectImporter()); - m_targetSetupPage->initializePage(); - m_targetSetupPage->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); - updateNoteText(); - auto box = new QDialogButtonBox(this); m_configureButton = new QPushButton(this); @@ -150,24 +150,20 @@ TargetSetupPageWrapper::TargetSetupPageWrapper(Project *project) auto layout = new QVBoxLayout(this); layout->setMargin(0); - layout->addWidget(m_targetSetupPage); + m_setupPageContainer = new QVBoxLayout; + layout->addLayout(m_setupPageContainer); layout->addLayout(hbox); layout->addStretch(10); - completeChanged(); - connect(m_configureButton, &QAbstractButton::clicked, this, &TargetSetupPageWrapper::done); - connect(m_targetSetupPage, &QWizardPage::completeChanged, - this, &TargetSetupPageWrapper::completeChanged); - connect(KitManager::instance(), &KitManager::defaultkitChanged, - this, &TargetSetupPageWrapper::updateNoteText); - connect(KitManager::instance(), &KitManager::kitUpdated, - this, &TargetSetupPageWrapper::kitUpdated); } void TargetSetupPageWrapper::updateNoteText() { + if (!m_targetSetupPage) + return; + Kit *k = KitManager::defaultKit(); QString text; @@ -198,6 +194,29 @@ void TargetSetupPageWrapper::updateNoteText() m_targetSetupPage->showOptionsHint(showHint); } +void TargetSetupPageWrapper::addTargetSetupPage() +{ + m_targetSetupPage = new TargetSetupPage(this); + m_targetSetupPage->setUseScrollArea(false); + m_targetSetupPage->setProjectPath(m_project->projectFilePath().toString()); + m_targetSetupPage->setRequiredKitPredicate(m_project->requiredKitPredicate()); + m_targetSetupPage->setPreferredKitPredicate(m_project->preferredKitPredicate()); + m_targetSetupPage->setProjectImporter(m_project->projectImporter()); + m_targetSetupPage->initializePage(); + m_targetSetupPage->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + m_setupPageContainer->addWidget(m_targetSetupPage); + updateNoteText(); + + completeChanged(); + + connect(m_targetSetupPage, &QWizardPage::completeChanged, + this, &TargetSetupPageWrapper::completeChanged); + connect(KitManager::instance(), &KitManager::defaultkitChanged, + this, &TargetSetupPageWrapper::updateNoteText); + connect(KitManager::instance(), &KitManager::kitUpdated, + this, &TargetSetupPageWrapper::kitUpdated); +} + // // TargetSettingsPanelItem // @@ -228,6 +247,7 @@ public: QPointer<QWidget> m_noKitLabel; QPointer<QWidget> m_configurePage; QPointer<QWidget> m_configuredPage; + TargetSetupPageWrapper *m_targetSetupPageWrapper = nullptr; }; void TargetGroupItemPrivate::ensureWidget() @@ -253,12 +273,13 @@ void TargetGroupItemPrivate::ensureWidget() } if (!m_configurePage) { - auto widget = new TargetSetupPageWrapper(m_project); + m_targetSetupPageWrapper = new TargetSetupPageWrapper(m_project); m_configurePage = new PanelsWidget(tr("Configure Project"), QIcon(":/projectexplorer/images/unconfigured.png"), - widget); - m_configurePage->setFocusProxy(widget); + m_targetSetupPageWrapper); + m_configurePage->setFocusProxy(m_targetSetupPageWrapper); } + m_targetSetupPageWrapper->ensureSetupPage(); if (!m_configuredPage) { auto widget = new QWidget; @@ -283,7 +304,7 @@ class TargetItem : public TypedTreeItem<TreeItem, TargetGroupItem> public: enum { DefaultPage = 0 }; // Build page. - TargetItem(Project *project, Id kitId, const QList<Task> &issues) + TargetItem(Project *project, Id kitId, const Tasks &issues) : m_project(project), m_kitId(kitId), m_kitIssues(issues) { m_kitWarningForProject = containsType(m_kitIssues, Task::TaskType::Warning); @@ -483,7 +504,7 @@ public: int m_currentChild = DefaultPage; bool m_kitErrorsForProject = false; bool m_kitWarningForProject = false; - QList<Task> m_kitIssues; + Tasks m_kitIssues; private: enum class IconOverlay { diff --git a/src/plugins/projectexplorer/targetsetuppage.cpp b/src/plugins/projectexplorer/targetsetuppage.cpp index 501e0efc39..0a750199fe 100644 --- a/src/plugins/projectexplorer/targetsetuppage.cpp +++ b/src/plugins/projectexplorer/targetsetuppage.cpp @@ -67,10 +67,10 @@ IPotentialKit::~IPotentialKit() } namespace Internal { -static Utils::FileName importDirectory(const QString &projectPath) +static Utils::FilePath importDirectory(const QString &projectPath) { // Setup import widget: - auto path = Utils::FileName::fromString(projectPath); + auto path = Utils::FilePath::fromString(projectPath); path = path.parentDir(); // base dir path = path.parentDir(); // parent dir @@ -210,7 +210,7 @@ TargetSetupPage::TargetSetupPage(QWidget *parent) : connect(km, &KitManager::kitRemoved, this, &TargetSetupPage::handleKitRemoval); connect(km, &KitManager::kitUpdated, this, &TargetSetupPage::handleKitUpdate); connect(m_importWidget, &ImportWidget::importFrom, - this, [this](const Utils::FileName &dir) { import(dir); }); + this, [this](const Utils::FilePath &dir) { import(dir); }); setProperty(Utils::SHORT_TITLE_PROPERTY, tr("Kits")); } @@ -328,6 +328,11 @@ void TargetSetupPage::setProjectImporter(ProjectImporter *importer) initializePage(); } +bool TargetSetupPage::importLineEditHasFocus() const +{ + return m_importWidget->lineEditHasFocus(); +} + void TargetSetupPage::setNoteText(const QString &text) { m_ui->descriptionLabel->setText(text); @@ -347,7 +352,7 @@ void TargetSetupPage::setupImports() QStringList toImport = m_importer->importCandidates(); foreach (const QString &path, toImport) - import(Utils::FileName::fromString(path), true); + import(Utils::FilePath::fromString(path), true); } void TargetSetupPage::handleKitAddition(Kit *k) @@ -485,7 +490,7 @@ bool TargetSetupPage::isUpdating() const return false; } -void TargetSetupPage::import(const Utils::FileName &path, bool silent) +void TargetSetupPage::import(const Utils::FilePath &path, bool silent) { if (!m_importer) return; diff --git a/src/plugins/projectexplorer/targetsetuppage.h b/src/plugins/projectexplorer/targetsetuppage.h index 86eeb65013..102ffbd57c 100644 --- a/src/plugins/projectexplorer/targetsetuppage.h +++ b/src/plugins/projectexplorer/targetsetuppage.h @@ -40,7 +40,7 @@ QT_FORWARD_DECLARE_CLASS(QSpacerItem) namespace Core { class Id; } -namespace Utils { class FileName; } +namespace Utils { class FilePath; } namespace ProjectExplorer { class Kit; @@ -70,6 +70,7 @@ public: void setPreferredKitPredicate(const ProjectExplorer::Kit::Predicate &predicate); void setProjectPath(const QString &dir); void setProjectImporter(ProjectImporter *importer); + bool importLineEditHasFocus() const; /// Sets whether the targetsetupage uses a scrollarea /// to host the widgets from the factories @@ -105,7 +106,7 @@ private: Internal::TargetSetupWidget *addWidget(Kit *k); void setupImports(); - void import(const Utils::FileName &path, bool silent = false); + void import(const Utils::FilePath &path, bool silent = false); void setupWidgets(const QString &filterText = QString()); void reset(); diff --git a/src/plugins/projectexplorer/targetsetupwidget.cpp b/src/plugins/projectexplorer/targetsetupwidget.cpp index 88e5b4ab06..6aa66517e3 100644 --- a/src/plugins/projectexplorer/targetsetupwidget.cpp +++ b/src/plugins/projectexplorer/targetsetupwidget.cpp @@ -29,7 +29,6 @@ #include "buildinfo.h" #include "projectexplorerconstants.h" #include "kit.h" -#include "kitconfigwidget.h" #include "kitmanager.h" #include "kitoptionspage.h" @@ -73,7 +72,7 @@ TargetSetupWidget::TargetSetupWidget(Kit *k, const QString &projectPath) : auto panel = new Utils::FadingWidget(m_detailsWidget); auto panelLayout = new QHBoxLayout(panel); - m_manageButton = new QPushButton(KitConfigWidget::msgManage()); + m_manageButton = new QPushButton(KitAspectWidget::msgManage()); panelLayout->addWidget(m_manageButton); m_detailsWidget->setToolWidget(panel); @@ -319,7 +318,7 @@ QPair<Task::TaskType, QString> TargetSetupWidget::findIssues(const BuildInfo &in return qMakePair(Task::Unknown, QString()); QString buildDir = info.buildDirectory.toString(); - QList<Task> issues; + Tasks issues; if (info.factory()) issues = info.factory()->reportIssues(m_kit, m_projectPath, buildDir); diff --git a/src/plugins/projectexplorer/task.cpp b/src/plugins/projectexplorer/task.cpp index 83409fadd5..2a323f0041 100644 --- a/src/plugins/projectexplorer/task.cpp +++ b/src/plugins/projectexplorer/task.cpp @@ -25,6 +25,7 @@ #include "task.h" +#include "fileinsessionfinder.h" #include "projectexplorerconstants.h" #include <app/app_version.h> @@ -34,6 +35,7 @@ #include <utils/utilsicons.h> #include <utils/qtcassert.h> +#include <QFileInfo> #include <QTextStream> namespace ProjectExplorer @@ -60,13 +62,14 @@ unsigned int Task::s_nextId = 1; */ Task::Task(TaskType type_, const QString &description_, - const Utils::FileName &file_, int line_, Core::Id category_, + const Utils::FilePath &file_, int line_, Core::Id category_, const QIcon &icon, Options options) : taskId(s_nextId), type(type_), options(options), description(description_), - file(file_), line(line_), movedLine(line_), category(category_), + line(line_), movedLine(line_), category(category_), icon(icon.isNull() ? taskTypeIcon(type_) : icon) { ++s_nextId; + setFile(file_); } Task Task::compilerMissingTask() @@ -76,7 +79,7 @@ Task Task::compilerMissingTask() "%1 needs a compiler set up to build. " "Configure a compiler in the kit options.") .arg(Core::Constants::IDE_DISPLAY_NAME), - Utils::FileName(), -1, + Utils::FilePath(), -1, Constants::TASK_CATEGORY_BUILDSYSTEM); } @@ -87,7 +90,7 @@ Task Task::buildConfigurationMissingTask() "%1 needs a build configuration set up to build. " "Configure a build configuration in the project settings.") .arg(Core::Constants::IDE_DISPLAY_NAME), - Utils::FileName(), -1, + Utils::FilePath(), -1, Constants::TASK_CATEGORY_BUILDSYSTEM); } @@ -108,7 +111,7 @@ void Task::clear() taskId = 0; type = Task::Unknown; description.clear(); - file = Utils::FileName(); + file = Utils::FilePath(); line = -1; movedLine = -1; category = Core::Id(); @@ -117,6 +120,18 @@ void Task::clear() m_mark.clear(); } +void Task::setFile(const Utils::FilePath &file_) +{ + file = file_; + if (!file.isEmpty() && !file.toFileInfo().isAbsolute()) { + Utils::FilePathList possiblePaths = Internal::findFileInSession(file); + if (possiblePaths.length() == 1) + file = possiblePaths.first(); + else + fileCandidates = possiblePaths; + } +} + // // functions // @@ -153,7 +168,7 @@ uint qHash(const Task &task) return task.taskId; } -QString toHtml(const QList<Task> &issues) +QString toHtml(const Tasks &issues) { QString result; QTextStream str(&result); @@ -176,7 +191,7 @@ QString toHtml(const QList<Task> &issues) return result; } -bool containsType(const QList<Task> &issues, Task::TaskType type) +bool containsType(const Tasks &issues, Task::TaskType type) { return Utils::contains(issues, [type](const Task &t) { return t.type == type; }); } diff --git a/src/plugins/projectexplorer/task.h b/src/plugins/projectexplorer/task.h index f3e1b76c88..739e90f176 100644 --- a/src/plugins/projectexplorer/task.h +++ b/src/plugins/projectexplorer/task.h @@ -61,7 +61,7 @@ public: Task() = default; Task(TaskType type, const QString &description, - const Utils::FileName &file, int line, Core::Id category, + const Utils::FilePath &file, int line, Core::Id category, const QIcon &icon = QIcon(), Options options = AddTextMark | FlashWorthy); @@ -70,12 +70,14 @@ public: bool isNull() const; void clear(); + void setFile(const Utils::FilePath &file); unsigned int taskId = 0; TaskType type = Unknown; Options options = AddTextMark | FlashWorthy; QString description; - Utils::FileName file; + Utils::FilePath file; + Utils::FilePathList fileCandidates; int line = -1; int movedLine = -1; // contains a line number if the line was moved in the editor Core::Id category; @@ -100,13 +102,15 @@ private: friend class TaskHub; }; +using Tasks = QVector<Task>; + bool PROJECTEXPLORER_EXPORT operator==(const Task &t1, const Task &t2); uint PROJECTEXPLORER_EXPORT qHash(const Task &task); bool PROJECTEXPLORER_EXPORT operator<(const Task &a, const Task &b); -QString PROJECTEXPLORER_EXPORT toHtml(const QList<Task> &issues); -bool PROJECTEXPLORER_EXPORT containsType(const QList<Task> &issues, Task::TaskType); +QString PROJECTEXPLORER_EXPORT toHtml(const Tasks &issues); +bool PROJECTEXPLORER_EXPORT containsType(const Tasks &issues, Task::TaskType); } //namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/taskhub.cpp b/src/plugins/projectexplorer/taskhub.cpp index 5ed8aa9677..10b04c4d5b 100644 --- a/src/plugins/projectexplorer/taskhub.cpp +++ b/src/plugins/projectexplorer/taskhub.cpp @@ -79,7 +79,7 @@ public: bool isClickable() const override; void clicked() override; - void updateFileName(const FileName &fileName) override; + void updateFileName(const FilePath &fileName) override; void updateLineNumber(int lineNumber) override; void removedFromEditor() override; private: @@ -92,10 +92,10 @@ void TaskMark::updateLineNumber(int lineNumber) TextMark::updateLineNumber(lineNumber); } -void TaskMark::updateFileName(const FileName &fileName) +void TaskMark::updateFileName(const FilePath &fileName) { TaskHub::updateTaskFileName(m_id, fileName.toString()); - TextMark::updateFileName(FileName::fromString(fileName.toString())); + TextMark::updateFileName(FilePath::fromString(fileName.toString())); } void TaskMark::removedFromEditor() @@ -117,7 +117,7 @@ TaskHub::TaskHub() { m_instance = this; qRegisterMetaType<ProjectExplorer::Task>("ProjectExplorer::Task"); - qRegisterMetaType<QList<ProjectExplorer::Task> >("QList<ProjectExplorer::Task>"); + qRegisterMetaType<Tasks >("Tasks"); } TaskHub::~TaskHub() @@ -138,7 +138,7 @@ TaskHub *TaskHub::instance() return m_instance; } -void TaskHub::addTask(Task::TaskType type, const QString &description, Core::Id category, const Utils::FileName &file, int line) +void TaskHub::addTask(Task::TaskType type, const QString &description, Core::Id category, const Utils::FilePath &file, int line) { addTask(Task(type, description, file, line, category)); } diff --git a/src/plugins/projectexplorer/taskhub.h b/src/plugins/projectexplorer/taskhub.h index 80bb6a5219..fc2c489c1a 100644 --- a/src/plugins/projectexplorer/taskhub.h +++ b/src/plugins/projectexplorer/taskhub.h @@ -43,7 +43,7 @@ public: // Convenience overload static void addTask(Task::TaskType type, const QString &description, Core::Id category, - const Utils::FileName &file = Utils::FileName(), + const Utils::FilePath &file = Utils::FilePath(), int line = -1); public slots: diff --git a/src/plugins/projectexplorer/taskmodel.cpp b/src/plugins/projectexplorer/taskmodel.cpp index dcdf1554c8..8fcc48e737 100644 --- a/src/plugins/projectexplorer/taskmodel.cpp +++ b/src/plugins/projectexplorer/taskmodel.cpp @@ -25,11 +25,13 @@ #include "taskmodel.h" +#include "fileinsessionfinder.h" #include "task.h" #include "taskhub.h" #include <utils/qtcassert.h> +#include <QFileInfo> #include <QFontMetrics> #include <algorithm> @@ -84,12 +86,12 @@ void TaskModel::addCategory(Core::Id categoryId, const QString &categoryName) m_categories.insert(categoryId, data); } -QList<Task> TaskModel::tasks(Core::Id categoryId) const +Tasks TaskModel::tasks(Core::Id categoryId) const { if (!categoryId.isValid()) return m_tasks; - QList<Task> taskList; + Tasks taskList; foreach (const Task &t, m_tasks) { if (t.category == categoryId) taskList.append(t); @@ -117,17 +119,18 @@ void TaskModel::addTask(const Task &task) endInsertRows(); } -void TaskModel::removeTask(const Task &task) +void TaskModel::removeTask(unsigned int id) { - int index = m_tasks.indexOf(task); - if (index >= 0) { + for (int index = 0; index < m_tasks.length(); ++index) { + if (m_tasks.at(index).taskId != id) + continue; const Task &t = m_tasks.at(index); - beginRemoveRows(QModelIndex(), index, index); - m_categories[task.category].removeTask(t); + m_categories[t.category].removeTask(t); m_categories[Core::Id()].removeTask(t); m_tasks.removeAt(index); endRemoveRows(); + break; } } @@ -144,7 +147,7 @@ void TaskModel::updateTaskFileName(unsigned int id, const QString &fileName) int i = rowForId(id); QTC_ASSERT(i != -1, return); if (m_tasks.at(i).taskId == id) { - m_tasks[i].file = Utils::FileName::fromString(fileName); + m_tasks[i].file = Utils::FilePath::fromString(fileName); emit dataChanged(index(i, 0), index(i, 0)); } } @@ -294,7 +297,7 @@ int TaskModel::sizeOfFile(const QFont &font) if (pos != -1) filename = filename.mid(pos +1); - m_maxSizeOfFileName = qMax(m_maxSizeOfFileName, fm.width(filename)); + m_maxSizeOfFileName = qMax(m_maxSizeOfFileName, fm.horizontalAdvance(filename)); } m_lastMaxSizeIndex = count - 1; return m_maxSizeOfFileName; @@ -305,7 +308,7 @@ int TaskModel::sizeOfLineNumber(const QFont &font) if (m_sizeOfLineNumber == 0 || font != m_lineMeasurementFont) { QFontMetrics fm(font); m_lineMeasurementFont = font; - m_sizeOfLineNumber = fm.width(QLatin1String("88888")); + m_sizeOfLineNumber = fm.horizontalAdvance(QLatin1String("88888")); } return m_sizeOfLineNumber; } @@ -323,174 +326,44 @@ void TaskModel::setFileNotFound(const QModelIndex &idx, bool b) // TaskFilterModel ///// -TaskFilterModel::TaskFilterModel(TaskModel *sourceModel, QObject *parent) : QAbstractItemModel(parent), - m_sourceModel(sourceModel) +TaskFilterModel::TaskFilterModel(TaskModel *sourceModel, QObject *parent) + : QSortFilterProxyModel(parent) { - Q_ASSERT(m_sourceModel); - updateMapping(); - - connect(m_sourceModel, &QAbstractItemModel::rowsInserted, - this, &TaskFilterModel::handleNewRows); - - connect(m_sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved, - this, &TaskFilterModel::handleRowsAboutToBeRemoved); - connect(m_sourceModel, &QAbstractItemModel::rowsRemoved, - this, [this](const QModelIndex &parent, int, int) { - QTC_ASSERT(!parent.isValid(), return); - if (m_beginRemoveRowsSent) { - m_beginRemoveRowsSent = false; - endRemoveRows(); - } - }); - - connect(m_sourceModel, &QAbstractItemModel::modelReset, - this, &TaskFilterModel::invalidateFilter); - - connect(m_sourceModel, &QAbstractItemModel::dataChanged, - this, &TaskFilterModel::handleDataChanged); - + QTC_ASSERT(sourceModel, return); + setSourceModel(sourceModel); m_includeUnknowns = m_includeWarnings = m_includeErrors = true; } -QModelIndex TaskFilterModel::index(int row, int column, const QModelIndex &parent) const -{ - if (parent.isValid()) - return QModelIndex(); - return createIndex(row, column); -} - -QModelIndex TaskFilterModel::parent(const QModelIndex &child) const -{ - Q_UNUSED(child) - return QModelIndex(); -} - -int TaskFilterModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return 0; - - return m_mapping.count(); -} - -int TaskFilterModel::columnCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return 0; - return m_sourceModel->columnCount(parent); -} - -QVariant TaskFilterModel::data(const QModelIndex &index, int role) const -{ - return m_sourceModel->data(mapToSource(index), role); -} - -static QPair<int, int> findFilteredRange(int first, int last, const QList<int> &list) +void TaskFilterModel::setFilterIncludesWarnings(bool b) { - auto filteredFirst = std::lower_bound(list.constBegin(), list.constEnd(), first); - auto filteredLast = std::upper_bound(filteredFirst, list.constEnd(), last); - return qMakePair(filteredFirst - list.constBegin(), filteredLast - list.constBegin() - 1); + m_includeWarnings = b; + m_includeUnknowns = b; // "Unknowns" are often associated with warnings + invalidateFilter(); } -void TaskFilterModel::handleNewRows(const QModelIndex &index, int first, int last) +void TaskFilterModel::updateFilterProperties(const QString &filterText, + Qt::CaseSensitivity caseSensitivity, bool isRegexp) { - QTC_ASSERT(!index.isValid(), return); - - const int newItemCount = last - first + 1; - - QList<int> newMapping; - for (int i = first; i <= last; ++i) { - const Task &task = m_sourceModel->task(m_sourceModel->index(i, 0)); - if (filterAcceptsTask(task)) - newMapping.append(i); - } - - const int newMappingCount = newMapping.count(); - if (!newMappingCount) + if (filterText == m_filterText && m_filterCaseSensitivity == caseSensitivity + && m_filterStringIsRegexp == isRegexp) { return; - - int filteredFirst = -1; - if (last == m_sourceModel->rowCount() - 1) - filteredFirst = m_mapping.count(); - else - filteredFirst = std::lower_bound(m_mapping.constBegin(), m_mapping.constEnd(), first) - m_mapping.constBegin(); - - const int filteredLast = filteredFirst + newMappingCount - 1; - beginInsertRows(QModelIndex(), filteredFirst, filteredLast); - if (filteredFirst == m_mapping.count()) { - m_mapping.append(newMapping); - } else { - const QList<int> rest = m_mapping.mid(filteredFirst); - - m_mapping.reserve(m_mapping.count() + newMappingCount); - m_mapping.erase(m_mapping.begin() + filteredFirst, m_mapping.end()); - m_mapping.append(newMapping); - for (int pos : rest) - m_mapping.append(pos + newItemCount); } - endInsertRows(); -} - -void TaskFilterModel::handleRowsAboutToBeRemoved(const QModelIndex &index, int first, int last) -{ - m_beginRemoveRowsSent = false; - QTC_ASSERT(!index.isValid(), return); - - const QPair<int, int> range = findFilteredRange(first, last, m_mapping); - if (range.first <= range.second) { // remove corresponding rows in filtermodel - beginRemoveRows(QModelIndex(), range.first, range.second); - m_beginRemoveRowsSent = true; - m_mapping.erase(m_mapping.begin() + range.first, m_mapping.begin() + range.second + 1); + m_filterText = filterText; + m_filterCaseSensitivity = caseSensitivity; + m_filterStringIsRegexp = isRegexp; + if (m_filterStringIsRegexp) { + m_filterRegexp.setPattern(m_filterText); + m_filterRegexp.setPatternOptions(m_filterCaseSensitivity == Qt::CaseInsensitive + ? QRegularExpression::CaseInsensitiveOption + : QRegularExpression::NoPatternOption); } - // adapt existing mapping to removed source indices - const int sourceRemovedCount = (last - first) + 1; - for (int i = range.first; i < m_mapping.count(); ++i) - m_mapping[i] = m_mapping.at(i) - sourceRemovedCount; -} - -void TaskFilterModel::handleDataChanged(const QModelIndex &top, const QModelIndex &bottom) -{ - const QPair<int, int> range = findFilteredRange(top.row(), bottom.row(), m_mapping); - if (range.first > range.second) - return; - - emit dataChanged(index(range.first, top.column()), index(range.second, bottom.column())); -} - -QModelIndex TaskFilterModel::mapFromSource(const QModelIndex &idx) const -{ - if (!idx.isValid()) - return QModelIndex(); - auto it = std::lower_bound(m_mapping.constBegin(), m_mapping.constEnd(), idx.row()); - QTC_ASSERT(it != m_mapping.constEnd() && idx.row() == *it, return QModelIndex()); - return index(it - m_mapping.constBegin(), 0); -} - -QModelIndex TaskFilterModel::mapToSource(const QModelIndex &index) const -{ - if (!index.isValid()) - return QModelIndex(); - int row = index.row(); - QTC_ASSERT(row >= 0 && row < m_mapping.count(), return QModelIndex()); - return m_sourceModel->index(m_mapping.at(row), index.column(), index.parent()); -} - -void TaskFilterModel::invalidateFilter() -{ - beginResetModel(); - updateMapping(); - endResetModel(); + invalidateFilter(); } -void TaskFilterModel::updateMapping() const +bool TaskFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { - m_mapping.clear(); - for (int i = 0; i < m_sourceModel->rowCount(); ++i) { - QModelIndex index = m_sourceModel->index(i, 0); - const Task &task = m_sourceModel->task(index); - if (filterAcceptsTask(task)) - m_mapping.append(i); - } + Q_UNUSED(source_parent); + return filterAcceptsTask(taskModel()->tasks().at(source_row)); } bool TaskFilterModel::filterAcceptsTask(const Task &task) const @@ -508,9 +381,18 @@ bool TaskFilterModel::filterAcceptsTask(const Task &task) const break; } - if (m_categoryIds.contains(task.category)) + if (accept && m_categoryIds.contains(task.category)) accept = false; + if (accept && !m_filterText.isEmpty()) { + const auto accepts = [this](const QString &s) { + return m_filterStringIsRegexp ? m_filterRegexp.isValid() && s.contains(m_filterRegexp) + : s.contains(m_filterText, m_filterCaseSensitivity); + }; + if (!accepts(task.file.toString()) && !accepts(task.description)) + accept = false; + } + return accept; } diff --git a/src/plugins/projectexplorer/taskmodel.h b/src/plugins/projectexplorer/taskmodel.h index 4b2757c289..b1044a3234 100644 --- a/src/plugins/projectexplorer/taskmodel.h +++ b/src/plugins/projectexplorer/taskmodel.h @@ -25,9 +25,10 @@ #pragma once -#include <QAbstractItemModel> +#include <QSortFilterProxyModel> #include <QIcon> +#include <QRegularExpression> #include "task.h" @@ -53,9 +54,9 @@ public: QString categoryDisplayName(Core::Id categoryId) const; void addCategory(Core::Id categoryId, const QString &categoryName); - QList<Task> tasks(Core::Id categoryId = Core::Id()) const; - void addTask(const Task &task); - void removeTask(const Task &task); + Tasks tasks(Core::Id categoryId = Core::Id()) const; + void addTask(const Task &t); + void removeTask(unsigned int id); void clearTasks(Core::Id categoryId = Core::Id()); void updateTaskFileName(unsigned int id, const QString &fileName); void updateTaskLineNumber(unsigned int id, int line); @@ -110,7 +111,7 @@ private: }; QHash<Core::Id,CategoryData> m_categories; // category id to data - QList<Task> m_tasks; // all tasks (in order of id) + Tasks m_tasks; // all tasks (in order of id) QHash<QString,bool> m_fileNotFound; QFont m_fileMeasurementFont; @@ -120,26 +121,17 @@ private: int m_sizeOfLineNumber = 0; }; -class TaskFilterModel : public QAbstractItemModel +class TaskFilterModel : public QSortFilterProxyModel { Q_OBJECT public: TaskFilterModel(TaskModel *sourceModel, QObject *parent = nullptr); - TaskModel *taskModel() { return m_sourceModel; } - - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; - QModelIndex parent(const QModelIndex &child) const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - - bool filterIncludesUnknowns() const { return m_includeUnknowns; } - void setFilterIncludesUnknowns(bool b) { m_includeUnknowns = b; invalidateFilter(); } + TaskModel *taskModel() const { return static_cast<TaskModel *>(sourceModel()); } bool filterIncludesWarnings() const { return m_includeWarnings; } - void setFilterIncludesWarnings(bool b) { m_includeWarnings = b; invalidateFilter(); } + void setFilterIncludesWarnings(bool b); bool filterIncludesErrors() const { return m_includeErrors; } void setFilterIncludesErrors(bool b) { m_includeErrors = b; invalidateFilter(); } @@ -147,33 +139,27 @@ public: QList<Core::Id> filteredCategories() const { return m_categoryIds; } void setFilteredCategories(const QList<Core::Id> &categoryIds) { m_categoryIds = categoryIds; invalidateFilter(); } - Task task(const QModelIndex &index) const - { return m_sourceModel->task(mapToSource(index)); } + Task task(const QModelIndex &index) const { return taskModel()->task(mapToSource(index)); } bool hasFile(const QModelIndex &index) const - { return m_sourceModel->hasFile(mapToSource(index)); } + { return taskModel()->hasFile(mapToSource(index)); } - QModelIndex mapFromSource(const QModelIndex &idx) const; + void updateFilterProperties(const QString &filterText, Qt::CaseSensitivity caseSensitivity, + bool isRegex); private: - void handleNewRows(const QModelIndex &index, int first, int last); - void handleRowsAboutToBeRemoved(const QModelIndex &index, int first, int last); - void handleDataChanged(const QModelIndex &top, const QModelIndex &bottom); - - QModelIndex mapToSource(const QModelIndex &index) const; - void invalidateFilter(); - void updateMapping() const; + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; bool filterAcceptsTask(const Task &task) const; bool m_beginRemoveRowsSent = false; bool m_includeUnknowns; bool m_includeWarnings; bool m_includeErrors; + bool m_filterStringIsRegexp = false; + Qt::CaseSensitivity m_filterCaseSensitivity = Qt::CaseInsensitive; QList<Core::Id> m_categoryIds; - - mutable QList<int> m_mapping; - - TaskModel *m_sourceModel; + QString m_filterText; + QRegularExpression m_filterRegexp; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/taskwindow.cpp b/src/plugins/projectexplorer/taskwindow.cpp index 7d8010aa27..7a98d0fd33 100644 --- a/src/plugins/projectexplorer/taskwindow.cpp +++ b/src/plugins/projectexplorer/taskwindow.cpp @@ -38,6 +38,7 @@ #include <coreplugin/icontext.h> #include <utils/algorithm.h> +#include <utils/fileinprojectfinder.h> #include <utils/qtcassert.h> #include <utils/itemviews.h> #include <utils/utilsicons.h> @@ -236,7 +237,6 @@ static QToolButton *createFilterButton(const QIcon &icon, const QString &toolTip button->setToolTip(toolTip); button->setCheckable(true); button->setChecked(true); - button->setAutoRaise(true); button->setEnabled(true); QObject::connect(button, &QToolButton::toggled, receiver, lambda); return button; @@ -282,7 +282,6 @@ TaskWindow::TaskWindow() : d(std::make_unique<TaskWindowPrivate>()) d->m_categoriesButton->setIcon(Utils::Icons::FILTER.icon()); d->m_categoriesButton->setToolTip(tr("Filter by categories")); d->m_categoriesButton->setProperty("noArrow", true); - d->m_categoriesButton->setAutoRaise(true); d->m_categoriesButton->setPopupMode(QToolButton::InstantPopup); d->m_categoriesMenu = new QMenu(d->m_categoriesButton); @@ -290,6 +289,9 @@ TaskWindow::TaskWindow() : d(std::make_unique<TaskWindowPrivate>()) d->m_categoriesButton->setMenu(d->m_categoriesMenu); + setupFilterUi("IssuesPane.Filter"); + setFilteringEnabled(true); + TaskHub *hub = TaskHub::instance(); connect(hub, &TaskHub::categoryAdded, this, &TaskWindow::addCategory); connect(hub, &TaskHub::taskAdded, this, &TaskWindow::addTask); @@ -356,7 +358,7 @@ void TaskWindow::delayedInitialization() QList<QWidget*> TaskWindow::toolBarWidgets() const { - return {d->m_filterWarningsButton, d->m_categoriesButton}; + return {d->m_filterWarningsButton, d->m_categoriesButton, filterWidget()}; } QWidget *TaskWindow::outputWidget(QWidget *) @@ -416,7 +418,6 @@ void TaskWindow::loadSettings() if (value.isValid()) { bool includeWarnings = value.toBool(); d->m_filter->setFilterIncludesWarnings(includeWarnings); - d->m_filter->setFilterIncludesUnknowns(includeWarnings); d->m_filterWarningsButton->setDown(d->m_filter->filterIncludesWarnings()); } } @@ -454,7 +455,7 @@ void TaskWindow::addTask(const Task &task) void TaskWindow::removeTask(const Task &task) { - d->m_model->removeTask(task); + d->m_model->removeTask(task.taskId); emit tasksChanged(); navigateStateChanged(); @@ -498,6 +499,15 @@ void TaskWindow::triggerDefaultHandler(const QModelIndex &index) if (task.isNull()) return; + if (!task.file.isEmpty() && !task.file.toFileInfo().isAbsolute() + && !task.fileCandidates.empty()) { + const Utils::FilePath userChoice = Utils::chooseFileFromList(task.fileCandidates); + if (!userChoice.isEmpty()) { + task.file = userChoice; + updatedTaskFileName(task.taskId, task.file.toString()); + } + } + if (d->m_defaultHandler->canHandle(task)) { d->m_defaultHandler->handle(task); } else { @@ -526,7 +536,6 @@ void TaskWindow::actionTriggered() void TaskWindow::setShowWarnings(bool show) { d->m_filter->setFilterIncludesWarnings(show); - d->m_filter->setFilterIncludesUnknowns(show); // "Unknowns" are often associated with warnings } void TaskWindow::updateCategoriesMenu() @@ -658,6 +667,11 @@ void TaskWindow::goToPrev() triggerDefaultHandler(currentIndex); } +void TaskWindow::updateFilter() +{ + d->m_filter->updateFilterProperties(filterText(), filterCaseSensitivity(), filterUsesRegexp()); +} + bool TaskWindow::canNavigate() const { return true; @@ -786,7 +800,7 @@ void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, QString bottom = index.data(TaskModel::Description).toString().split(QLatin1Char('\n')).first(); painter->setClipRect(positions.textArea()); painter->drawText(positions.textAreaLeft(), positions.top() + fm.ascent(), bottom); - if (fm.width(bottom) > positions.textAreaWidth()) { + if (fm.horizontalAdvance(bottom) > positions.textAreaWidth()) { // draw a gradient to mask the text int gradientStart = positions.textAreaRight() - ELLIPSIS_GRADIENT_WIDTH + 1; QLinearGradient lg(gradientStart, 0, gradientStart + ELLIPSIS_GRADIENT_WIDTH, 0); @@ -840,7 +854,7 @@ void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const int pos = file.lastIndexOf(QLatin1Char('/')); if (pos != -1) file = file.mid(pos +1); - const int realFileWidth = fm.width(file); + const int realFileWidth = fm.horizontalAdvance(file); painter->setClipRect(positions.fileArea()); painter->drawText(qMin(positions.fileAreaLeft(), positions.fileAreaRight() - realFileWidth), positions.top() + fm.ascent(), file); @@ -877,7 +891,7 @@ void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, } painter->setClipRect(positions.lineArea()); - const int realLineWidth = fm.width(lineText); + const int realLineWidth = fm.horizontalAdvance(lineText); painter->drawText(positions.lineAreaRight() - realLineWidth, positions.top() + fm.ascent(), lineText); painter->setClipRect(opt.rect); diff --git a/src/plugins/projectexplorer/taskwindow.h b/src/plugins/projectexplorer/taskwindow.h index 290927b47b..efefb72658 100644 --- a/src/plugins/projectexplorer/taskwindow.h +++ b/src/plugins/projectexplorer/taskwindow.h @@ -82,6 +82,8 @@ signals: void tasksCleared(); private: + void updateFilter() override; + void addCategory(Core::Id categoryId, const QString &displayName, bool visible); void addTask(const ProjectExplorer::Task &task); void removeTask(const ProjectExplorer::Task &task); diff --git a/src/plugins/projectexplorer/toolchain.cpp b/src/plugins/projectexplorer/toolchain.cpp index 212427417a..66c5ac83db 100644 --- a/src/plugins/projectexplorer/toolchain.cpp +++ b/src/plugins/projectexplorer/toolchain.cpp @@ -46,6 +46,8 @@ static const char LANGUAGE_KEY_V2[] = "ProjectExplorer.ToolChain.LanguageV2"; // namespace ProjectExplorer { namespace Internal { +static QList<ToolChainFactory *> g_toolChainFactories; + // -------------------------------------------------------------------------- // ToolChainPrivate // -------------------------------------------------------------------------- @@ -55,10 +57,11 @@ class ToolChainPrivate public: using Detection = ToolChain::Detection; - explicit ToolChainPrivate(Core::Id typeId, Detection d) : + explicit ToolChainPrivate(Core::Id typeId) : m_id(QUuid::createUuid().toByteArray()), m_typeId(typeId), - m_detection(d) + m_predefinedMacrosCache(new ToolChain::MacrosCache::element_type()), + m_headerPathsCache(new ToolChain::HeaderPathsCache::element_type()) { QTC_ASSERT(m_typeId.isValid(), return); QTC_ASSERT(!m_typeId.toString().contains(QLatin1Char(':')), return); @@ -69,7 +72,10 @@ public: mutable QString m_displayName; Core::Id m_typeId; Core::Id m_language; - Detection m_detection; + Detection m_detection = ToolChain::UninitializedDetection; + + ToolChain::MacrosCache m_predefinedMacrosCache; + ToolChain::HeaderPathsCache m_headerPathsCache; }; @@ -116,20 +122,11 @@ QString languageId(Language l) // -------------------------------------------------------------------------- -ToolChain::ToolChain(Core::Id typeId, Detection d) : - d(std::make_unique<Internal::ToolChainPrivate>(typeId, d)) +ToolChain::ToolChain(Core::Id typeId) : + d(std::make_unique<Internal::ToolChainPrivate>(typeId)) { } -ToolChain::ToolChain(const ToolChain &other) : ToolChain(other.d->m_typeId, ManualDetection) -{ - d->m_language = other.d->m_language; - - // leave the autodetection bit at false. - d->m_displayName = QCoreApplication::translate("ProjectExplorer::ToolChain", "Clone of %1") - .arg(other.displayName()); -} - void ToolChain::setLanguage(Core::Id language) { QTC_ASSERT(!d->m_language.isValid() || isAutoDetected(), return); @@ -167,12 +164,12 @@ QByteArray ToolChain::id() const return d->m_id; } -Utils::FileNameList ToolChain::suggestedMkspecList() const +QStringList ToolChain::suggestedMkspecList() const { - return Utils::FileNameList(); + return {}; } -Utils::FileName ToolChain::suggestedDebugger() const +Utils::FilePath ToolChain::suggestedDebugger() const { return ToolChainManager::defaultDebugger(targetAbi()); } @@ -182,7 +179,7 @@ Core::Id ToolChain::typeId() const return d->m_typeId; } -QList<Abi> ToolChain::supportedAbis() const +Abis ToolChain::supportedAbis() const { return {targetAbi()}; } @@ -192,11 +189,6 @@ Core::Id ToolChain::language() const return d->m_language; } -bool ToolChain::canClone() const -{ - return true; -} - bool ToolChain::operator == (const ToolChain &tc) const { if (this == &tc) @@ -208,6 +200,22 @@ bool ToolChain::operator == (const ToolChain &tc) const && language() == tc.language(); } +ToolChain *ToolChain::clone() const +{ + for (ToolChainFactory *f : Internal::g_toolChainFactories) { + if (f->supportedToolChainType() == d->m_typeId) { + ToolChain *tc = f->create(); + QTC_ASSERT(tc, return nullptr); + tc->fromMap(toMap()); + // New ID for the clone. It's different. + tc->d->m_id = QUuid::createUuid().toByteArray(); + return tc; + } + } + QTC_CHECK(false); + return nullptr; +} + /*! Used by the tool chain manager to save user-generated tool chains. @@ -236,6 +244,9 @@ QVariantMap ToolChain::toMap() const void ToolChain::toolChainUpdated() { + d->m_predefinedMacrosCache->invalidate(); + d->m_headerPathsCache->invalidate(); + ToolChainManager::notifyAboutUpdate(this); } @@ -243,8 +254,12 @@ void ToolChain::setDetection(ToolChain::Detection de) { if (d->m_detection == de) return; - d->m_detection = de; - toolChainUpdated(); + if (d->m_detection == ToolChain::UninitializedDetection) { + d->m_detection = de; + } else { + d->m_detection = de; + toolChainUpdated(); + } } /*! @@ -265,7 +280,7 @@ bool ToolChain::fromMap(const QVariantMap &data) d->m_id = id.mid(pos + 1).toUtf8(); const bool autoDetect = data.value(QLatin1String(AUTODETECT_KEY), false).toBool(); - d->m_detection = autoDetect ? AutoDetectionFromSettings : ManualDetection; + d->m_detection = autoDetect ? AutoDetection : ManualDetection; if (data.contains(LANGUAGE_KEY_V2)) { // remove hack to trim language id in 4.4: This is to fix up broken language @@ -286,6 +301,16 @@ bool ToolChain::fromMap(const QVariantMap &data) return true; } +const ToolChain::HeaderPathsCache &ToolChain::headerPathsCache() const +{ + return d->m_headerPathsCache; +} + +const ToolChain::MacrosCache &ToolChain::predefinedMacrosCache() const +{ + return d->m_predefinedMacrosCache; +} + static long toLanguageVersionAsLong(QByteArray dateAsByteArray) { dateAsByteArray.chop(1); // Strip 'L'. @@ -357,9 +382,9 @@ Utils::LanguageVersion ToolChain::languageVersion(const Core::Id &language, cons Used by the tool chain kit information to validate the kit. */ -QList<Task> ToolChain::validateKit(const Kit *) const +Tasks ToolChain::validateKit(const Kit *) const { - return QList<Task>(); + return {}; } QString ToolChain::sysRoot() const @@ -390,21 +415,19 @@ QString ToolChain::sysRoot() const Used by the tool chain manager to restore user-generated tool chains. */ -static QList<ToolChainFactory *> g_toolChainFactories; - ToolChainFactory::ToolChainFactory() { - g_toolChainFactories.append(this); + Internal::g_toolChainFactories.append(this); } ToolChainFactory::~ToolChainFactory() { - g_toolChainFactories.removeOne(this); + Internal::g_toolChainFactories.removeOne(this); } const QList<ToolChainFactory *> ToolChainFactory::allToolChainFactories() { - return g_toolChainFactories; + return Internal::g_toolChainFactories; } QList<ToolChain *> ToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown) @@ -413,31 +436,35 @@ QList<ToolChain *> ToolChainFactory::autoDetect(const QList<ToolChain *> &alread return QList<ToolChain *>(); } -QList<ToolChain *> ToolChainFactory::autoDetect(const Utils::FileName &compilerPath, const Core::Id &language) +QList<ToolChain *> ToolChainFactory::autoDetect(const Utils::FilePath &compilerPath, const Core::Id &language) { Q_UNUSED(compilerPath); Q_UNUSED(language); return QList<ToolChain *>(); } -bool ToolChainFactory::canCreate() +bool ToolChainFactory::canCreate() const { - return false; + return m_userCreatable; } -ToolChain *ToolChainFactory::create(Core::Id l) +ToolChain *ToolChainFactory::create() { - Q_UNUSED(l); - return nullptr; + return m_toolchainConstructor ? m_toolchainConstructor() : nullptr; } -bool ToolChainFactory::canRestore(const QVariantMap &) +ToolChain *ToolChainFactory::restore(const QVariantMap &data) { - return false; -} + if (!m_toolchainConstructor) + return nullptr; -ToolChain *ToolChainFactory::restore(const QVariantMap &) -{ + ToolChain *tc = m_toolchainConstructor(); + QTC_ASSERT(tc, return nullptr); + + if (tc->fromMap(data)) + return tc; + + delete tc; return nullptr; } @@ -464,4 +491,40 @@ void ToolChainFactory::autoDetectionToMap(QVariantMap &data, bool detected) data.insert(QLatin1String(AUTODETECT_KEY), detected); } +QSet<Core::Id> ToolChainFactory::supportedLanguages() const +{ + return m_supportsAllLanguages ? ToolChainManager::allLanguages() : m_supportedLanguages; +} + +Core::Id ToolChainFactory::supportedToolChainType() const +{ + return m_supportedToolChainType; +} + +void ToolChainFactory::setSupportedToolChainType(const Core::Id &supportedToolChain) +{ + m_supportedToolChainType = supportedToolChain; +} + +void ToolChainFactory::setSupportedLanguages(const QSet<Core::Id> &supportedLanguages) +{ + m_supportedLanguages = supportedLanguages; +} + +void ToolChainFactory::setSupportsAllLanguages(bool supportsAllLanguages) +{ + m_supportsAllLanguages = supportsAllLanguages; +} + +void ToolChainFactory::setToolchainConstructor + (const std::function<ToolChain *()> &toolchainContructor) +{ + m_toolchainConstructor = toolchainContructor; +} + +void ToolChainFactory::setUserCreatable(bool userCreatable) +{ + m_userCreatable = userCreatable; +} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/toolchain.h b/src/plugins/projectexplorer/toolchain.h index 8925b449a4..1cbe478691 100644 --- a/src/plugins/projectexplorer/toolchain.h +++ b/src/plugins/projectexplorer/toolchain.h @@ -28,13 +28,15 @@ #include "projectexplorer_export.h" #include "projectexplorer_global.h" +#include "abi.h" #include "headerpath.h" #include "projectmacro.h" +#include "task.h" +#include "toolchaincache.h" #include <coreplugin/id.h> #include <utils/cpplanguage_details.h> -#include <utils/fileutils.h> #include <QObject> #include <QSet> @@ -66,7 +68,6 @@ class Abi; class IOutputParser; class ToolChainConfigWidget; class ToolChainFactory; -class Task; class Kit; namespace Internal { class ToolChainSettingsAccessor; } @@ -81,7 +82,8 @@ public: enum Detection { ManualDetection, AutoDetection, - AutoDetectionFromSettings + AutoDetectionFromSdk, + UninitializedDetection, }; using Predicate = std::function<bool(const ToolChain *)>; @@ -96,13 +98,13 @@ public: QByteArray id() const; - virtual Utils::FileNameList suggestedMkspecList() const; - virtual Utils::FileName suggestedDebugger() const; + virtual QStringList suggestedMkspecList() const; + virtual Utils::FilePath suggestedDebugger() const; Core::Id typeId() const; virtual QString typeDisplayName() const = 0; virtual Abi targetAbi() const = 0; - virtual QList<Abi> supportedAbis() const; + virtual ProjectExplorer::Abis supportedAbis() const; virtual QString originalTargetTriple() const { return QString(); } virtual QStringList extraCodeModelFlags() const { return QStringList(); } @@ -119,43 +121,51 @@ public: Utils::LanguageVersion languageVersion; }; + using MacrosCache = std::shared_ptr<Cache<ToolChain::MacroInspectionReport, 64>>; + using HeaderPathsCache = std::shared_ptr<Cache<HeaderPaths>>; + // A MacroInspectionRunner is created in the ui thread and runs in another thread. using MacroInspectionRunner = std::function<MacroInspectionReport(const QStringList &cxxflags)>; virtual MacroInspectionRunner createMacroInspectionRunner() const = 0; virtual Macros predefinedMacros(const QStringList &cxxflags) const = 0; // A BuiltInHeaderPathsRunner is created in the ui thread and runs in another thread. - using BuiltInHeaderPathsRunner = std::function<HeaderPaths(const QStringList &cxxflags, - const QString &sysRoot)>; + using BuiltInHeaderPathsRunner = std::function<HeaderPaths( + const QStringList &cxxflags, const QString &sysRoot, const QString &originalTargetTriple)>; virtual BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner() const = 0; virtual HeaderPaths builtInHeaderPaths(const QStringList &cxxflags, - const Utils::FileName &sysRoot) const = 0; + const Utils::FilePath &sysRoot) const = 0; virtual void addToEnvironment(Utils::Environment &env) const = 0; - virtual QString makeCommand(const Utils::Environment &env) const = 0; + virtual Utils::FilePath makeCommand(const Utils::Environment &env) const = 0; Core::Id language() const; - virtual Utils::FileName compilerCommand() const = 0; + virtual Utils::FilePath compilerCommand() const = 0; virtual IOutputParser *outputParser() const = 0; virtual bool operator ==(const ToolChain &) const; virtual std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() = 0; - virtual bool canClone() const; - virtual ToolChain *clone() const = 0; + ToolChain *clone() const; // Used by the toolchainmanager to save user-generated tool chains. // Make sure to call this function when deriving! virtual QVariantMap toMap() const; - virtual QList<Task> validateKit(const Kit *k) const; + virtual Tasks validateKit(const Kit *k) const; + + virtual bool isJobCountSupported() const { return true; } void setLanguage(Core::Id language); + void setDetection(Detection d); + static Utils::LanguageVersion cxxLanguageVersion(const QByteArray &cplusplusMacroValue); static Utils::LanguageVersion languageVersion(const Core::Id &language, const Macros ¯os); protected: - explicit ToolChain(Core::Id typeId, Detection d); - explicit ToolChain(const ToolChain &); + explicit ToolChain(Core::Id typeId); + + const MacrosCache &predefinedMacrosCache() const; + const HeaderPathsCache &headerPathsCache() const; virtual void toolChainUpdated(); @@ -163,7 +173,8 @@ protected: virtual bool fromMap(const QVariantMap &data); private: - void setDetection(Detection d); + ToolChain(const ToolChain &) = delete; + ToolChain &operator=(const ToolChain &) = delete; const std::unique_ptr<Internal::ToolChainPrivate> d; @@ -182,27 +193,51 @@ public: static const QList<ToolChainFactory *> allToolChainFactories(); QString displayName() const { return m_displayName; } + Core::Id supportedToolChainType() const; virtual QList<ToolChain *> autoDetect(const QList<ToolChain *> &alreadyKnown); - virtual QList<ToolChain *> autoDetect(const Utils::FileName &compilerPath, const Core::Id &language); + virtual QList<ToolChain *> autoDetect(const Utils::FilePath &compilerPath, const Core::Id &language); - virtual bool canCreate(); - virtual ToolChain *create(Core::Id l); + virtual bool canCreate() const; + virtual ToolChain *create(); - virtual bool canRestore(const QVariantMap &data); - virtual ToolChain *restore(const QVariantMap &data); + ToolChain *restore(const QVariantMap &data); static QByteArray idFromMap(const QVariantMap &data); static Core::Id typeIdFromMap(const QVariantMap &data); static void autoDetectionToMap(QVariantMap &data, bool detected); - virtual QSet<Core::Id> supportedLanguages() const = 0; + QSet<Core::Id> supportedLanguages() const; + + void setUserCreatable(bool userCreatable); protected: void setDisplayName(const QString &name) { m_displayName = name; } + void setSupportedToolChainType(const Core::Id &supportedToolChainType); + void setSupportedLanguages(const QSet<Core::Id> &supportedLanguages); + void setSupportsAllLanguages(bool supportsAllLanguages); + void setToolchainConstructor(const std::function<ToolChain *()> &constructor); + + class Candidate { + public: + Utils::FilePath compilerPath; + QString compilerVersion; + + bool operator==(const ToolChainFactory::Candidate &other) const { + return compilerPath == other.compilerPath + && compilerVersion == other.compilerVersion; + } + }; + + using Candidates = QVector<Candidate>; private: QString m_displayName; + Core::Id m_supportedToolChainType; + QSet<Core::Id> m_supportedLanguages; + bool m_supportsAllLanguages = false; + bool m_userCreatable = false; + std::function<ToolChain *()> m_toolchainConstructor; }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/toolchainmanager.cpp b/src/plugins/projectexplorer/toolchainmanager.cpp index caa15e6d2f..ffea568dbe 100644 --- a/src/plugins/projectexplorer/toolchainmanager.cpp +++ b/src/plugins/projectexplorer/toolchainmanager.cpp @@ -63,11 +63,12 @@ class ToolChainManagerPrivate public: ~ToolChainManagerPrivate(); - QMap<QString, FileName> m_abiToDebugger; + QMap<QString, FilePath> m_abiToDebugger; std::unique_ptr<ToolChainSettingsAccessor> m_accessor; QList<ToolChain *> m_toolChains; // prioritized List QVector<LanguageDisplayPair> m_languages; + ToolchainDetectionSettings m_detectionSettings; }; ToolChainManagerPrivate::~ToolChainManagerPrivate() @@ -83,6 +84,8 @@ static ToolChainManagerPrivate *d = nullptr; using namespace Internal; +const char DETECT_X64_AS_X32_KEY[] = "ProjectExplorer/Toolchains/DetectX64AsX32"; + // -------------------------------------------------------------------------- // ToolChainManager // -------------------------------------------------------------------------- @@ -100,6 +103,9 @@ ToolChainManager::ToolChainManager(QObject *parent) : connect(this, &ToolChainManager::toolChainAdded, this, &ToolChainManager::toolChainsChanged); connect(this, &ToolChainManager::toolChainRemoved, this, &ToolChainManager::toolChainsChanged); connect(this, &ToolChainManager::toolChainUpdated, this, &ToolChainManager::toolChainsChanged); + + QSettings * const s = Core::ICore::settings(); + d->m_detectionSettings.detectX64AsX32 = s->value(DETECT_X64_AS_X32_KEY, false).toBool(); } ToolChainManager::~ToolChainManager() @@ -130,6 +136,8 @@ void ToolChainManager::saveToolChains() QTC_ASSERT(d->m_accessor, return); d->m_accessor->saveToolChains(d->m_toolChains, Core::ICore::dialogParent()); + QSettings * const s = Core::ICore::settings(); + s->setValue(DETECT_X64_AS_X32_KEY, d->m_detectionSettings.detectX64AsX32); } QList<ToolChain *> ToolChainManager::toolChains(const ToolChain::Predicate &predicate) @@ -178,7 +186,7 @@ ToolChain *ToolChainManager::findToolChain(const QByteArray &id) return tc; } -FileName ToolChainManager::defaultDebugger(const Abi &abi) +FilePath ToolChainManager::defaultDebugger(const Abi &abi) { return d->m_abiToDebugger.value(abi.toString()); } @@ -257,4 +265,14 @@ void ToolChainManager::aboutToShutdown() #endif } +ToolchainDetectionSettings ToolChainManager::detectionSettings() +{ + return d->m_detectionSettings; +} + +void ToolChainManager::setDetectionSettings(const ToolchainDetectionSettings &settings) +{ + d->m_detectionSettings = settings; +} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/toolchainmanager.h b/src/plugins/projectexplorer/toolchainmanager.h index 2e26431b84..4de51c2b2a 100644 --- a/src/plugins/projectexplorer/toolchainmanager.h +++ b/src/plugins/projectexplorer/toolchainmanager.h @@ -38,13 +38,19 @@ #include <functional> -namespace Utils { class FileName; } +namespace Utils { class FilePath; } namespace ProjectExplorer { class ProjectExplorerPlugin; class Abi; +class ToolchainDetectionSettings +{ +public: + bool detectX64AsX32 = false; +}; + // -------------------------------------------------------------------------- // ToolChainManager // -------------------------------------------------------------------------- @@ -62,7 +68,7 @@ public: static QList<ToolChain *> findToolChains(const Abi &abi); static ToolChain *findToolChain(const QByteArray &id); - static Utils::FileName defaultDebugger(const Abi &abi); + static Utils::FilePath defaultDebugger(const Abi &abi); static bool isLoaded(); @@ -76,6 +82,9 @@ public: static void aboutToShutdown(); + static ToolchainDetectionSettings detectionSettings(); + static void setDetectionSettings(const ToolchainDetectionSettings &settings); + void saveToolChains(); signals: diff --git a/src/plugins/projectexplorer/toolchainoptionspage.cpp b/src/plugins/projectexplorer/toolchainoptionspage.cpp index 2e41c8cd42..a16c6f216e 100644 --- a/src/plugins/projectexplorer/toolchainoptionspage.cpp +++ b/src/plugins/projectexplorer/toolchainoptionspage.cpp @@ -38,15 +38,21 @@ #include <utils/detailswidget.h> #include <utils/qtcassert.h> #include <utils/treemodel.h> +#include <utils/utilsicons.h> #include <QAction> #include <QApplication> +#include <QCheckBox> +#include <QCoreApplication> +#include <QDialog> +#include <QDialogButtonBox> #include <QHBoxLayout> #include <QHeaderView> #include <QItemSelectionModel> #include <QMenu> #include <QMessageBox> #include <QPushButton> +#include <QSet> #include <QSpacerItem> #include <QStackedWidget> #include <QTextStream> @@ -84,17 +90,20 @@ public: if (column == 0) return toolChain->displayName(); return toolChain->typeDisplayName(); - case Qt::FontRole: { QFont font; font.setBold(changed); return font; } - case Qt::ToolTipRole: + if (!toolChain->isValid()) + return ToolChainOptionsPage::tr("This toolchain is no longer valid."); return ToolChainOptionsPage::tr("<nobr><b>ABI:</b> %1").arg( changed ? ToolChainOptionsPage::tr("not up-to-date") : toolChain->targetAbi().toString()); + case Qt::DecorationRole: + return column == 0 && !toolChain->isValid() + ? Utils::Icons::CRITICAL.icon() : QVariant(); } return QVariant(); } @@ -104,6 +113,39 @@ public: bool changed; }; +class DetectionSettingsDialog : public QDialog +{ +public: + DetectionSettingsDialog(const ToolchainDetectionSettings &settings, QWidget *parent) + : QDialog(parent) + { + setWindowTitle(ToolChainOptionsPage::tr("Toolchain Auto-detection Settings")); + const auto layout = new QVBoxLayout(this); + m_detectX64AsX32CheckBox.setText(ToolChainOptionsPage::tr("Detect x86_64 GCC compilers " + "as x86_64 and x86")); + m_detectX64AsX32CheckBox.setToolTip(ToolChainOptionsPage::tr("If checked, Qt Creator will " + "set up two instances of each x86_64 compiler:\nOne for the native x86_64 target, " + "and one for a plain x86 target.\nEnable this if you plan to create 32-bit x86 " + "binaries without using a dedicated cross compiler.")); + m_detectX64AsX32CheckBox.setChecked(settings.detectX64AsX32); + layout->addWidget(&m_detectX64AsX32CheckBox); + const auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + layout->addWidget(buttonBox); + } + + ToolchainDetectionSettings settings() const + { + ToolchainDetectionSettings s; + s.detectX64AsX32 = m_detectX64AsX32CheckBox.isChecked(); + return s; + } + +private: + QCheckBox m_detectX64AsX32CheckBox; +}; + // -------------------------------------------------------------------------- // ToolChainOptionsWidget // -------------------------------------------------------------------------- @@ -113,6 +155,7 @@ class ToolChainOptionsWidget : public QWidget public: ToolChainOptionsWidget() { + m_detectionSettings = ToolChainManager::detectionSettings(); m_factories = Utils::filtered(ToolChainFactory::allToolChainFactories(), [](ToolChainFactory *factory) { return factory->canCreate();}); @@ -169,6 +212,34 @@ public: m_delButton = new QPushButton(ToolChainOptionsPage::tr("Remove"), this); + m_removeAllButton = new QPushButton(ToolChainOptionsPage::tr("Remove All"), this); + connect(m_removeAllButton, &QAbstractButton::clicked, this, + [this] { + QList<ToolChainTreeItem *> itemsToRemove; + m_model.forAllItems([&itemsToRemove](TreeItem *item) { + if (item->level() != 3) + return; + const auto tcItem = static_cast<ToolChainTreeItem *>(item); + if (tcItem->toolChain->detection() != ToolChain::AutoDetectionFromSdk) + itemsToRemove << tcItem; + }); + for (ToolChainTreeItem * const tcItem : qAsConst(itemsToRemove)) + markForRemoval(tcItem); + }); + + m_redetectButton = new QPushButton(ToolChainOptionsPage::tr("Re-detect"), this); + connect(m_redetectButton, &QAbstractButton::clicked, + this, &ToolChainOptionsWidget::redetectToolchains); + + m_detectionSettingsButton = new QPushButton( + ToolChainOptionsPage::tr("Auto-detection Settings..."), this); + connect(m_detectionSettingsButton, &QAbstractButton::clicked, this, + [this] { + DetectionSettingsDialog dlg(m_detectionSettings, this); + if (dlg.exec() == QDialog::Accepted) + m_detectionSettings = dlg.settings(); + }); + m_container = new DetailsWidget(this); m_container->setState(DetailsWidget::NoSummary); m_container->setVisible(false); @@ -185,6 +256,9 @@ public: buttonLayout->addWidget(m_addButton); buttonLayout->addWidget(m_cloneButton); buttonLayout->addWidget(m_delButton); + buttonLayout->addWidget(m_removeAllButton); + buttonLayout->addWidget(m_redetectButton); + buttonLayout->addWidget(m_detectionSettingsButton); buttonLayout->addItem(new QSpacerItem(10, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); auto verticalLayout = new QVBoxLayout; @@ -232,9 +306,11 @@ public: return action; } + void redetectToolchains(); + void apply(); - public: + private: TreeModel<TreeItem, ToolChainTreeItem> m_model; QList<ToolChainFactory *> m_factories; QTreeView *m_toolChainView; @@ -243,11 +319,16 @@ public: QPushButton *m_addButton; QPushButton *m_cloneButton; QPushButton *m_delButton; + QPushButton *m_removeAllButton; + QPushButton *m_redetectButton; + QPushButton *m_detectionSettingsButton; QHash<Core::Id, QPair<StaticTreeItem *, StaticTreeItem *>> m_languageMap; QList<ToolChainTreeItem *> m_toAddList; QList<ToolChainTreeItem *> m_toRemoveList; + + ToolchainDetectionSettings m_detectionSettings; }; void ToolChainOptionsWidget::markForRemoval(ToolChainTreeItem *item) @@ -312,6 +393,50 @@ StaticTreeItem *ToolChainOptionsWidget::parentForToolChain(ToolChain *tc) return tc->isAutoDetected() ? nodes.first : nodes.second; } +void ToolChainOptionsWidget::redetectToolchains() +{ + QList<ToolChainTreeItem *> itemsToRemove; + QList<ToolChain *> knownTcs; + m_model.forAllItems([&itemsToRemove, &knownTcs](TreeItem *item) { + if (item->level() != 3) + return; + const auto tcItem = static_cast<ToolChainTreeItem *>(item); + if (tcItem->toolChain->isAutoDetected() + && tcItem->toolChain->detection() != ToolChain::AutoDetectionFromSdk) { + itemsToRemove << tcItem; + } else { + knownTcs << tcItem->toolChain; + } + }); + QList<ToolChain *> toAdd; + QSet<ToolChain *> toDelete; + for (ToolChainFactory *f : ToolChainFactory::allToolChainFactories()) { + for (ToolChain * const tc : f->autoDetect(knownTcs)) { + if (knownTcs.contains(tc) || toDelete.contains(tc)) + continue; + const auto matchItem = [tc](const ToolChainTreeItem *item) { + return item->toolChain->compilerCommand() == tc->compilerCommand() + && item->toolChain->typeId() == tc->typeId() + && item->toolChain->language() == tc->language() + && item->toolChain->targetAbi() == tc->targetAbi(); + }; + ToolChainTreeItem * const item = findOrDefault(itemsToRemove, matchItem); + if (item) { + itemsToRemove.removeOne(item); + toDelete << tc; + continue; + } + knownTcs << tc; + toAdd << tc; + } + } + for (ToolChainTreeItem * const tcItem : qAsConst(itemsToRemove)) + markForRemoval(tcItem); + for (ToolChain * const newTc : qAsConst(toAdd)) + m_toAddList.append(insertToolChain(newTc, true)); + qDeleteAll(toDelete); +} + void ToolChainOptionsWidget::toolChainSelectionChanged() { ToolChainTreeItem *item = currentTreeItem(); @@ -334,14 +459,16 @@ void ToolChainOptionsWidget::apply() // Update tool chains: foreach (const Core::Id &l, m_languageMap.keys()) { - StaticTreeItem *parent = m_languageMap.value(l).second; - for (TreeItem *item : *parent) { - auto tcItem = static_cast<ToolChainTreeItem *>(item); - Q_ASSERT(tcItem->toolChain); - if (tcItem->widget) - tcItem->widget->apply(); - tcItem->changed = false; - tcItem->update(); + const QPair<StaticTreeItem *, StaticTreeItem *> autoAndManual = m_languageMap.value(l); + for (StaticTreeItem *parent : {autoAndManual.first, autoAndManual.second}) { + for (TreeItem *item : *parent) { + auto tcItem = static_cast<ToolChainTreeItem *>(item); + Q_ASSERT(tcItem->toolChain); + if (!tcItem->toolChain->isAutoDetected() && tcItem->widget) + tcItem->widget->apply(); + tcItem->changed = false; + tcItem->update(); + } } } @@ -374,6 +501,7 @@ void ToolChainOptionsWidget::apply() "They were not configured again.") .arg(removedTcs.join(QLatin1String(",<br> ")))); } + ToolChainManager::setDetectionSettings(m_detectionSettings); } void ToolChainOptionsWidget::createToolChain(ToolChainFactory *factory, const Core::Id &language) @@ -382,10 +510,13 @@ void ToolChainOptionsWidget::createToolChain(ToolChainFactory *factory, const Co QTC_ASSERT(factory->canCreate(), return); QTC_ASSERT(language.isValid(), return); - ToolChain *tc = factory->create(language); + ToolChain *tc = factory->create(); if (!tc) return; + tc->setDetection(ToolChain::ManualDetection); + tc->setLanguage(language); + auto item = insertToolChain(tc, true); m_toAddList.append(item); @@ -397,11 +528,15 @@ void ToolChainOptionsWidget::cloneToolChain() ToolChainTreeItem *current = currentTreeItem(); if (!current) return; - ToolChain *tc = current->toolChain->clone(); + ToolChain *tc = current->toolChain->clone(); if (!tc) return; + tc->setDetection(ToolChain::ManualDetection); + tc->setDisplayName(QCoreApplication::translate("ProjectExplorer::ToolChain", "Clone of %1") + .arg(current->toolChain->displayName())); + auto item = insertToolChain(tc, true); m_toAddList.append(item); @@ -414,8 +549,8 @@ void ToolChainOptionsWidget::updateState() bool canDelete = false; if (ToolChainTreeItem *item = currentTreeItem()) { ToolChain *tc = item->toolChain; - canCopy = tc->isValid() && tc->canClone(); - canDelete = tc->detection() != ToolChain::AutoDetection; + canCopy = tc->isValid(); + canDelete = tc->detection() != ToolChain::AutoDetectionFromSdk; } m_cloneButton->setEnabled(canCopy); diff --git a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp index 80d280a122..d2c347dd37 100644 --- a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp +++ b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp @@ -164,12 +164,9 @@ static ToolChainOperations mergeToolChainLists(const QList<ToolChain *> &systemF const QList<ToolChain *> notRedetectedButValidUserTcs = Utils::filtered(notRedetectedUserTcs, &ToolChain::isValid); - const QList<ToolChain *> validManualUserTcs - = Utils::filtered(manualUserFileTcs, &ToolChain::isValid); - ToolChainOperations result; result.toDemote = notRedetectedButValidUserTcs; - result.toRegister = stabilizeOrder(systemFileTcs + validManualUserTcs + result.toDemote // manual TCs + result.toRegister = stabilizeOrder(systemFileTcs + manualUserFileTcs + result.toDemote // manual TCs + redetectedUserTcs + newlyAutodetectedTcs, // auto TCs userFileTcs); @@ -187,7 +184,7 @@ ToolChainSettingsAccessor::ToolChainSettingsAccessor() : QCoreApplication::translate("ProjectExplorer::ToolChainManager", "Tool Chains"), Core::Constants::IDE_DISPLAY_NAME) { - setBaseFilePath(FileName::fromString(Core::ICore::userResourcePath() + TOOLCHAIN_FILENAME)); + setBaseFilePath(FilePath::fromString(Core::ICore::userResourcePath() + TOOLCHAIN_FILENAME)); addVersionUpgrader(std::make_unique<ToolChainSettingsUpgraderV0>()); } @@ -196,8 +193,10 @@ QList<ToolChain *> ToolChainSettingsAccessor::restoreToolChains(QWidget *parent) { // read all tool chains from SDK const QList<ToolChain *> systemFileTcs - = toolChains(restoreSettings(FileName::fromString(Core::ICore::installerResourcePath() + TOOLCHAIN_FILENAME), + = toolChains(restoreSettings(FilePath::fromString(Core::ICore::installerResourcePath() + TOOLCHAIN_FILENAME), parent)); + for (ToolChain * const systemTc : systemFileTcs) + systemTc->setDetection(ToolChain::AutoDetectionFromSdk); // read all tool chains from user file. const QList<ToolChain *> userFileTcs = toolChains(restoreSettings(parent)); @@ -225,7 +224,7 @@ void ToolChainSettingsAccessor::saveToolChains(const QList<ToolChain *> &toolcha int count = 0; for (const ToolChain *tc : toolchains) { - if (!tc || !tc->isValid()) + if (!tc || (!tc->isValid() && tc->isAutoDetected())) continue; const QVariantMap tmp = tc->toMap(); if (tmp.isEmpty()) @@ -254,18 +253,21 @@ QList<ToolChain *> ToolChainSettingsAccessor::toolChains(const QVariantMap &data const QVariantMap tcMap = data.value(key).toMap(); bool restored = false; - for (ToolChainFactory *f : factories) { - if (f->canRestore(tcMap)) { - if (ToolChain *tc = f->restore(tcMap)) { - result.append(tc); - restored = true; - break; + const Core::Id tcType = ToolChainFactory::typeIdFromMap(tcMap); + if (tcType.isValid()) { + for (ToolChainFactory *f : factories) { + if (f->supportedToolChainType() == tcType) { + if (ToolChain *tc = f->restore(tcMap)) { + result.append(tc); + restored = true; + break; + } } } } if (!restored) qWarning("Warning: Unable to restore compiler type '%s' for tool chain %s.", - qPrintable(ToolChainFactory::typeIdFromMap(tcMap).toString()), + qPrintable(tcType.toString()), qPrintable(QString::fromUtf8(ToolChainFactory::idFromMap(tcMap)))); } @@ -290,11 +292,17 @@ namespace ProjectExplorer { using TCList = QList<ToolChain *>; +const char TestTokenKey[] = "TestTokenKey"; +const char TestToolChainType[] = "TestToolChainType"; + + class TTC : public ToolChain { public: - TTC(ToolChain::Detection d, const QByteArray &t, bool v = true) : - ToolChain("TestToolChainType", d), + TTC() : ToolChain(TestToolChainType) {} + + TTC(const QByteArray &t, bool v = true) : + ToolChain(TestToolChainType), token(t), m_valid(v) { @@ -313,28 +321,36 @@ public: LanguageExtensions languageExtensions(const QStringList &cxxflags) const override { Q_UNUSED(cxxflags); return LanguageExtension::None; } WarningFlags warningFlags(const QStringList &cflags) const override { Q_UNUSED(cflags); return WarningFlags::NoWarnings; } BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner() const override { return BuiltInHeaderPathsRunner(); } - HeaderPaths builtInHeaderPaths(const QStringList &cxxflags, const FileName &sysRoot) const override + HeaderPaths builtInHeaderPaths(const QStringList &cxxflags, const FilePath &sysRoot) const override { Q_UNUSED(cxxflags); Q_UNUSED(sysRoot); return {}; } void addToEnvironment(Environment &env) const override { Q_UNUSED(env); } - QString makeCommand(const Environment &env) const override { Q_UNUSED(env); return QString("make"); } - FileName compilerCommand() const override { return Utils::FileName::fromString("/tmp/test/gcc"); } + FilePath makeCommand(const Environment &) const override { return FilePath::fromString("make"); } + FilePath compilerCommand() const override { return Utils::FilePath::fromString("/tmp/test/gcc"); } IOutputParser *outputParser() const override { return nullptr; } std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override { return nullptr; } - TTC *clone() const override { return new TTC(*this); } bool operator ==(const ToolChain &other) const override { if (!ToolChain::operator==(other)) return false; return static_cast<const TTC *>(&other)->token == token; } + bool fromMap(const QVariantMap &data) final + { + ToolChain::fromMap(data); + token = data.value(TestTokenKey).toByteArray(); + return true; + } + + QVariantMap toMap() const final + { + QVariantMap data = ToolChain::toMap(); + data[TestTokenKey] = token; + return data; + } + QByteArray token; private: - TTC(const TTC &other) : - ToolChain(other.typeId(), other.detection()), - token(other.token) - {} - bool m_valid = false; static QList<TTC *> m_toolChains; @@ -352,6 +368,17 @@ namespace ProjectExplorer { void ProjectExplorerPlugin::testToolChainMerging_data() { + class TestToolChainFactory : ToolChainFactory + { + public: + TestToolChainFactory() { + setSupportedToolChainType(TestToolChainType); + setToolchainConstructor([] { return new TTC; }); + } + }; + + TestToolChainFactory factory; + QTest::addColumn<TCList>("system"); QTest::addColumn<TCList>("user"); QTest::addColumn<TCList>("autodetect"); @@ -359,33 +386,43 @@ void ProjectExplorerPlugin::testToolChainMerging_data() QTest::addColumn<TCList>("toRegister"); TTC *system1 = nullptr; - TTC *system1c = nullptr; + ToolChain *system1c = nullptr; TTC *system2 = nullptr; TTC *system3i = nullptr; TTC *user1 = nullptr; - TTC *user1c = nullptr; + ToolChain *user1c = nullptr; TTC *user3i = nullptr; TTC *user2 = nullptr; TTC *auto1 = nullptr; - TTC *auto1c = nullptr; + ToolChain *auto1c = nullptr; TTC *auto1_2 = nullptr; TTC *auto2 = nullptr; TTC *auto3i = nullptr; if (!TTC::hasToolChains()) { - system1 = new TTC(ToolChain::AutoDetection, "system1"); Q_UNUSED(system1); - system1c = system1->clone(); Q_UNUSED(system1c); - system2 = new TTC(ToolChain::AutoDetection, "system2"); Q_UNUSED(system2); - system3i = new TTC(ToolChain::AutoDetection, "system3", false); Q_UNUSED(system3i); - user1 = new TTC(ToolChain::ManualDetection, "user1"); Q_UNUSED(user1); - user1c = user1->clone(); Q_UNUSED(user1c); - user2 = new TTC(ToolChain::ManualDetection, "user2"); Q_UNUSED(user2); - user3i = new TTC(ToolChain::ManualDetection, "user3", false); Q_UNUSED(user3i); - auto1 = new TTC(ToolChain::AutoDetectionFromSettings, "auto1"); Q_UNUSED(auto1); - auto1c = auto1->clone(); Q_UNUSED(auto1c); - auto1_2 = new TTC(ToolChain::AutoDetectionFromSettings, "auto1"); Q_UNUSED(auto1_2); - auto2 = new TTC(ToolChain::AutoDetectionFromSettings, "auto2"); Q_UNUSED(auto2); - auto3i = new TTC(ToolChain::AutoDetectionFromSettings, "auto3", false); Q_UNUSED(auto3i); + system1 = new TTC("system1"); + system1->setDetection(ToolChain::AutoDetection); + system1c = system1->clone(); Q_UNUSED(system1c) + system2 = new TTC("system2"); + system2->setDetection(ToolChain::AutoDetection); + system3i = new TTC("system3", false); + system3i->setDetection(ToolChain::AutoDetection); + user1 = new TTC("user1"); + user1->setDetection(ToolChain::ManualDetection); + user1c = user1->clone(); Q_UNUSED(user1c) + user2 = new TTC("user2"); + user2->setDetection(ToolChain::ManualDetection); + user3i = new TTC("user3", false); + user3i->setDetection(ToolChain::ManualDetection); + auto1 = new TTC("auto1"); + auto1->setDetection(ToolChain::AutoDetection); + auto1c = auto1->clone(); + auto1_2 = new TTC("auto1"); + auto1_2->setDetection(ToolChain::AutoDetection); + auto2 = new TTC("auto2"); + auto2->setDetection(ToolChain::AutoDetection); + auto3i = new TTC("auto3", false); + auto3i->setDetection(ToolChain::AutoDetection); } QTest::newRow("no toolchains") @@ -424,9 +461,9 @@ void ProjectExplorerPlugin::testToolChainMerging_data() << (TCList()) << (TCList() << auto3i) << (TCList()) << (TCList()) << (TCList()); - QTest::newRow("Delete invalid user") + QTest::newRow("invalid user") << (TCList()) << (TCList() << user3i) << (TCList()) - << (TCList()) << (TCList()); + << (TCList()) << (TCList{user3i}); QTest::newRow("one of everything") << (TCList() << system1) << (TCList() << user1) << (TCList() << auto1) diff --git a/src/plugins/projectexplorer/treescanner.cpp b/src/plugins/projectexplorer/treescanner.cpp new file mode 100644 index 0000000000..7c7f634d1a --- /dev/null +++ b/src/plugins/projectexplorer/treescanner.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Alexander Drozdov. +** Contact: Alexander Drozdov (adrozdoff@gmail.com) +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "treescanner.h" +#include "projectexplorerconstants.h" + +#include <coreplugin/iversioncontrol.h> +#include <coreplugin/vcsmanager.h> + +#include <cpptools/cpptoolsconstants.h> + +#include <utils/qtcassert.h> +#include <utils/algorithm.h> +#include <utils/runextensions.h> + +#include <memory> + +namespace ProjectExplorer { + +TreeScanner::TreeScanner(QObject *parent) : QObject(parent) +{ + m_factory = TreeScanner::genericFileType; + m_filter = [](const Utils::MimeType &mimeType, const Utils::FilePath &fn) { + return isWellKnownBinary(mimeType, fn) && isMimeBinary(mimeType, fn); + }; + + connect(&m_futureWatcher, &FutureWatcher::finished, this, &TreeScanner::finished); +} + +TreeScanner::~TreeScanner() +{ + if (!m_futureWatcher.isFinished()) { + m_futureWatcher.cancel(); + m_futureWatcher.waitForFinished(); + } +} + +bool TreeScanner::asyncScanForFiles(const Utils::FilePath &directory) +{ + if (!m_futureWatcher.isFinished()) + return false; + + auto fi = new FutureInterface(); + m_scanFuture = fi->future(); + m_futureWatcher.setFuture(m_scanFuture); + + Utils::runAsync([this, fi, directory]() { TreeScanner::scanForFiles(fi, directory, m_filter, m_factory); }); + + return true; +} + +void TreeScanner::setFilter(TreeScanner::FileFilter filter) +{ + if (isFinished()) + m_filter = filter; +} + +void TreeScanner::setTypeFactory(TreeScanner::FileTypeFactory factory) +{ + if (isFinished()) + m_factory = factory; +} + +TreeScanner::Future TreeScanner::future() const +{ + return m_scanFuture; +} + +bool TreeScanner::isFinished() const +{ + return m_futureWatcher.isFinished(); +} + +TreeScanner::Result TreeScanner::result() const +{ + if (isFinished()) + return m_scanFuture.result(); + return Result(); +} + +TreeScanner::Result TreeScanner::release() +{ + if (isFinished()) { + auto result = m_scanFuture.result(); + m_scanFuture = Future(); + return result; + } + return Result(); +} + +void TreeScanner::reset() +{ + if (isFinished()) + m_scanFuture = Future(); +} + +bool TreeScanner::isWellKnownBinary(const Utils::MimeType & /*mdb*/, const Utils::FilePath &fn) +{ + return fn.endsWith(QLatin1String(".a")) || + fn.endsWith(QLatin1String(".o")) || + fn.endsWith(QLatin1String(".d")) || + fn.endsWith(QLatin1String(".exe")) || + fn.endsWith(QLatin1String(".dll")) || + fn.endsWith(QLatin1String(".obj")) || + fn.endsWith(QLatin1String(".elf")); +} + +bool TreeScanner::isMimeBinary(const Utils::MimeType &mimeType, const Utils::FilePath &/*fn*/) +{ + bool isBinary = false; + if (mimeType.isValid()) { + QStringList mimes; + mimes << mimeType.name() << mimeType.allAncestors(); + isBinary = !mimes.contains(QLatin1String("text/plain")); + } + return isBinary; +} + +FileType TreeScanner::genericFileType(const Utils::MimeType &mimeType, const Utils::FilePath &/*fn*/) +{ + return Node::fileTypeForMimeType(mimeType); +} + +void TreeScanner::scanForFiles(FutureInterface *fi, const Utils::FilePath& directory, + const FileFilter &filter, const FileTypeFactory &factory) +{ + std::unique_ptr<FutureInterface> fip(fi); + fip->reportStarted(); + + Result nodes = FileNode::scanForFiles( + directory, + [&filter, &factory](const Utils::FilePath &fn) -> FileNode * { + const Utils::MimeType mimeType = Utils::mimeTypeForFile(fn.toString()); + + // Skip some files during scan. + if (filter && filter(mimeType, fn)) + return nullptr; + + // Type detection + FileType type = FileType::Unknown; + if (factory) + type = factory(mimeType, fn); + + return new FileNode(fn, type); + }, fip.get()); + + Utils::sort(nodes, ProjectExplorer::Node::sortByPath); + + fip->setProgressValue(fip->progressMaximum()); + fip->reportResult(nodes); + fip->reportFinished(); +} + +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/treescanner.h b/src/plugins/projectexplorer/treescanner.h new file mode 100644 index 0000000000..e2ad544bde --- /dev/null +++ b/src/plugins/projectexplorer/treescanner.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Alexander Drozdov. +** Contact: Alexander Drozdov (adrozdoff@gmail.com) +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "projectexplorer_export.h" +#include "projectnodes.h" + +#include <utils/mimetypes/mimedatabase.h> +#include <utils/fileutils.h> + +#include <QObject> +#include <QFuture> +#include <QFutureWatcher> + +#include <functional> + +namespace Core { class IVersionControl; } + +namespace ProjectExplorer { + +class PROJECTEXPLORER_EXPORT TreeScanner : public QObject +{ + Q_OBJECT + +public: + using Result = QList<ProjectExplorer::FileNode *>; + using Future = QFuture<Result>; + using FutureWatcher = QFutureWatcher<Result>; + using FutureInterface = QFutureInterface<Result>; + + using FileFilter = std::function<bool(const Utils::MimeType &, const Utils::FilePath &)>; + using FileTypeFactory = std::function<ProjectExplorer::FileType(const Utils::MimeType &, const Utils::FilePath &)>; + + explicit TreeScanner(QObject *parent = nullptr); + ~TreeScanner() override; + + // Start scanning in given directory + bool asyncScanForFiles(const Utils::FilePath& directory); + + // Setup filter for ignored files + void setFilter(FileFilter filter); + + // Setup factory for file types + void setTypeFactory(FileTypeFactory factory); + + Future future() const; + bool isFinished() const; + + // Takes not-owning result + Result result() const; + // Takes owning of result + Result release(); + // Clear scan results + void reset(); + + // Standard filters helpers + static bool isWellKnownBinary(const Utils::MimeType &mimeType, const Utils::FilePath &fn); + static bool isMimeBinary(const Utils::MimeType &mimeType, const Utils::FilePath &fn); + + // Standard file factory + static ProjectExplorer::FileType genericFileType(const Utils::MimeType &mdb, const Utils::FilePath& fn); + +signals: + void finished(); + +private: + static void scanForFiles(FutureInterface *fi, const Utils::FilePath &directory, + const FileFilter &filter, const FileTypeFactory &factory); + +private: + FileFilter m_filter; + FileTypeFactory m_factory; + + FutureWatcher m_futureWatcher; + Future m_scanFuture; +}; + +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/userfileaccessor.cpp b/src/plugins/projectexplorer/userfileaccessor.cpp index 49a0ccdf7b..838507e544 100644 --- a/src/plugins/projectexplorer/userfileaccessor.cpp +++ b/src/plugins/projectexplorer/userfileaccessor.cpp @@ -52,6 +52,16 @@ const char OBSOLETE_VERSION_KEY[] = "ProjectExplorer.Project.Updater.FileVersion const char SHARED_SETTINGS[] = "SharedSettings"; const char USER_STICKY_KEYS_KEY[] = "UserStickyKeys"; +#ifdef PROJECT_USER_FILE_EXTENSION +#define STRINGIFY_INTERNAL(x) #x +#define STRINGIFY(x) STRINGIFY_INTERNAL(x) + +const char FILE_EXTENSION_STR[] = STRINGIFY(PROJECT_USER_FILE_EXTENSION); +#else +const char FILE_EXTENSION_STR[] = ".user"; + +#endif + // Version 14 Move builddir into BuildConfiguration class UserFileVersion14Upgrader : public VersionUpgrader { @@ -230,19 +240,18 @@ static QString makeRelative(QString path) } // Return complete file path of the .user file. -static FileName externalUserFilePath(const Utils::FileName &projectFilePath, const QString &suffix) +static FilePath externalUserFilePath(const Utils::FilePath &projectFilePath, const QString &suffix) { - FileName result; static const optional<QString> externalUserFileDir = defineExternalUserFileDir(); if (externalUserFileDir) { // Recreate the relative project file hierarchy under the shared directory. // PersistentSettingsWriter::write() takes care of creating the path. - result = FileName::fromString(externalUserFileDir.value()); - result.appendString('/' + makeRelative(projectFilePath.toString())); - result.appendString(suffix); + return FilePath::fromString(externalUserFileDir.value() + + '/' + makeRelative(projectFilePath.toString()) + + suffix); } - return result; + return {}; } } // namespace @@ -257,18 +266,18 @@ public: UserFileBackUpStrategy(UserFileAccessor *accessor) : Utils::VersionedBackUpStrategy(accessor) { } - FileNameList readFileCandidates(const Utils::FileName &baseFileName) const final; + FilePathList readFileCandidates(const Utils::FilePath &baseFileName) const final; }; -FileNameList UserFileBackUpStrategy::readFileCandidates(const FileName &baseFileName) const +FilePathList UserFileBackUpStrategy::readFileCandidates(const FilePath &baseFileName) const { const auto *const ac = static_cast<const UserFileAccessor *>(accessor()); - const FileName externalUser = ac->externalUserFile(); - const FileName projectUser = ac->projectUserFile(); + const FilePath externalUser = ac->externalUserFile(); + const FilePath projectUser = ac->projectUserFile(); QTC_CHECK(!baseFileName.isEmpty()); QTC_CHECK(baseFileName == externalUser || baseFileName == projectUser); - FileNameList result = Utils::VersionedBackUpStrategy::readFileCandidates(projectUser); + FilePathList result = Utils::VersionedBackUpStrategy::readFileCandidates(projectUser); if (!externalUser.isEmpty()) result.append(Utils::VersionedBackUpStrategy::readFileCandidates(externalUser)); @@ -286,8 +295,8 @@ UserFileAccessor::UserFileAccessor(Project *project) : m_project(project) { // Setup: - const FileName externalUser = externalUserFile(); - const FileName projectUser = projectUserFile(); + const FilePath externalUser = externalUserFile(); + const FilePath projectUser = projectUserFile(); setBaseFilePath(externalUser.isEmpty() ? projectUser : externalUser); auto secondary @@ -375,27 +384,25 @@ QVariant UserFileAccessor::retrieveSharedSettings() const return project()->property(SHARED_SETTINGS); } -FileName UserFileAccessor::projectUserFile() const +FilePath UserFileAccessor::projectUserFile() const { static const QString qtcExt = QLatin1String(qgetenv("QTC_EXTENSION")); - FileName projectUserFile = m_project->projectFilePath(); - projectUserFile.appendString(generateSuffix(qtcExt.isEmpty() ? ".user" : qtcExt)); - return projectUserFile; + return m_project->projectFilePath() + .stringAppended(generateSuffix(qtcExt.isEmpty() ? FILE_EXTENSION_STR : qtcExt)); } -FileName UserFileAccessor::externalUserFile() const +FilePath UserFileAccessor::externalUserFile() const { static const QString qtcExt = QFile::decodeName(qgetenv("QTC_EXTENSION")); return externalUserFilePath(m_project->projectFilePath(), - generateSuffix(qtcExt.isEmpty() ? ".user" : qtcExt)); + generateSuffix(qtcExt.isEmpty() ? FILE_EXTENSION_STR : qtcExt)); } -FileName UserFileAccessor::sharedFile() const +FilePath UserFileAccessor::sharedFile() const { static const QString qtcExt = QLatin1String(qgetenv("QTC_SHARED_EXTENSION")); - FileName sharedFile = m_project->projectFilePath(); - sharedFile.appendString(generateSuffix(qtcExt.isEmpty() ? ".shared" : qtcExt)); - return sharedFile; + return m_project->projectFilePath() + .stringAppended(generateSuffix(qtcExt.isEmpty() ? ".shared" : qtcExt)); } QVariantMap UserFileAccessor::postprocessMerge(const QVariantMap &main, @@ -877,7 +884,7 @@ private: class TestProject : public Project { public: - TestProject() : Project("x-test/testproject", Utils::FileName::fromString("/test/project")) { + TestProject() : Project("x-test/testproject", Utils::FilePath::fromString("/test/project")) { setDisplayName("Test Project"); } @@ -974,7 +981,7 @@ void ProjectExplorerPlugin::testUserFileAccessor_mergeSettings() sharedData.insert("shared1", "bar"); sharedData.insert("shared2", "baz"); sharedData.insert("shared3", "foooo"); - TestUserFileAccessor::RestoreData shared(FileName::fromString("/shared/data"), sharedData); + TestUserFileAccessor::RestoreData shared(FilePath::fromString("/shared/data"), sharedData); QVariantMap data; data.insert("Version", accessor.currentVersion()); @@ -983,7 +990,7 @@ void ProjectExplorerPlugin::testUserFileAccessor_mergeSettings() data.insert("shared1", "bar1"); data.insert("unique1", 1234); data.insert("shared3", "foo"); - TestUserFileAccessor::RestoreData user(FileName::fromString("/user/data"), data); + TestUserFileAccessor::RestoreData user(FilePath::fromString("/user/data"), data); TestUserFileAccessor::RestoreData result = accessor.mergeSettings(user, shared); QVERIFY(!result.hasIssue()); @@ -1009,10 +1016,10 @@ void ProjectExplorerPlugin::testUserFileAccessor_mergeSettingsEmptyUser() sharedData.insert("shared1", "bar"); sharedData.insert("shared2", "baz"); sharedData.insert("shared3", "foooo"); - TestUserFileAccessor::RestoreData shared(FileName::fromString("/shared/data"), sharedData); + TestUserFileAccessor::RestoreData shared(FilePath::fromString("/shared/data"), sharedData); QVariantMap data; - TestUserFileAccessor::RestoreData user(FileName::fromString("/shared/data"), data); + TestUserFileAccessor::RestoreData user(FilePath::fromString("/shared/data"), data); TestUserFileAccessor::RestoreData result = accessor.mergeSettings(user, shared); @@ -1026,7 +1033,7 @@ void ProjectExplorerPlugin::testUserFileAccessor_mergeSettingsEmptyShared() TestUserFileAccessor accessor(&project); QVariantMap sharedData; - TestUserFileAccessor::RestoreData shared(FileName::fromString("/shared/data"), sharedData); + TestUserFileAccessor::RestoreData shared(FilePath::fromString("/shared/data"), sharedData); QVariantMap data; data.insert("Version", accessor.currentVersion()); @@ -1036,7 +1043,7 @@ void ProjectExplorerPlugin::testUserFileAccessor_mergeSettingsEmptyShared() data.insert("shared1", "bar1"); data.insert("unique1", 1234); data.insert("shared3", "foo"); - TestUserFileAccessor::RestoreData user(FileName::fromString("/shared/data"), data); + TestUserFileAccessor::RestoreData user(FilePath::fromString("/shared/data"), data); TestUserFileAccessor::RestoreData result = accessor.mergeSettings(user, shared); diff --git a/src/plugins/projectexplorer/userfileaccessor.h b/src/plugins/projectexplorer/userfileaccessor.h index 6544de987e..6b7d42ec1a 100644 --- a/src/plugins/projectexplorer/userfileaccessor.h +++ b/src/plugins/projectexplorer/userfileaccessor.h @@ -47,9 +47,9 @@ public: virtual QVariant retrieveSharedSettings() const; - Utils::FileName projectUserFile() const; - Utils::FileName externalUserFile() const; - Utils::FileName sharedFile() const; + Utils::FilePath projectUserFile() const; + Utils::FilePath externalUserFile() const; + Utils::FilePath sharedFile() const; protected: QVariantMap postprocessMerge(const QVariantMap &main, diff --git a/src/plugins/projectexplorer/waitforstopdialog.h b/src/plugins/projectexplorer/waitforstopdialog.h index d8c0037712..47c960dba8 100644 --- a/src/plugins/projectexplorer/waitforstopdialog.h +++ b/src/plugins/projectexplorer/waitforstopdialog.h @@ -29,7 +29,7 @@ #include <QDialog> #include <QTime> -#include "runconfiguration.h" +#include "runcontrol.h" QT_BEGIN_NAMESPACE class QLabel; diff --git a/src/plugins/projectexplorer/xcodebuildparser.cpp b/src/plugins/projectexplorer/xcodebuildparser.cpp index a96d4bb097..ae551e259c 100644 --- a/src/plugins/projectexplorer/xcodebuildparser.cpp +++ b/src/plugins/projectexplorer/xcodebuildparser.cpp @@ -73,7 +73,7 @@ void XcodebuildParser::stdOutput(const QString &line) Task task(Task::Warning, QCoreApplication::translate("ProjectExplorer::XcodebuildParser", "Replacing signature"), - Utils::FileName::fromString( + Utils::FilePath::fromString( lne.left(lne.size() - QLatin1String(signatureChangeEndsWithPattern).size())), /* filename */ -1, /* line */ Constants::TASK_CATEGORY_COMPILE); @@ -96,7 +96,7 @@ void XcodebuildParser::stdError(const QString &line) Task task(Task::Error, QCoreApplication::translate("ProjectExplorer::XcodebuildParser", "Xcodebuild failed."), - Utils::FileName(), /* filename */ + Utils::FilePath(), /* filename */ -1, /* line */ Constants::TASK_CATEGORY_COMPILE); taskAdded(task); @@ -139,7 +139,7 @@ void ProjectExplorerPlugin::testXcodebuildParserParsing_data() QTest::addColumn<OutputParserTester::Channel>("inputChannel"); QTest::addColumn<QString>("childStdOutLines"); QTest::addColumn<QString>("childStdErrLines"); - QTest::addColumn<QList<Task> >("tasks"); + QTest::addColumn<Tasks >("tasks"); QTest::addColumn<QString>("outputLines"); QTest::addColumn<ProjectExplorer::XcodebuildParser::XcodebuildStatus>("finalStatus"); @@ -147,42 +147,42 @@ void ProjectExplorerPlugin::testXcodebuildParserParsing_data() << XcodebuildParser::OutsideXcodebuild << QString::fromLatin1("Sometext") << OutputParserTester::STDOUT << QString::fromLatin1("Sometext\n") << QString() - << QList<Task>() + << Tasks() << QString() << XcodebuildParser::OutsideXcodebuild; QTest::newRow("outside pass-through stderr") << XcodebuildParser::OutsideXcodebuild << QString::fromLatin1("Sometext") << OutputParserTester::STDERR << QString() << QString::fromLatin1("Sometext\n") - << QList<Task>() + << Tasks() << QString() << XcodebuildParser::OutsideXcodebuild; QTest::newRow("inside pass stdout to stderr") << XcodebuildParser::InXcodebuild << QString::fromLatin1("Sometext") << OutputParserTester::STDOUT << QString() << QString::fromLatin1("Sometext\n") - << QList<Task>() + << Tasks() << QString() << XcodebuildParser::InXcodebuild; QTest::newRow("inside ignore stderr") << XcodebuildParser::InXcodebuild << QString::fromLatin1("Sometext") << OutputParserTester::STDERR << QString() << QString() - << QList<Task>() + << Tasks() << QString() << XcodebuildParser::InXcodebuild; QTest::newRow("unknown pass stdout to stderr") << XcodebuildParser::UnknownXcodebuildState << QString::fromLatin1("Sometext") << OutputParserTester::STDOUT << QString() << QString::fromLatin1("Sometext\n") - << QList<Task>() + << Tasks() << QString() << XcodebuildParser::UnknownXcodebuildState; QTest::newRow("unknown ignore stderr (change?)") << XcodebuildParser::UnknownXcodebuildState << QString::fromLatin1("Sometext") << OutputParserTester::STDERR << QString() << QString() - << QList<Task>() + << Tasks() << QString() << XcodebuildParser::UnknownXcodebuildState; QTest::newRow("switch outside->in->outside") @@ -196,7 +196,7 @@ void ProjectExplorerPlugin::testXcodebuildParserParsing_data() "outside2") << OutputParserTester::STDOUT << QString::fromLatin1("outside\noutside2\n") << QString::fromLatin1("in xcodebuild\nin xcodebuild2\n") - << QList<Task>() + << Tasks() << QString() << XcodebuildParser::OutsideXcodebuild; QTest::newRow("switch Unknown->in->outside") @@ -208,7 +208,7 @@ void ProjectExplorerPlugin::testXcodebuildParserParsing_data() "outside") << OutputParserTester::STDOUT << QString::fromLatin1("outside\n") << QString::fromLatin1("unknown\nin xcodebuild\n") - << QList<Task>() + << Tasks() << QString() << XcodebuildParser::OutsideXcodebuild; QTest::newRow("switch in->unknown") @@ -218,12 +218,12 @@ void ProjectExplorerPlugin::testXcodebuildParserParsing_data() "unknownErr") << OutputParserTester::STDERR << QString() << QString() - << (QList<Task>() + << (Tasks() << Task( Task::Error, QCoreApplication::translate("ProjectExplorer::XcodebuildParser", "Xcodebuild failed."), - Utils::FileName(), /* filename */ + Utils::FilePath(), /* filename */ -1, /* line */ Constants::TASK_CATEGORY_COMPILE)) << QString() @@ -235,12 +235,12 @@ void ProjectExplorerPlugin::testXcodebuildParserParsing_data() "unknownErr") << OutputParserTester::STDERR << QString() << QString::fromLatin1("outErr\n") - << (QList<Task>() + << (Tasks() << Task( Task::Error, QCoreApplication::translate("ProjectExplorer::XcodebuildParser", "Xcodebuild failed."), - Utils::FileName(), /* filename */ + Utils::FilePath(), /* filename */ -1, /* line */ Constants::TASK_CATEGORY_COMPILE)) << QString() @@ -249,11 +249,11 @@ void ProjectExplorerPlugin::testXcodebuildParserParsing_data() << XcodebuildParser::InXcodebuild << QString::fromLatin1("/somepath/somefile.app: replacing existing signature") << OutputParserTester::STDOUT << QString() << QString() - << (QList<Task>() + << (Tasks() << Task(Task::Warning, QCoreApplication::translate("ProjectExplorer::XcodebuildParser", "Replacing signature"), - Utils::FileName::fromString(QLatin1String("/somepath/somefile.app")), /* filename */ + Utils::FilePath::fromString(QLatin1String("/somepath/somefile.app")), /* filename */ -1, /* line */ Constants::TASK_CATEGORY_COMPILE)) << QString() @@ -262,7 +262,7 @@ void ProjectExplorerPlugin::testXcodebuildParserParsing_data() << XcodebuildParser::OutsideXcodebuild << QString::fromLatin1("/somepath/somefile.app: replacing existing signature") << OutputParserTester::STDOUT << QString::fromLatin1("/somepath/somefile.app: replacing existing signature\n") << QString() - << QList<Task>() + << Tasks() << QString() << XcodebuildParser::OutsideXcodebuild; } @@ -282,7 +282,7 @@ void ProjectExplorerPlugin::testXcodebuildParserParsing() QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(QString, childStdOutLines); QFETCH(QString, childStdErrLines); - QFETCH(QList<Task>, tasks); + QFETCH(Tasks, tasks); QFETCH(QString, outputLines); QFETCH(ProjectExplorer::XcodebuildParser::XcodebuildStatus, finalStatus); |