diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/builddirmanager.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/builddirmanager.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/builddirmanager.cpp 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/builddirmanager.cpp 2017-02-26 23:55:32.000000000 +0000 @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -55,6 +54,7 @@ #include #include #include +#include #include #include @@ -62,7 +62,6 @@ #include #include #include -#include using namespace ProjectExplorer; @@ -91,10 +90,9 @@ if (bdir.exists()) return bdir; if (!m_tempDir) { - const QString path = QDir::tempPath() + QLatin1String("/qtc-cmake-XXXXXX"); - m_tempDir.reset(new QTemporaryDir(path)); + m_tempDir.reset(new Utils::TemporaryDirectory("qtc-cmake-XXXXXXXX")); if (!m_tempDir->isValid()) - emit errorOccured(tr("Failed to create temporary directory using template \"%1\".").arg(path)); + emit errorOccured(tr("Failed to create temporary directory \"%1\".").arg(m_tempDir->path())); } return Utils::FileName::fromString(m_tempDir->path()); } @@ -158,7 +156,7 @@ const QByteArray CMAKE_CXX_COMPILER_KEY = "CMAKE_CXX_COMPILER"; const QByteArrayList criticalKeys - = { GENERATOR_KEY, CMAKE_COMMAND_KEY, CMAKE_C_COMPILER_KEY, CMAKE_CXX_COMPILER_KEY }; + = {GENERATOR_KEY, CMAKE_COMMAND_KEY, CMAKE_C_COMPILER_KEY, CMAKE_CXX_COMPILER_KEY}; const CMakeConfig currentConfig = parsedConfiguration(); @@ -282,19 +280,18 @@ const Utils::FileName projectFile = m_buildConfiguration->target()->project()->projectFilePath(); + root->makeEmpty(); m_reader->generateProjectTree(root, allFiles); // Make sure the top level CMakeLists.txt is always visible: - if (root->fileNodes().isEmpty() - && root->folderNodes().isEmpty() - && root->projectNodes().isEmpty()) - root->addFileNodes({ new FileNode(projectFile, FileType::Project, false) }); + if (root->isEmpty()) + root->addNode(new FileNode(projectFile, FileType::Project, false)); } -QSet BuildDirManager::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) +void BuildDirManager::updateCodeModel(CppTools::RawProjectParts &rpps) { - QTC_ASSERT(m_reader, return QSet()); - return m_reader->updateCodeModel(ppBuilder); + QTC_ASSERT(m_reader, return); + return m_reader->updateCodeModel(rpps); } void BuildDirManager::parse() @@ -332,9 +329,26 @@ return m_cmakeCache; if (m_cmakeCache.isEmpty()) m_cmakeCache = m_reader->takeParsedConfiguration(); + + for (auto &ci : m_cmakeCache) + ci.inCMakeCache = true; + return m_cmakeCache; } +CMakeConfig BuildDirManager::parseConfiguration(const Utils::FileName &cacheFile, QString *errorMessage) +{ + if (!cacheFile.exists()) { + if (errorMessage) + *errorMessage = tr("CMakeCache.txt file not found."); + return { }; + } + CMakeConfig result = CMakeConfigItem::itemsFromFile(cacheFile, errorMessage); + if (!errorMessage->isEmpty()) + return { }; + return result; +} + void BuildDirManager::checkConfiguration() { if (m_tempDir) // always throw away changes in the tmpdir! diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/builddirmanager.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/builddirmanager.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/builddirmanager.h 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/builddirmanager.h 2017-02-26 23:55:32.000000000 +0000 @@ -29,16 +29,14 @@ #include "cmakeconfigitem.h" #include +#include #include -#include #include #include #include -namespace CppTools { class ProjectPartBuilder; } - namespace ProjectExplorer { class FileNode; class IOutputParser; @@ -73,26 +71,25 @@ void generateProjectTree(CMakeListsNode *root, const QList &allFiles); - QSet updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder); + void updateCodeModel(CppTools::RawProjectParts &rpps); QList buildTargets() const; CMakeConfig parsedConfiguration() const; + static CMakeConfig parseConfiguration(const Utils::FileName &cacheFile, + QString *errorMessage); + signals: void configurationStarted() const; void dataAvailable() const; void errorOccured(const QString &err) const; -protected: - static CMakeConfig parseConfiguration(const Utils::FileName &cacheFile, - QString *errorMessage); - - const Utils::FileName workDirectory() const; - private: void emitDataAvailable(); void checkConfiguration(); + const Utils::FileName workDirectory() const; + void updateReaderType(std::function todo); void updateReaderData(); @@ -104,7 +101,7 @@ void becameDirty(); CMakeBuildConfiguration *m_buildConfiguration = nullptr; - mutable std::unique_ptr m_tempDir = nullptr; + mutable std::unique_ptr m_tempDir = nullptr; mutable CMakeConfig m_cmakeCache; QTimer m_reparseTimer; diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/builddirreader.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/builddirreader.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/builddirreader.cpp 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/builddirreader.cpp 2017-02-26 23:55:32.000000000 +0000 @@ -32,6 +32,7 @@ #include "simpleservermorereader.h" #include +#include #include using namespace ProjectExplorer; @@ -64,10 +65,10 @@ pathMapper = cmake->pathMapper(); isAutorun = cmake->isAutoRun(); - auto tc = ProjectExplorer::ToolChainKitInformation::toolChain(k, ProjectExplorer::ToolChain::Language::Cxx); + auto tc = ProjectExplorer::ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::CXX_LANGUAGE_ID); if (tc) cxxToolChainId = tc->id(); - tc = ProjectExplorer::ToolChainKitInformation::toolChain(k, ProjectExplorer::ToolChain::Language::C); + tc = ProjectExplorer::ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::C_LANGUAGE_ID); if (tc) cToolChainId = tc->id(); sysRoot = ProjectExplorer::SysRootKitInformation::sysRoot(k); diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/builddirreader.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/builddirreader.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/builddirreader.h 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/builddirreader.h 2017-02-26 23:55:32.000000000 +0000 @@ -29,6 +29,8 @@ #include "cmakeproject.h" #include "cmaketool.h" +#include + #include #include #include @@ -36,15 +38,6 @@ #include #include -namespace CppTools { class ProjectPartBuilder; } - -namespace ProjectExplorer { -class IOutputParser; -class Kit; -} // ProjectExplorer - -namespace Utils { class QtcProcess; } - namespace CMakeProjectManager { namespace Internal { @@ -104,7 +97,7 @@ virtual QList buildTargets() const = 0; virtual void generateProjectTree(CMakeListsNode *root, const QList &allFiles) = 0; - virtual QSet updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) = 0; + virtual void updateCodeModel(CppTools::RawProjectParts &rpps) = 0; signals: void isReadyNow() const; diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakebuildconfiguration.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakebuildconfiguration.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakebuildconfiguration.cpp 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakebuildconfiguration.cpp 2017-02-26 23:55:32.000000000 +0000 @@ -33,6 +33,7 @@ #include "cmakeprojectconstants.h" #include "cmakebuildsettingswidget.h" #include "cmakeprojectmanager.h" +#include "cmakeprojectnodes.h" #include #include @@ -219,9 +220,9 @@ m_buildDirManager->generateProjectTree(root, allFiles); } -QSet CMakeBuildConfiguration::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) +void CMakeBuildConfiguration::updateCodeModel(CppTools::RawProjectParts &rpps) { - return m_buildDirManager->updateCodeModel(ppBuilder); + m_buildDirManager->updateCodeModel(rpps); } FileName CMakeBuildConfiguration::shadowBuildDirectory(const FileName &projectFilePath, @@ -273,6 +274,7 @@ j.value = QString::fromUtf8(i.value); j.description = QString::fromUtf8(i.documentation); j.values = i.values; + j.inCMakeCache = i.inCMakeCache; j.isAdvanced = i.isAdvanced || i.type == CMakeConfigItem::INTERNAL; switch (i.type) { @@ -308,6 +310,7 @@ ni.value = i.value.toUtf8(); ni.documentation = i.description.toUtf8(); ni.isAdvanced = i.isAdvanced; + ni.inCMakeCache = i.inCMakeCache; ni.values = i.values; switch (i.type) { case CMakeProjectManager::ConfigModel::DataItem::BOOLEAN: @@ -380,7 +383,7 @@ CMakeConfig CMakeBuildConfiguration::cmakeConfiguration() const { - return removeDuplicates(m_configuration + CMakeConfigurationKitInformation::configuration(target()->kit())); + return removeDuplicates(CMakeConfigurationKitInformation::configuration(target()->kit()) + m_configuration); } void CMakeBuildConfiguration::setError(const QString &message) @@ -423,6 +426,33 @@ ProjectExplorer::IBuildConfigurationFactory(parent) { } +CMakeBuildConfigurationFactory::BuildType CMakeBuildConfigurationFactory::buildTypeFromByteArray(const QByteArray &in) +{ + const QByteArray bt = in.toLower(); + if (bt == "debug") + return BuildTypeDebug; + if (bt == "release") + return BuildTypeRelease; + if (bt == "relwithdebinfo") + return BuildTypeRelWithDebInfo; + if (bt == "minsizerel") + return BuildTypeMinSizeRel; + return BuildTypeNone; +} + +BuildConfiguration::BuildType CMakeBuildConfigurationFactory::cmakeBuildTypeToBuildType(const CMakeBuildConfigurationFactory::BuildType &in) +{ + // Cover all common CMake build types + if (in == BuildTypeRelease || in == BuildTypeMinSizeRel) + return BuildConfiguration::Release; + else if (in == BuildTypeDebug) + return BuildConfiguration::Debug; + else if (in == BuildTypeRelWithDebInfo) + return BuildConfiguration::Profile; + else + return BuildConfiguration::Unknown; +} + int CMakeBuildConfigurationFactory::priority(const ProjectExplorer::Target *parent) const { return canHandle(parent) ? 0 : -1; @@ -568,22 +598,22 @@ info->typeName = tr("Build"); break; case BuildTypeDebug: - buildTypeItem = { CMakeConfigItem("CMAKE_BUILD_TYPE", "Debug") }; + buildTypeItem = {CMakeConfigItem("CMAKE_BUILD_TYPE", "Debug")}; info->typeName = tr("Debug"); info->buildType = BuildConfiguration::Debug; break; case BuildTypeRelease: - buildTypeItem = { CMakeConfigItem("CMAKE_BUILD_TYPE", "Release") }; + buildTypeItem = {CMakeConfigItem("CMAKE_BUILD_TYPE", "Release")}; info->typeName = tr("Release"); info->buildType = BuildConfiguration::Release; break; case BuildTypeMinSizeRel: - buildTypeItem = { CMakeConfigItem("CMAKE_BUILD_TYPE", "MinSizeRel") }; + buildTypeItem = {CMakeConfigItem("CMAKE_BUILD_TYPE", "MinSizeRel")}; info->typeName = tr("Minimum Size Release"); info->buildType = BuildConfiguration::Release; break; case BuildTypeRelWithDebInfo: - buildTypeItem = { CMakeConfigItem("CMAKE_BUILD_TYPE", "RelWithDebInfo") }; + buildTypeItem = {CMakeConfigItem("CMAKE_BUILD_TYPE", "RelWithDebInfo")}; info->typeName = tr("Release with Debug Information"); info->buildType = BuildConfiguration::Profile; break; @@ -600,14 +630,14 @@ ProjectExplorer::BuildConfiguration::BuildType CMakeBuildConfiguration::buildType() const { - QString cmakeBuildType; + QByteArray cmakeBuildTypeName; QFile cmakeCache(buildDirectory().toString() + QLatin1String("/CMakeCache.txt")); if (cmakeCache.open(QIODevice::ReadOnly)) { while (!cmakeCache.atEnd()) { QByteArray line = cmakeCache.readLine(); if (line.startsWith("CMAKE_BUILD_TYPE")) { if (int pos = line.indexOf('=')) - cmakeBuildType = QString::fromLocal8Bit(line.mid(pos + 1).trimmed()); + cmakeBuildTypeName = line.mid(pos + 1).trimmed(); break; } } @@ -615,17 +645,9 @@ } // Cover all common CMake build types - if (cmakeBuildType.compare(QLatin1String("Release"), Qt::CaseInsensitive) == 0 - || cmakeBuildType.compare(QLatin1String("MinSizeRel"), Qt::CaseInsensitive) == 0) { - return Release; - } else if (cmakeBuildType.compare(QLatin1String("Debug"), Qt::CaseInsensitive) == 0 - || cmakeBuildType.compare(QLatin1String("DebugFull"), Qt::CaseInsensitive) == 0) { - return Debug; - } else if (cmakeBuildType.compare(QLatin1String("RelWithDebInfo"), Qt::CaseInsensitive) == 0) { - return Profile; - } - - return Unknown; + const CMakeBuildConfigurationFactory::BuildType cmakeBuildType + = CMakeBuildConfigurationFactory::buildTypeFromByteArray(cmakeBuildTypeName); + return CMakeBuildConfigurationFactory::cmakeBuildTypeToBuildType(cmakeBuildType); } } // namespace Internal diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakebuildconfiguration.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakebuildconfiguration.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakebuildconfiguration.h 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakebuildconfiguration.h 2017-02-26 23:55:32.000000000 +0000 @@ -29,12 +29,13 @@ #include "cmakeproject.h" #include "configmodel.h" +#include + #include #include #include -namespace CppTools { class ProjectPartBuilder; } namespace ProjectExplorer { class ToolChain; } namespace CMakeProjectManager { @@ -86,7 +87,7 @@ QList buildTargets() const; void generateProjectTree(CMakeListsNode *root, const QList &allFiles) const; - QSet updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder); + void updateCodeModel(CppTools::RawProjectParts &rpps); static Utils::FileName shadowBuildDirectory(const Utils::FileName &projectFilePath, const ProjectExplorer::Kit *k, @@ -124,6 +125,8 @@ friend class CMakeProjectManager::CMakeProject; }; +class CMakeProjectImporter; + class CMakeBuildConfigurationFactory : public ProjectExplorer::IBuildConfigurationFactory { Q_OBJECT @@ -131,6 +134,15 @@ public: CMakeBuildConfigurationFactory(QObject *parent = 0); + enum BuildType { BuildTypeNone = 0, + BuildTypeDebug = 1, + BuildTypeRelease = 2, + BuildTypeRelWithDebInfo = 3, + BuildTypeMinSizeRel = 4, + BuildTypeLast = 5 }; + static BuildType buildTypeFromByteArray(const QByteArray &in); + static ProjectExplorer::BuildConfiguration::BuildType cmakeBuildTypeToBuildType(const BuildType &in); + int priority(const ProjectExplorer::Target *parent) const override; QList availableBuilds(const ProjectExplorer::Target *parent) const override; int priority(const ProjectExplorer::Kit *k, const QString &projectPath) const override; @@ -147,16 +159,11 @@ private: bool canHandle(const ProjectExplorer::Target *t) const; - enum BuildType { BuildTypeNone = 0, - BuildTypeDebug = 1, - BuildTypeRelease = 2, - BuildTypeRelWithDebInfo = 3, - BuildTypeMinSizeRel = 4, - BuildTypeLast = 5 }; - CMakeBuildInfo *createBuildInfo(const ProjectExplorer::Kit *k, const QString &sourceDir, BuildType buildType) const; + + friend class CMakeProjectImporter; }; } // namespace Internal diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakebuildsettingswidget.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakebuildsettingswidget.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakebuildsettingswidget.cpp 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakebuildsettingswidget.cpp 2017-02-26 23:55:32.000000000 +0000 @@ -27,11 +27,13 @@ #include "configmodel.h" #include "configmodelitemdelegate.h" +#include "cmakekitinformation.h" #include "cmakeproject.h" #include "cmakebuildconfiguration.h" #include #include +#include #include #include @@ -252,6 +254,10 @@ connect(bc, &CMakeBuildConfiguration::errorOccured, this, &CMakeBuildSettingsWidget::setError); connect(bc, &CMakeBuildConfiguration::warningOccured, this, &CMakeBuildSettingsWidget::setWarning); + + updateFromKit(); + connect(m_buildConfiguration->target(), &ProjectExplorer::Target::kitChanged, + this, &CMakeBuildSettingsWidget::updateFromKit); } void CMakeBuildSettingsWidget::setError(const QString &message) @@ -263,11 +269,9 @@ m_errorMessageLabel->setText(message); m_errorMessageLabel->setToolTip(message); - m_configView->setVisible(!showError); - m_editButton->setVisible(!showError); - m_resetButton->setVisible(!showError); - m_showAdvancedCheckBox->setVisible(!showError); - m_reconfigureButton->setVisible(!showError); + m_editButton->setEnabled(!showError); + m_resetButton->setEnabled(!showError); + m_showAdvancedCheckBox->setEnabled(!showError); } void CMakeBuildSettingsWidget::setWarning(const QString &message) @@ -294,5 +298,17 @@ m_configFilterModel->setFilterRole(m_showAdvancedCheckBox->isChecked() ? Qt::EditRole : Qt::DisplayRole); } +void CMakeBuildSettingsWidget::updateFromKit() +{ + const ProjectExplorer::Kit *k = m_buildConfiguration->target()->kit(); + const CMakeConfig config = CMakeConfigurationKitInformation::configuration(k); + + QHash configHash; + for (const CMakeConfigItem &i : config) + configHash.insert(QString::fromUtf8(i.key), i.expandedValue(k)); + + m_configModel->setKitConfiguration(configHash); +} + } // namespace Internal } // namespace CMakeProjectManager diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakebuildsettingswidget.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakebuildsettingswidget.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakebuildsettingswidget.h 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakebuildsettingswidget.h 2017-02-26 23:55:32.000000000 +0000 @@ -60,6 +60,7 @@ private: void updateButtonState(); void updateAdvancedCheckBox(); + void updateFromKit(); CMakeBuildConfiguration *m_buildConfiguration; QTreeView *m_configView; diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakebuildstep.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakebuildstep.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakebuildstep.cpp 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakebuildstep.cpp 2017-02-26 23:55:32.000000000 +0000 @@ -237,10 +237,10 @@ bool mustDelay = false; if (bc->persistCMakeState()) { - emit addOutput(tr("Persisting CMake state..."), BuildStep::MessageOutput); + emit addOutput(tr("Persisting CMake state..."), BuildStep::OutputFormat::NormalMessage); mustDelay = true; } else if (bc->updateCMakeStateBeforeBuild()) { - emit addOutput(tr("Running CMake in preparation to build..."), BuildStep::MessageOutput); + emit addOutput(tr("Running CMake in preparation to build..."), BuildStep::OutputFormat::NormalMessage); mustDelay = true; } else { mustDelay = false; @@ -526,8 +526,8 @@ if (parent->target()->project()->id() != Constants::CMAKEPROJECT_ID) return {}; - return {{ Constants::CMAKE_BUILD_STEP_ID, - tr("Build", "Display name for CMakeProjectManager::CMakeBuildStep id.") }}; + return {{Constants::CMAKE_BUILD_STEP_ID, + tr("Build", "Display name for CMakeProjectManager::CMakeBuildStep id.")}}; } BuildStep *CMakeBuildStepFactory::create(BuildStepList *parent, Core::Id id) diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakecbpparser.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakecbpparser.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakecbpparser.cpp 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakecbpparser.cpp 2017-02-26 23:55:32.000000000 +0000 @@ -47,7 +47,7 @@ namespace { int distance(const FileName &targetDirectory, const FileName &fileName) { - const QString commonParent = commonPath(QStringList({ targetDirectory.toString(), fileName.toString() })); + const QString commonParent = commonPath(QStringList({targetDirectory.toString(), fileName.toString()})); return targetDirectory.toString().mid(commonParent.size()).count('/') + fileName.toString().mid(commonParent.size()).count('/'); } diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeconfigitem.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeconfigitem.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeconfigitem.cpp 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeconfigitem.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -45,7 +45,7 @@ CMakeConfigItem::CMakeConfigItem(const CMakeConfigItem &other) : key(other.key), type(other.type), isAdvanced(other.isAdvanced), value(other.value), documentation(other.documentation), values(other.values) -{ } +{} CMakeConfigItem::CMakeConfigItem(const QByteArray &k, Type t, const QByteArray &d, const QByteArray &v) : @@ -372,34 +372,34 @@ << "" << true << QStringList(); QTest::newRow("single path") - << "C:/something" << false << QStringList({ "C:/something" }); + << "C:/something" << false << QStringList({"C:/something"}); QTest::newRow("single path, keep empty") - << "C:/something" << true << QStringList({ "C:/something" }); + << "C:/something" << true << QStringList({"C:/something"}); QTest::newRow(";single path") - << ";C:/something" << false << QStringList({ "C:/something" }); + << ";C:/something" << false << QStringList({"C:/something"}); QTest::newRow(";single path, keep empty") - << ";C:/something" << true << QStringList({ "", "C:/something" }); + << ";C:/something" << true << QStringList({"", "C:/something"}); QTest::newRow("single path;") - << "C:/something;" << false << QStringList({ "C:/something" }); + << "C:/something;" << false << QStringList({"C:/something"}); QTest::newRow("single path;, keep empty") - << "C:/something;" << true << QStringList({ "C:/something", "" }); + << "C:/something;" << true << QStringList({"C:/something", ""}); QTest::newRow("single path\\;") - << "C:/something\\;" << false << QStringList({ "C:/something;" }); + << "C:/something\\;" << false << QStringList({"C:/something;"}); QTest::newRow("single path\\;, keep empty") - << "C:/something\\;" << true << QStringList({ "C:/something;" }); + << "C:/something\\;" << true << QStringList({"C:/something;"}); QTest::newRow("single path\\;;second path") - << "C:/something\\;;/second/path" << false << QStringList({ "C:/something;", "/second/path" }); + << "C:/something\\;;/second/path" << false << QStringList({"C:/something;", "/second/path"}); QTest::newRow("single path\\;;second path, keep empty") - << "C:/something\\;;/second/path" << true << QStringList({ "C:/something;", "/second/path" }); + << "C:/something\\;;/second/path" << true << QStringList({"C:/something;", "/second/path"}); QTest::newRow("single path;;second path") - << "C:/something;;/second/path" << false << QStringList({ "C:/something", "/second/path" }); + << "C:/something;;/second/path" << false << QStringList({"C:/something", "/second/path"}); QTest::newRow("single path;;second path, keep empty") - << "C:/something;;/second/path" << true << QStringList({ "C:/something", "", "/second/path" }); + << "C:/something;;/second/path" << true << QStringList({"C:/something", "", "/second/path"}); } void CMakeProjectPlugin::testCMakeSplitValue() diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeconfigitem.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeconfigitem.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeconfigitem.h 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeconfigitem.h 2017-02-26 23:55:33.000000000 +0000 @@ -67,6 +67,7 @@ QByteArray key; Type type = STRING; bool isAdvanced = false; + bool inCMakeCache = false; QByteArray value; // converted to string as needed QByteArray documentation; QStringList values; diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakekitinformation.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakekitinformation.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakekitinformation.cpp 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakekitinformation.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -76,6 +76,11 @@ [this]() { foreach (Kit *k, KitManager::kits()) fix(k); }); } +Core::Id CMakeKitInformation::id() +{ + return TOOL_ID; +} + CMakeTool *CMakeKitInformation::cmakeTool(const Kit *k) { if (!k) @@ -262,7 +267,7 @@ const QString &generator, const QString &extraGenerator, const QString &platform, const QString &toolset) { - GeneratorInfo info = { generator, extraGenerator, platform, toolset }; + GeneratorInfo info = {generator, extraGenerator, platform, toolset}; setGeneratorInfo(k, info); } @@ -306,12 +311,12 @@ k->addToEnvironment(env); const Utils::FileName ninjaExec = env.searchInPath(QLatin1String("ninja")); if (!ninjaExec.isEmpty()) - return GeneratorInfo({ QString("Ninja"), extraGenerator, QString(), QString() }).toVariant(); + return GeneratorInfo({QString("Ninja"), extraGenerator, QString(), QString()}).toVariant(); } if (Utils::HostOsInfo::isWindowsHost()) { // *sigh* Windows with its zoo of incompatible stuff again... - ToolChain *tc = ToolChainKitInformation::toolChain(k, ToolChain::Language::Cxx); + ToolChain *tc = ToolChainKitInformation::toolChain(k, Constants::CXX_LANGUAGE_ID); if (tc && tc->typeId() == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID) { it = std::find_if(known.constBegin(), known.constEnd(), [extraGenerator](const CMakeTool::Generator &g) { @@ -336,7 +341,7 @@ if (it == known.constEnd()) return QVariant(); - return GeneratorInfo({ it->name, extraGenerator, QString(), QString() }).toVariant(); + return GeneratorInfo({it->name, extraGenerator, QString(), QString()}).toVariant(); } QList CMakeGeneratorKitInformation::validate(const Kit *k) const @@ -402,9 +407,9 @@ dv.fromVariant(defaultValue(k)); setGeneratorInfo(k, dv); } else { - const GeneratorInfo dv = { info.generator, info.extraGenerator, - it->supportsPlatform ? info.platform : QString(), - it->supportsToolset ? info.toolset : QString() }; + const GeneratorInfo dv = {info.generator, info.extraGenerator, + it->supportsPlatform ? info.platform : QString(), + it->supportsToolset ? info.toolset : QString()}; setGeneratorInfo(k, dv); } } @@ -531,8 +536,8 @@ QList CMakeConfigurationKitInformation::validate(const Kit *k) const { const QtSupport::BaseQtVersion *const version = QtSupport::QtKitInformation::qtVersion(k); - const ToolChain *const tcC = ToolChainKitInformation::toolChain(k, ToolChain::Language::C); - const ToolChain *const tcCxx = ToolChainKitInformation::toolChain(k, ToolChain::Language::Cxx); + const ToolChain *const tcC = ToolChainKitInformation::toolChain(k, Constants::C_LANGUAGE_ID); + const ToolChain *const tcCxx = ToolChainKitInformation::toolChain(k, Constants::CXX_LANGUAGE_ID); const CMakeConfig config = configuration(k); const bool isQt4 = version && version->qtVersion() < QtSupport::QtVersionNumber(5, 0, 0); diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakekitinformation.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakekitinformation.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakekitinformation.h 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakekitinformation.h 2017-02-26 23:55:33.000000000 +0000 @@ -41,6 +41,8 @@ public: CMakeKitInformation(); + static Core::Id id(); + static CMakeTool *cmakeTool(const ProjectExplorer::Kit *k); static void setCMakeTool(ProjectExplorer::Kit *k, const Core::Id id); diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeparser.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeparser.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeparser.cpp 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeparser.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -30,7 +30,6 @@ #include using namespace CMakeProjectManager; -using namespace Internal; using namespace ProjectExplorer; const char COMMON_ERROR_PATTERN[] = "^CMake Error at (.*):([0-9]*) \\((.*)\\):"; @@ -141,7 +140,7 @@ #include -void CMakeProjectPlugin::testCMakeParser_data() +void Internal::CMakeProjectPlugin::testCMakeParser_data() { QTest::addColumn("input"); QTest::addColumn("inputChannel"); @@ -265,7 +264,7 @@ << QString(); } -void CMakeProjectPlugin::testCMakeParser() +void Internal::CMakeProjectPlugin::testCMakeParser() { OutputParserTester testbench; testbench.appendOutputParser(new CMakeParser); diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeparser.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeparser.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeparser.h 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeparser.h 2017-02-26 23:55:33.000000000 +0000 @@ -25,15 +25,16 @@ #pragma once +#include "cmake_global.h" + #include #include #include namespace CMakeProjectManager { -namespace Internal { -class CMakeParser : public ProjectExplorer::IOutputParser +class CMAKE_EXPORT CMakeParser : public ProjectExplorer::IOutputParser { Q_OBJECT @@ -58,4 +59,3 @@ }; } // namespace CMakeProjectManager -} // namespace Internal diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeproject.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeproject.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeproject.cpp 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeproject.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -35,11 +35,11 @@ #include #include -#include +#include +#include #include #include #include -#include #include #include #include @@ -77,6 +77,7 @@ \class CMakeProject */ CMakeProject::CMakeProject(CMakeManager *manager, const FileName &fileName) + : m_cppCodeModelUpdater(new CppTools::CppProjectUpdater(this)) { setId(CMakeProjectManager::Constants::CMAKEPROJECT_ID); setProjectManager(manager); @@ -85,7 +86,7 @@ setRootProjectNode(new CMakeListsNode(fileName, this)); setProjectContext(Core::Context(CMakeProjectManager::Constants::PROJECTCONTEXT)); - setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX)); + setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); rootProjectNode()->setDisplayName(fileName.parentDir().fileName()); @@ -131,8 +132,13 @@ CMakeProject::~CMakeProject() { + if (!m_treeScanner.isFinished()) { + auto future = m_treeScanner.future(); + future.cancel(); + future.waitForFinished(); + } + delete m_cppCodeModelUpdater; setRootProjectNode(nullptr); - m_codeModelFuture.cancel(); qDeleteAll(m_extraCompilers); qDeleteAll(m_allFiles); } @@ -157,16 +163,12 @@ createGeneratedCodeModelSupport(); - ToolChain *tc = ToolChainKitInformation::toolChain(k, ToolChain::Language::Cxx); + ToolChain *tc = ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::CXX_LANGUAGE_ID); if (!tc) { emit fileListChanged(); return; } - CppTools::CppModelManager *modelmanager = CppTools::CppModelManager::instance(); - CppTools::ProjectInfo pinfo(this); - CppTools::ProjectPartBuilder ppBuilder(pinfo); - CppTools::ProjectPart::QtVersion activeQtVersion = CppTools::ProjectPart::NoQt; if (QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(k)) { if (qtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0)) @@ -175,14 +177,17 @@ activeQtVersion = CppTools::ProjectPart::Qt5; } - ppBuilder.setQtVersion(activeQtVersion); + CppTools::RawProjectParts rpps; + bc->updateCodeModel(rpps); - const QSet languages = bc->updateCodeModel(ppBuilder); - for (const auto &lid : languages) - setProjectLanguage(lid, true); + for (CppTools::RawProjectPart &rpp : rpps) { + // TODO: Set the Qt version only if target actually depends on Qt. + rpp.setQtVersion(activeQtVersion); + // TODO: Support also C + rpp.setFlagsForCxx({tc, rpp.flagsForCxx.commandLineFlags}); + } - m_codeModelFuture.cancel(); - m_codeModelFuture = modelmanager->updateProjectInfo(pinfo); + m_cppCodeModelUpdater->update({this, nullptr, tc, k, rpps}); updateQmlJSCodeModel(); @@ -226,22 +231,6 @@ modelManager->updateProjectInfo(projectInfo, this); } -void CMakeProject::askRunCMake() -{ - QPointer box = new QMessageBox(Core::ICore::mainWindow()); - box->setIcon(QMessageBox::Question); - box->setText(tr("Project file system tree changed.")); - box->setInformativeText(tr("File system tree changed. The change does not affect CMake project targets.\n\n" - "Edit appropriate CMakeLists.txt or run CMake now if project uses globbing expressions to collect files.")); - box->addButton(tr("Run CMake Later"), QMessageBox::RejectRole); - auto defaultButton = box->addButton(tr("Run CMake Now"), QMessageBox::AcceptRole); - box->setDefaultButton(defaultButton); - - int ret = box->exec(); - if (ret == QMessageBox::Accepted) - runCMake(); -} - bool CMakeProject::needsConfiguration() const { return targets().isEmpty(); @@ -286,6 +275,13 @@ bc->buildTarget(buildTarget); } +ProjectImporter *CMakeProject::projectImporter() const +{ + if (!m_projectImporter) + m_projectImporter = std::make_unique(projectFilePath()); + return m_projectImporter.get(); +} + bool CMakeProject::addFiles(const QStringList &filePaths) { Utils::MimeDatabase mdb; @@ -296,13 +292,15 @@ auto type = TreeScanner::genericFileType(mimeType, fn); auto parent = fn.parentDir(); - auto folder = rootProjectNode()->recursiveFindOrCreateFolderNode(parent.toString(), projectDirectory()); + auto folder = rootProjectNode()->recursiveFindOrCreateFolderNode(parent, projectDirectory()); if (!folder->fileNode(fn)) { auto node = new FileNode(fn, type, false); node->setEnabled(false); nodes << node; - folder->addFileNodes({new FileNode(*node)}); + auto added = new FileNode(node->filePath(), node->fileType(), node->isGenerated()); + added->setEnabled(false); + folder->addNode(added); } } @@ -319,10 +317,6 @@ m_allFiles.end(), Node::sortByPath); - QTC_ASSERT(isSorted(m_allFiles, Node::sortByPath), return false); - - askRunCMake(); - return true; } @@ -339,9 +333,9 @@ return false; // To update list - removed << new FileNode(*node); - - folder->removeFileNodes({node}); + removed << new FileNode(node->filePath(), node->fileType(), node->isGenerated()); + folder->removeNode(node); + delete node; } // Update tree without full rescan run @@ -370,10 +364,6 @@ } } - QTC_ASSERT(isSorted(m_allFiles, Node::sortByPath), return false); - - askRunCMake(); - return true; } @@ -408,25 +398,24 @@ Node::sortByPath); m_allFiles.insert(it, toAdd); - - QTC_ASSERT(isSorted(m_allFiles, Node::sortByPath), return false); } - auto folder = rootProjectNode()->recursiveFindOrCreateFolderNode(newfn.parentDir().toString(), projectDirectory()); + auto folder = rootProjectNode()->recursiveFindOrCreateFolderNode(newfn.parentDir(), projectDirectory()); if (!folder) return false; if (folder != node->parentFolderNode()) { // Rename with moving - folder->addFileNodes({ new FileNode(newfn, node->fileType(), false) }); + auto added = new FileNode(newfn, node->fileType(), false); + added->setEnabled(node->isEnabled()); + folder->addNode(added); auto old = node->parentFolderNode(); - old->removeFileNodes({node}); + old->removeNode(node); + delete node; } else { node->setAbsoluteFilePathAndLine(newfn, -1); } - askRunCMake(); - return true; } @@ -620,8 +609,8 @@ } else if (fi.suffix() == "scxml") { generatedFilePath += "/"; generatedFilePath += QDir::cleanPath(fi.completeBaseName()); - return QStringList({ generatedFilePath + ".h", - generatedFilePath + ".cpp" }); + return QStringList({generatedFilePath + ".h", + generatedFilePath + ".cpp"}); } else { // TODO: Other types will be added when adapters for their compilers become available. return QStringList(); diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeproject.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeproject.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeproject.h 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeproject.h 2017-02-26 23:55:33.000000000 +0000 @@ -26,6 +26,7 @@ #pragma once #include "cmake_global.h" +#include "cmakeprojectimporter.h" #include "treescanner.h" #include @@ -36,10 +37,14 @@ #include #include +#include + QT_BEGIN_NAMESPACE class QFileSystemWatcher; QT_END_NAMESPACE +namespace CppTools { class CppProjectUpdater; } + namespace CMakeProjectManager { namespace Internal { @@ -102,6 +107,8 @@ // Context menu actions: void buildCMakeTarget(const QString &buildTarget); + ProjectExplorer::ProjectImporter *projectImporter() const final; + bool addFiles(const QStringList &filePaths); bool eraseFiles(const QStringList &filePaths); bool renameFile(const QString &filePath, const QString &newFilePath); @@ -124,8 +131,6 @@ void updateProjectData(Internal::CMakeBuildConfiguration *cmakeBc); void updateQmlJSCodeModel(); - void askRunCMake(); - void createGeneratedCodeModelSupport(); QStringList filesGeneratedFrom(const QString &sourceFile) const final; void updateTargetRunConfigurations(ProjectExplorer::Target *t); @@ -135,12 +140,13 @@ // TODO probably need a CMake specific node structure QList m_buildTargets; - QFuture m_codeModelFuture; + CppTools::CppProjectUpdater *m_cppCodeModelUpdater = nullptr; QList m_extraCompilers; Internal::TreeScanner m_treeScanner; QHash m_mimeBinaryCache; QList m_allFiles; + mutable std::unique_ptr m_projectImporter; friend class Internal::CMakeBuildConfiguration; friend class Internal::CMakeBuildSettingsWidget; diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectimporter.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectimporter.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectimporter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectimporter.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -0,0 +1,533 @@ +/**************************************************************************** +** +** 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 "cmakeprojectimporter.h" + +#include "builddirmanager.h" +#include "cmakebuildconfiguration.h" +#include "cmakebuildinfo.h" +#include "cmakekitinformation.h" +#include "cmaketoolmanager.h" + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace ProjectExplorer; +using namespace QtSupport; + +namespace { + +Q_LOGGING_CATEGORY(cmInputLog, "qtc.cmake.import"); + +struct CMakeToolChainData +{ + QByteArray languageId; + Utils::FileName compilerPath; + Core::Id mapLanguageIdToQtC() const + { + const QByteArray li = languageId.toLower(); + if (li == "cxx") + return ProjectExplorer::Constants::CXX_LANGUAGE_ID; + else if (li == "c") + return ProjectExplorer::Constants::C_LANGUAGE_ID; + else + return Core::Id::fromName(languageId); + } +}; + +struct DirectoryData +{ + // Project Stuff: + QByteArray cmakeBuildType; + Utils::FileName buildDirectory; + + // Kit Stuff + Utils::FileName cmakeBinary; + QByteArray generator; + QByteArray extraGenerator; + QByteArray platform; + QByteArray toolset; + QByteArray sysroot; + QtProjectImporter::QtVersionData qt; + QVector toolChains; +}; + +static QStringList scanDirectory(const QString &path, const QString &prefix) +{ + QStringList result; + qCDebug(cmInputLog()) << "Scanning for directories matching" << prefix << "in" << path; + + const QDir base = QDir(path); + foreach (const QString &dir, base.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { + const QString subPath = path + '/' + dir; + qCDebug(cmInputLog()) << "Checking" << subPath; + if (dir.startsWith(prefix)) + result.append(subPath); + } + return result; +} + +} // namespace + +namespace CMakeProjectManager { +namespace Internal { + +CMakeProjectImporter::CMakeProjectImporter(const Utils::FileName &path) : QtProjectImporter(path) +{ + useTemporaryKitInformation(CMakeKitInformation::id(), + [this](Kit *k, const QVariantList &vl) { cleanupTemporaryCMake(k, vl); }, + [this](Kit *k, const QVariantList &vl) { persistTemporaryCMake(k, vl); }); + +} + +QStringList CMakeProjectImporter::importCandidates() +{ + QStringList candidates; + + QFileInfo pfi = projectFilePath().toFileInfo(); + candidates << scanDirectory(pfi.absolutePath(), "build"); + + foreach (Kit *k, KitManager::kits()) { + QFileInfo fi(CMakeBuildConfiguration::shadowBuildDirectory(projectFilePath(), k, + QString(), BuildConfiguration::Unknown).toString()); + candidates << scanDirectory(fi.absolutePath(), QString()); + } + const QStringList finalists = Utils::filteredUnique(candidates); + qCInfo(cmInputLog()) << "import candidates:" << finalists; + return finalists; +} + +static Utils::FileName qmakeFromCMakeCache(const CMakeConfig &config) +{ + // Qt4 way to define things (more convenient for us, so try this first;-) + Utils::FileName qmake + = Utils::FileName::fromUtf8(CMakeConfigItem::valueOf(QByteArray("QT_QMAKE_EXECUTABLE"), config)); + qCDebug(cmInputLog()) << "QT_QMAKE_EXECUTABLE=" << qmake.toUserOutput(); + if (!qmake.isEmpty()) + return qmake; + + // Check Qt5 settings: oh, the horror! + const Utils::FileName qtCMakeDir + = Utils::FileName::fromUtf8(CMakeConfigItem::valueOf(QByteArray("Qt5Core_DIR"), config)); + qCDebug(cmInputLog()) << "Qt5Core_DIR=" << qtCMakeDir.toUserOutput(); + const Utils::FileName canQtCMakeDir = Utils::FileName::fromString(qtCMakeDir.toFileInfo().canonicalFilePath()); + qCInfo(cmInputLog()) << "Qt5Core_DIR (canonical)=" << canQtCMakeDir.toUserOutput(); + if (qtCMakeDir.isEmpty()) + return Utils::FileName(); + const Utils::FileName baseQtDir = canQtCMakeDir.parentDir().parentDir().parentDir(); // Up 3 levels... + qCDebug(cmInputLog()) << "BaseQtDir:" << baseQtDir.toUserOutput(); + + // "Parse" Qt5Core/Qt5CoreConfigExtras.cmake: + { + // This assumes that roughly this kind of data is found in + // inside Qt5Core/Qt5CoreConfigExtras.cmake: + // + // if (NOT TARGET Qt5::qmake) + // add_executable(Qt5::qmake IMPORTED) + // set(imported_location "${_qt5Core_install_prefix}/bin/qmake") + // _qt5_Core_check_file_exists(${imported_location}) + // set_target_properties(Qt5::qmake PROPERTIES + // IMPORTED_LOCATION ${imported_location} + // ) + // endif() + + QFile extras(qtCMakeDir.toString() + "/Qt5CoreConfigExtras.cmake"); + if (!extras.open(QIODevice::ReadOnly)) + return Utils::FileName(); + + QByteArray data; + bool inQmakeSection = false; + // Read in 4k chunks, lines longer than that are going to be ignored + while (!extras.atEnd()) { + data = extras.read(4 * 1024 - data.count()); + int startPos = 0; + forever { + const int pos = data.indexOf('\n', startPos); + if (pos < 0) { + data = data.mid(startPos); + break; + } + + QByteArray line = data.mid(startPos, pos - startPos); + const QByteArray origLine = line; + + startPos = pos + 1; + + line.replace("(", " ( "); + line.replace(")", " ) "); + line = line.simplified(); + + if (line == "if ( NOT TARGET Qt5::qmake )") + inQmakeSection = true; + + if (!inQmakeSection) + continue; + + const QByteArray set = "set ( imported_location "; + if (line.startsWith(set)) { + const int sp = origLine.indexOf('}'); + const int ep = origLine.lastIndexOf('"'); + + QTC_ASSERT(sp > 0, return Utils::FileName()); + QTC_ASSERT(ep > sp + 2, return Utils::FileName()); + QTC_ASSERT(ep < origLine.count(), return Utils::FileName()); + + // Eat the leading "}/" and trailing " + const QByteArray locationPart = origLine.mid(sp + 2, ep - 2 - sp); + Utils::FileName result = baseQtDir; + result.appendPath(QString::fromUtf8(locationPart)); + return result; + } + } + } + } + + // Now try to make sense of .../Qt5CoreConfig.cmake: + return Utils::FileName(); +} + +QVector extractToolChainsFromCache(const CMakeConfig &config) +{ + QVector result; + for (const CMakeConfigItem &i : config) { + if (!i.key.startsWith("CMAKE_") || !i.key.endsWith("_COMPILER")) + continue; + const QByteArray language = i.key.mid(6, i.key.count() - 6 - 9); // skip "CMAKE_" and "_COMPILER" + result.append({language, Utils::FileName::fromUtf8(i.value)}); + } + return result; +} + +QList CMakeProjectImporter::examineDirectory(const Utils::FileName &importPath) const +{ + qCInfo(cmInputLog()) << "Examining directory:" << importPath.toUserOutput(); + Utils::FileName cacheFile = importPath; + cacheFile.appendPath("CMakeCache.txt"); + + if (!cacheFile.exists()) { + qCDebug(cmInputLog()) << cacheFile.toUserOutput() << "does not exist, returning."; + return { }; + } + + QString errorMessage; + const CMakeConfig config = BuildDirManager::parseConfiguration(cacheFile, &errorMessage); + if (config.isEmpty() || !errorMessage.isEmpty()) { + qCDebug(cmInputLog()) << "Failed to read configuration from" << cacheFile << errorMessage; + return { }; + } + const auto homeDir + = Utils::FileName::fromUserInput(QString::fromUtf8(CMakeConfigItem::valueOf("CMAKE_HOME_DIRECTORY", config))); + if (homeDir != projectDirectory()) { + qCDebug(cmInputLog()) << "Wrong source directory:" << homeDir.toUserOutput() + << "expected:" << projectDirectory().toUserOutput(); + return { }; + } + + auto data = std::make_unique(); + data->buildDirectory = importPath; + data->cmakeBuildType = CMakeConfigItem::valueOf("CMAKE_BUILD_TYPE", config); + + data->cmakeBinary + = Utils::FileName::fromUtf8(CMakeConfigItem::valueOf("CMAKE_COMMAND", config)); + data->generator = CMakeConfigItem::valueOf("CMAKE_GENERATOR", config); + data->extraGenerator = CMakeConfigItem::valueOf("CMAKE_EXTRA_GENERATOR", config); + data->platform = CMakeConfigItem::valueOf("CMAKE_GENERATOR_PLATFORM", config); + data->toolset = CMakeConfigItem::valueOf("CMAKE_GENERATOR_TOOLSET", config); + + data->sysroot = CMakeConfigItem::valueOf("CMAKE_SYSROOT", config); + + // Qt: + const Utils::FileName qmake = qmakeFromCMakeCache(config); + if (!qmake.isEmpty()) + data->qt = findOrCreateQtVersion(qmake); + + // ToolChains: + data->toolChains = extractToolChainsFromCache(config); + + qCInfo(cmInputLog()) << "Offering to import" << importPath.toUserOutput(); + return {static_cast(data.release())}; +} + +bool CMakeProjectImporter::matchKit(void *directoryData, const Kit *k) const +{ + const DirectoryData *data = static_cast(directoryData); + + CMakeTool *cm = CMakeKitInformation::cmakeTool(k); + if (!cm || cm->cmakeExecutable() != data->cmakeBinary) + return false; + + if (CMakeGeneratorKitInformation::generator(k) != QString::fromUtf8(data->generator) + || CMakeGeneratorKitInformation::extraGenerator(k) != QString::fromUtf8(data->extraGenerator) + || CMakeGeneratorKitInformation::platform(k) != QString::fromUtf8(data->platform) + || CMakeGeneratorKitInformation::toolset(k) != QString::fromUtf8(data->toolset)) + return false; + + if (SysRootKitInformation::sysRoot(k) != Utils::FileName::fromUtf8(data->sysroot)) + return false; + + if (data->qt.qt && QtSupport::QtKitInformation::qtVersionId(k) != data->qt.qt->uniqueId()) + return false; + + for (const CMakeToolChainData &tcd : data->toolChains) { + ToolChain *tc = ToolChainKitInformation::toolChain(k, tcd.mapLanguageIdToQtC()); + if (!tc || tc->compilerCommand() != tcd.compilerPath) + return false; + } + + qCDebug(cmInputLog()) << k->displayName() + << "matches directoryData for" << data->buildDirectory.toUserOutput(); + return true; +} + +Kit *CMakeProjectImporter::createKit(void *directoryData) const +{ + const DirectoryData *data = static_cast(directoryData); + + return QtProjectImporter::createTemporaryKit(data->qt, [&data, this](Kit *k) { + const CMakeToolData cmtd = findOrCreateCMakeTool(data->cmakeBinary); + QTC_ASSERT(cmtd.cmakeTool, return); + if (cmtd.isTemporary) + addTemporaryData(CMakeKitInformation::id(), cmtd.cmakeTool->id().toSetting(), k); + + CMakeGeneratorKitInformation::setGenerator(k, QString::fromUtf8(data->generator)); + CMakeGeneratorKitInformation::setExtraGenerator(k, QString::fromUtf8(data->extraGenerator)); + CMakeGeneratorKitInformation::setPlatform(k, QString::fromUtf8(data->platform)); + CMakeGeneratorKitInformation::setToolset(k, QString::fromUtf8(data->toolset)); + + SysRootKitInformation::setSysRoot(k, Utils::FileName::fromUtf8(data->sysroot)); + + for (const CMakeToolChainData &cmtcd : data->toolChains) { + const ToolChainData tcd + = findOrCreateToolChains(cmtcd.compilerPath, cmtcd.mapLanguageIdToQtC()); + QTC_ASSERT(!tcd.tcs.isEmpty(), continue); + + if (tcd.areTemporary) { + for (ToolChain *tc : tcd.tcs) + addTemporaryData(ToolChainKitInformation::id(), tc->id(), k); + } + + ToolChainKitInformation::setToolChain(k, tcd.tcs.at(0)); + } + + qCInfo(cmInputLog()) << "Temporary Kit created."; + }); +} + +QList CMakeProjectImporter::buildInfoListForKit(const Kit *k, void *directoryData) const +{ + QList result; + DirectoryData *data = static_cast(directoryData); + auto factory = qobject_cast( + IBuildConfigurationFactory::find(k, projectFilePath().toString())); + if (!factory) + return result; + + // create info: + std::unique_ptr + info(factory->createBuildInfo(k, projectDirectory().toString(), + CMakeBuildConfigurationFactory::buildTypeFromByteArray(data->cmakeBuildType))); + info->buildDirectory = data->buildDirectory; + info->displayName = info->typeName; + + bool found = false; + foreach (BuildInfo *bInfo, result) { + if (*static_cast(bInfo) == *info) { + found = true; + break; + } + } + if (!found) + result << info.release(); + + qCDebug(cmInputLog()) << "BuildInfo configured."; + + return result; +} + +CMakeProjectImporter::CMakeToolData +CMakeProjectImporter::findOrCreateCMakeTool(const Utils::FileName &cmakeToolPath) const +{ + CMakeToolData result; + result.cmakeTool = CMakeToolManager::findByCommand(cmakeToolPath); + if (!result.cmakeTool) { + qCDebug(cmInputLog()) << "Creating temporary CMakeTool for" << cmakeToolPath.toUserOutput(); + result.cmakeTool = new CMakeTool(CMakeTool::ManualDetection, CMakeTool::createId()); + result.isTemporary = true; + } + return result; +} + +void CMakeProjectImporter::deleteDirectoryData(void *directoryData) const +{ + delete static_cast(directoryData); +} + +void CMakeProjectImporter::cleanupTemporaryCMake(Kit *k, const QVariantList &vl) +{ + if (vl.isEmpty()) + return; // No temporary CMake + QTC_ASSERT(vl.count() == 1, return); + CMakeKitInformation::setCMakeTool(k, Core::Id()); // Always mark Kit as not using this Qt + CMakeToolManager::deregisterCMakeTool(Core::Id::fromSetting(vl.at(0))); + qCDebug(cmInputLog()) << "Temporary CMake tool cleaned up."; +} + +void CMakeProjectImporter::persistTemporaryCMake(Kit *k, const QVariantList &vl) +{ + if (vl.isEmpty()) + return; // No temporary CMake + QTC_ASSERT(vl.count() == 1, return); + const QVariant data = vl.at(0); + CMakeTool *tmpCmake = CMakeToolManager::findById(Core::Id::fromSetting(data)); + CMakeTool *actualCmake = CMakeKitInformation::cmakeTool(k); + + // User changed Kit away from temporary CMake that was set up: + if (tmpCmake && actualCmake != tmpCmake) + CMakeToolManager::deregisterCMakeTool(tmpCmake->id()); + + qCDebug(cmInputLog()) << "Temporary CMake tool made persistent."; +} + +} // namespace Internal +} // namespace CMakeProjectManager + +#ifdef WITH_TESTS + +#include "cmakeprojectplugin.h" + +#include + +namespace CMakeProjectManager { +namespace Internal { + +void CMakeProjectPlugin::testCMakeProjectImporterQt_data() +{ + QTest::addColumn("cache"); + QTest::addColumn("expectedQmake"); + + QTest::newRow("Empty input") + << QStringList() << QString(); + + QTest::newRow("Qt4") + << QStringList({QString::fromLatin1("QT_QMAKE_EXECUTABLE=/usr/bin/xxx/qmake")}) + << "/usr/bin/xxx/qmake"; + + // Everything else will require Qt installations! +} + +void CMakeProjectPlugin::testCMakeProjectImporterQt() +{ + QFETCH(QStringList, cache); + QFETCH(QString, expectedQmake); + + CMakeConfig config; + foreach (const QString &c, cache) { + const int pos = c.indexOf('='); + Q_ASSERT(pos > 0); + const QString key = c.left(pos); + const QString value = c.mid(pos + 1); + config.append(CMakeConfigItem(key.toUtf8(), value.toUtf8())); + } + + Utils::FileName realQmake = qmakeFromCMakeCache(config); + QCOMPARE(realQmake.toString(), expectedQmake); +} +void CMakeProjectPlugin::testCMakeProjectImporterToolChain_data() +{ + QTest::addColumn("cache"); + QTest::addColumn("expectedLanguages"); + QTest::addColumn("expectedToolChains"); + + QTest::newRow("Empty input") + << QStringList() << QByteArrayList() << QStringList(); + + QTest::newRow("Unrelated input") + << QStringList("CMAKE_SOMETHING_ELSE=/tmp") << QByteArrayList() << QStringList(); + QTest::newRow("CXX compiler") + << QStringList({"CMAKE_CXX_COMPILER=/usr/bin/g++"}) + << QByteArrayList({"CXX"}) + << QStringList({"/usr/bin/g++"}); + QTest::newRow("CXX compiler, C compiler") + << QStringList({"CMAKE_CXX_COMPILER=/usr/bin/g++", "CMAKE_C_COMPILER=/usr/bin/clang"}) + << QByteArrayList({"CXX", "C"}) + << QStringList({"/usr/bin/g++", "/usr/bin/clang"}); + QTest::newRow("CXX compiler, C compiler, strange compiler") + << QStringList({"CMAKE_CXX_COMPILER=/usr/bin/g++", + "CMAKE_C_COMPILER=/usr/bin/clang", + "CMAKE_STRANGE_LANGUAGE_COMPILER=/tmp/strange/compiler"}) + << QByteArrayList({"CXX", "C", "STRANGE_LANGUAGE"}) + << QStringList({"/usr/bin/g++", "/usr/bin/clang", "/tmp/strange/compiler"}); + QTest::newRow("CXX compiler, C compiler, strange compiler (with junk)") + << QStringList({"FOO=test", + "CMAKE_CXX_COMPILER=/usr/bin/g++", + "CMAKE_BUILD_TYPE=debug", + "CMAKE_C_COMPILER=/usr/bin/clang", + "SOMETHING_COMPILER=/usr/bin/something", + "CMAKE_STRANGE_LANGUAGE_COMPILER=/tmp/strange/compiler", + "BAR=more test"}) + << QByteArrayList({"CXX", "C", "STRANGE_LANGUAGE"}) + << QStringList({"/usr/bin/g++", "/usr/bin/clang", "/tmp/strange/compiler"}); +} + +void CMakeProjectPlugin::testCMakeProjectImporterToolChain() +{ + QFETCH(QStringList, cache); + QFETCH(QByteArrayList, expectedLanguages); + QFETCH(QStringList, expectedToolChains); + + QCOMPARE(expectedLanguages.count(), expectedToolChains.count()); + + CMakeConfig config; + foreach (const QString &c, cache) { + const int pos = c.indexOf('='); + Q_ASSERT(pos > 0); + const QString key = c.left(pos); + const QString value = c.mid(pos + 1); + config.append(CMakeConfigItem(key.toUtf8(), value.toUtf8())); + } + + QVector tcs = extractToolChainsFromCache(config); + QCOMPARE(tcs.count(), expectedLanguages.count()); + for (int i = 0; i < tcs.count(); ++i) { + QCOMPARE(tcs.at(i).languageId, expectedLanguages.at(i)); + QCOMPARE(tcs.at(i).compilerPath.toString(), expectedToolChains.at(i)); + } +} + +} // namespace Internal +} // namespace CMakeProjectManager + +#endif diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectimporter.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectimporter.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectimporter.h 1970-01-01 00:00:00.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectimporter.h 2017-02-26 23:55:33.000000000 +0000 @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** 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 + +namespace CMakeProjectManager { + +class CMakeTool; + +namespace Internal { + +class CMakeProjectImporter : public QtSupport::QtProjectImporter +{ +public: + CMakeProjectImporter(const Utils::FileName &path); + + QStringList importCandidates() final; + +private: + QList examineDirectory(const Utils::FileName &importPath) const final; + bool matchKit(void *directoryData, const ProjectExplorer::Kit *k) const final; + ProjectExplorer::Kit *createKit(void *directoryData) const final; + QList buildInfoListForKit(const ProjectExplorer::Kit *k, + void *directoryData) const final; + + struct CMakeToolData { + bool isTemporary = false; + CMakeTool *cmakeTool = nullptr; + }; + CMakeToolData findOrCreateCMakeTool(const Utils::FileName &cmakeToolPath) const; + + void deleteDirectoryData(void *directoryData) const final; + + void cleanupTemporaryCMake(ProjectExplorer::Kit *k, const QVariantList &vl); + void persistTemporaryCMake(ProjectExplorer::Kit *k, const QVariantList &vl); +}; + +} // namespace Internal +} // namespace CMakeProjectManager diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectmanager.pro qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectmanager.pro --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectmanager.pro 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectmanager.pro 2017-02-26 23:55:33.000000000 +0000 @@ -17,6 +17,7 @@ cmakebuildstep.h \ cmakeconfigitem.h \ cmakeproject.h \ + cmakeprojectimporter.h \ cmakeprojectplugin.h \ cmakeprojectmanager.h \ cmakeprojectconstants.h \ @@ -52,6 +53,7 @@ cmakebuildstep.cpp \ cmakeconfigitem.cpp \ cmakeproject.cpp \ + cmakeprojectimporter.cpp \ cmakeprojectplugin.cpp \ cmakeprojectmanager.cpp \ cmakeprojectnodes.cpp \ diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectmanager.qbs qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectmanager.qbs --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectmanager.qbs 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectmanager.qbs 2017-02-26 23:55:33.000000000 +0000 @@ -49,6 +49,8 @@ "cmakeproject.cpp", "cmakeproject.h", "cmakeproject.qrc", + "cmakeprojectimporter.cpp", + "cmakeprojectimporter.h", "cmakeprojectconstants.h", "cmakeprojectmanager.cpp", "cmakeprojectmanager.h", diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectnodes.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectnodes.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectnodes.cpp 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectnodes.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -118,6 +118,12 @@ return m_project ? m_project->renameFile(filePath, newFilePath) : false; } +void CMakeListsNode::makeTree(QList &files, const Utils::FileName &overrideBaseDir) +{ + buildTree(files, overrideBaseDir); + emitTreeChanged(); +} + CMakeProjectNode::CMakeProjectNode(const Utils::FileName &directory) : ProjectExplorer::ProjectNode(directory) { @@ -168,7 +174,7 @@ const QString &type) { m_tooltip = QCoreApplication::translate("CMakeTargetNode", "Target type: ") + type + "
"; - if (artifacts.count() == 0) { + if (artifacts.isEmpty()) { m_tooltip += QCoreApplication::translate("CMakeTargetNode", "No build artifacts"); } else { const QStringList tmp = Utils::transform(artifacts, &Utils::FileName::toUserOutput); diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectnodes.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectnodes.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectnodes.h 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectnodes.h 2017-02-26 23:55:33.000000000 +0000 @@ -57,6 +57,8 @@ bool deleteFiles(const QStringList &filePaths) final; bool renameFile(const QString &filePath, const QString &newFilePath) final; + void makeTree(QList &files, const Utils::FileName &overrideBaseDir); + private: CMakeProject *m_project = nullptr; }; diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectplugin.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectplugin.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectplugin.cpp 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectplugin.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -108,9 +108,10 @@ CMakeToolManager::restoreCMakeTools(); } -void CMakeProjectPlugin::updateContextActions(ProjectExplorer::Node *node, - ProjectExplorer::Project *project) +void CMakeProjectPlugin::updateContextActions() { + Project *project = ProjectTree::currentProject(); + Node *node = ProjectTree::currentNode(); CMakeTargetNode *targetNode = dynamic_cast(node); // as targetNode can be deleted while the menu is open, we keep only the const QString targetDisplayName = targetNode ? targetNode->displayName() : QString(); diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectplugin.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectplugin.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakeprojectplugin.h 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakeprojectplugin.h 2017-02-26 23:55:33.000000000 +0000 @@ -29,11 +29,6 @@ #include -namespace ProjectExplorer { -class Node; -class Project; -} // namespace ProjectExplorer - namespace Utils { class ParameterAction; } namespace CMakeProjectManager { @@ -60,10 +55,16 @@ void testCMakeSplitValue_data(); void testCMakeSplitValue(); + + void testCMakeProjectImporterQt_data(); + void testCMakeProjectImporterQt(); + + void testCMakeProjectImporterToolChain_data(); + void testCMakeProjectImporterToolChain(); #endif private: - void updateContextActions(ProjectExplorer::Node *node, ProjectExplorer::Project *project); + void updateContextActions(); Utils::ParameterAction *m_buildTargetContextAction = nullptr; QMetaObject::Connection m_actionConnect; diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakerunconfiguration.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakerunconfiguration.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakerunconfiguration.h 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakerunconfiguration.h 2017-02-26 23:55:33.000000000 +0000 @@ -56,6 +56,8 @@ bool isEnabled() const override; QString disabledReason() const override; + QString buildSystemTarget() const final { return m_buildTarget; } + protected: CMakeRunConfiguration(ProjectExplorer::Target *parent, CMakeRunConfiguration *source); bool fromMap(const QVariantMap &map) override; diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakesettingspage.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakesettingspage.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmakesettingspage.cpp 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmakesettingspage.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -341,7 +341,7 @@ m_binaryChooser->setExpectedKind(PathChooser::ExistingCommand); m_binaryChooser->setMinimumWidth(400); m_binaryChooser->setHistoryCompleter(QLatin1String("Cmake.Command.History")); - m_binaryChooser->setCommandVersionArguments({ "--version" }); + m_binaryChooser->setCommandVersionArguments({"--version"}); m_autoRunCheckBox = new QCheckBox; m_autoRunCheckBox->setText(tr("Autorun CMake")); diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmaketool.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmaketool.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/cmaketool.cpp 2017-01-16 23:14:37.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/cmaketool.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -172,19 +172,19 @@ { if (m_functions.isEmpty()) { Utils::SynchronousProcessResponse response; - response = run({ "--help-command-list" }); + response = run({"--help-command-list"}); if (response.result == Utils::SynchronousProcessResponse::Finished) m_functions = response.stdOut().split('\n'); - response = run({ "--help-commands" }); + response = run({"--help-commands"}); if (response.result == Utils::SynchronousProcessResponse::Finished) parseFunctionDetailsOutput(response.stdOut()); - response = run({ "--help-property-list" }); + response = run({"--help-property-list"}); if (response.result == Utils::SynchronousProcessResponse::Finished) m_variables = parseVariableOutput(response.stdOut()); - response = run({ "--help-variable-list" }); + response = run({"--help-variable-list"}); if (response.result == Utils::SynchronousProcessResponse::Finished) { m_variables.append(parseVariableOutput(response.stdOut())); m_variables = Utils::filteredUnique(m_variables); @@ -354,7 +354,7 @@ void CMakeTool::fetchGeneratorsFromHelp() const { - Utils::SynchronousProcessResponse response = run({ "--help" }); + Utils::SynchronousProcessResponse response = run({"--help"}); if (response.result != Utils::SynchronousProcessResponse::Finished) return; @@ -406,7 +406,7 @@ void CMakeTool::fetchVersionFromVersionOutput() const { - Utils::SynchronousProcessResponse response = run({ "--version" }); + Utils::SynchronousProcessResponse response = run({"--version" }); if (response.result != Utils::SynchronousProcessResponse::Finished) return; @@ -427,7 +427,7 @@ void CMakeTool::fetchFromCapabilities() const { - Utils::SynchronousProcessResponse response = run({ "-E", "capabilities" }, true); + Utils::SynchronousProcessResponse response = run({"-E", "capabilities" }, true); if (response.result != Utils::SynchronousProcessResponse::Finished) return; diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/compat.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/compat.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/compat.h 2017-01-16 23:14:38.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/compat.h 2017-02-26 23:55:33.000000000 +0000 @@ -4,6 +4,7 @@ namespace ProjectExplorer { class FileNode; +class FolderNode; class ProjectNode; } diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/configmodel.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/configmodel.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/configmodel.cpp 2017-01-16 23:14:38.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/configmodel.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -106,13 +107,16 @@ QFont font; font.setItalic(item.isCMakeChanged); font.setBold(item.isUserNew); + font.setStrikeOut(!item.inCMakeCache && !item.isUserNew); return font; } default: return QVariant(); } case 1: { - const QString value = item.isUserChanged ? item.newValue : item.value; + const QString value = item.currentValue(); + const QString kitValue = m_kitConfiguartion.value(item.key); + switch (role) { case Qt::CheckStateRole: return (item.type == DataItem::BOOLEAN) @@ -127,8 +131,19 @@ font.setItalic(item.isCMakeChanged); return font; } - case Qt::ToolTipRole: - return item.toolTip(); + case Qt::ForegroundRole: + return Utils::creatorTheme()->color((!kitValue.isNull() && kitValue != value) + ? Utils::Theme::TextColorHighlight : Utils::Theme::TextColorNormal); + case Qt::ToolTipRole: { + QString tooltip = item.toolTip(); + const QString kitValue = m_kitConfiguartion.value(item.key); + if (!kitValue.isNull()) { + if (!tooltip.isEmpty()) + tooltip.append("
"); + tooltip.append(tr("Kit value: %1").arg(kitValue)); + } + return tooltip; + } default: return QVariant(); } @@ -272,6 +287,11 @@ endResetModel(); } +void ConfigModel::setKitConfiguration(const QHash &kitConfig) +{ + m_kitConfiguartion = kitConfig; +} + void ConfigModel::flush() { beginResetModel(); @@ -307,16 +327,16 @@ QList ConfigModel::configurationChanges() const { - QList result; const QList tmp - = Utils::filtered(m_configuration, [](const InternalDataItem &i) { return i.isUserChanged || i.isUserNew; }); - foreach (const InternalDataItem &item, tmp) { + = Utils::filtered(m_configuration, [](const InternalDataItem &i) { + return i.isUserChanged || i.isUserNew || !i.inCMakeCache; + }); + return Utils::transform(tmp, [](const InternalDataItem &item) { DataItem newItem(item); if (item.isUserChanged) newItem.value = item.newValue; - result << newItem; - } - return result; + return newItem; + }); } ConfigModel::InternalDataItem &ConfigModel::itemAtRow(int row) @@ -337,10 +357,18 @@ QString ConfigModel::InternalDataItem::toolTip() const { QStringList tooltip(description); - if (!value.isEmpty() && !newValue.isEmpty() && value != newValue) - tooltip << QCoreApplication::translate("CMakeProjectManager", "Current CMake: %1").arg(value); + if (inCMakeCache) { + if (value != newValue) + tooltip << QCoreApplication::translate("CMakeProjectManager", "Current CMake: %1").arg(value); + } else { + tooltip << QCoreApplication::translate("CMakeProjectManager", "Not in CMakeCache.txt").arg(value); + } return tooltip.join("
"); } -} // namespace CMakeProjectManager +QString ConfigModel::InternalDataItem::currentValue() const +{ + return isUserChanged ? newValue : value; +} +} // namespace CMakeProjectManager diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/configmodel.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/configmodel.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/configmodel.h 2017-01-16 23:14:38.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/configmodel.h 2017-02-26 23:55:33.000000000 +0000 @@ -46,6 +46,7 @@ QString key; Type type = STRING; bool isAdvanced = false; + bool inCMakeCache = false; QString value; QString description; QStringList values; @@ -67,6 +68,7 @@ const QString &description = QString(), const QStringList &values = QStringList()); void setConfiguration(const QList &config); + void setKitConfiguration(const QHash &kitConfig); void flush(); void resetAllChanges(); @@ -83,6 +85,7 @@ InternalDataItem(const InternalDataItem &item) = default; QString toolTip() const; + QString currentValue() const; bool isUserChanged = false; bool isUserNew = false; @@ -93,6 +96,7 @@ InternalDataItem &itemAtRow(int row); const InternalDataItem &itemAtRow(int row) const; QList m_configuration; + QHash m_kitConfiguartion; }; } // namespace CMakeProjectManager diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/debian/changelog qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/debian/changelog --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/debian/changelog 2017-01-16 23:17:36.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/debian/changelog 2017-02-26 23:57:47.000000000 +0000 @@ -1,3 +1,9 @@ +qtcreator-git-plugin-cmake2 (20170227.573-9fcb7a7-trusty~ppa1) trusty; urgency=medium + + * Bump version + + -- Alexander Drozdov Mon, 27 Feb 2017 09:56:06 +1000 + qtcreator-git-plugin-cmake2 (20170117.517-3d39bac-trusty~ppa1) trusty; urgency=medium * Bump version diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/debian/changelog.tmp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/debian/changelog.tmp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/debian/changelog.tmp 2017-01-16 23:17:36.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/debian/changelog.tmp 2017-02-26 23:57:47.000000000 +0000 @@ -1,3 +1,9 @@ +qtcreator-git-plugin-cmake2 (20170227.573-9fcb7a7-unstable~ppa1) unstable; urgency=medium + + * Bump version + + -- Alexander Drozdov Mon, 27 Feb 2017 09:56:06 +1000 + qtcreator-git-plugin-cmake2 (20170117.517-3d39bac-unstable~ppa1) unstable; urgency=medium * Bump version diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/debian/control qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/debian/control --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/debian/control 2017-01-16 23:14:35.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/debian/control 2017-02-26 23:57:39.000000000 +0000 @@ -6,7 +6,7 @@ qt56base, qt56tools, qt56xmlpatterns, - qtcreator-git-dev (>= 4.2.82~20170117), + qtcreator-git-dev (>= 4.2.82~20170227), libgl1-mesa-dev, Standards-Version: 3.9.5 Homepage: https://github.com/h4tr3d/cmakeprojectmanager2 diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/servermode.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/servermode.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/servermode.cpp 2017-01-16 23:14:38.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/servermode.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -25,14 +25,17 @@ #include "servermode.h" +#include #include #include #include #include +#include #include #include +#include #include #include #include @@ -60,15 +63,6 @@ // Helpers: // ---------------------------------------------------------------------- -QString socketName(const Utils::FileName &buildDirectory) -{ - if (HostOsInfo::isWindowsHost()) { - QUuid uuid = QUuid::createUuid(); - return "\\\\.\\pipe\\" + uuid.toString(); - } - return buildDirectory.toString() + "/socket"; -} - bool isValid(const QVariant &v) { if (v.isNull()) @@ -91,6 +85,12 @@ bool experimental, int major, int minor, QObject *parent) : QObject(parent), +#if defined(Q_OS_UNIX) + // Some unixes (e.g. Darwin) limit the length of a local socket to about 100 char (or less). + // Since some unixes (e.g. Darwin) also point TMPDIR to /really/long/paths we need to create + // our own socket in a place closer to '/'. + m_socketDir("/tmp/cmake-"), +#endif m_sourceDirectory(sourceDirectory), m_buildDirectory(buildDirectory), m_cmakeExecutable(cmakeExecutable), m_generator(generator), m_extraGenerator(extraGenerator), @@ -107,8 +107,14 @@ m_cmakeProcess->setEnvironment(env); m_cmakeProcess->setWorkingDirectory(buildDirectory.toString()); - m_socketName = socketName(buildDirectory); - const QStringList args = QStringList({ "-E", "server", "--pipe=" + m_socketName }); + +#if defined(Q_OS_UNIX) + m_socketName = m_socketDir.path() + "/socket"; +#else + m_socketName = QString::fromLatin1("\\\\.\\pipe\\") + QUuid::createUuid().toString(); +#endif + + const QStringList args = QStringList({"-E", "server", "--pipe=" + m_socketName}); connect(m_cmakeProcess.get(), &QtcProcess::started, this, [this]() { m_connectionTimer.start(); }); connect(m_cmakeProcess.get(), @@ -162,7 +168,7 @@ data.insert(TYPE_KEY, type); const QVariant realCookie = cookie.isNull() ? QVariant(m_requestCounter) : cookie; data.insert(COOKIE_KEY, realCookie); - m_expectedReplies.push_back({ type, realCookie }); + m_expectedReplies.push_back({type, realCookie}); QJsonObject object = QJsonObject::fromVariantMap(data); QJsonDocument document; diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/servermode.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/servermode.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/servermode.h 2017-01-16 23:14:38.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/servermode.h 2017-02-26 23:55:33.000000000 +0000 @@ -28,9 +28,9 @@ #include #include -#include #include #include +#include #include @@ -85,6 +85,9 @@ void reportError(const QString &msg); +#if defined(Q_OS_UNIX) + QTemporaryDir m_socketDir; +#endif std::unique_ptr m_cmakeProcess; QLocalSocket *m_cmakeSocket = nullptr; QTimer m_connectionTimer; @@ -113,6 +116,7 @@ const int m_minorProtocol = -1; int m_requestCounter = 0; + }; } // namespace Internal diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/servermodereader.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/servermodereader.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/servermodereader.cpp 2017-01-16 23:14:38.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/servermodereader.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -62,15 +61,6 @@ const int MAX_PROGRESS = 1400; -// ---------------------------------------------------------------------- -// Helpers: -// ---------------------------------------------------------------------- - -QString socketName(const BuildDirReader::Parameters &p) -{ - return p.buildDirectory.toString() + "/socket"; -} - // -------------------------------------------------------------------- // ServerModeReader: // -------------------------------------------------------------------- @@ -112,7 +102,10 @@ connect(m_cmakeServer.get(), &ServerMode::connected, this, &ServerModeReader::isReadyNow, Qt::QueuedConnection); // Delay connect(m_cmakeServer.get(), &ServerMode::disconnected, - this, [this]() { m_cmakeServer.reset(); }, Qt::QueuedConnection); // Delay + this, [this]() { + stop(); + m_cmakeServer.reset(); + }, Qt::QueuedConnection); // Delay } } @@ -144,7 +137,7 @@ QTC_ASSERT(m_cmakeServer, return); QVariantMap extra; - if (force) { + if (force || !QDir(m_parameters.buildDirectory.toString()).exists("CMakeCache.txt")) { QStringList cacheArguments = transform(m_parameters.configuration, [this](const CMakeConfigItem &i) { return i.toArgument(m_parameters.expander); @@ -223,66 +216,38 @@ return config; } -FolderNode *setupCMakeVFolder(FolderNode *base, const Utils::FileName &basePath, int priority, - const QString &displayName, QList &files) +static void addCMakeVFolder(FolderNode *base, const Utils::FileName &basePath, int priority, + const QString &displayName, QList &files) { if (files.isEmpty()) - return nullptr; - - FolderNode *folder - = findOrDefault(base->folderNodes(), [basePath](const FolderNode *fn) { - return fn->filePath() == basePath; - }); - - if (!folder) { - folder = new VirtualFolderNode(basePath, priority); - folder->setDisplayName(displayName); - base->addFolderNodes({ folder }); - } + return; + auto folder = new VirtualFolderNode(basePath, priority); + folder->setDisplayName(displayName); + base->addNode(folder); folder->buildTree(files); - return folder; + for (FolderNode *fn : folder->folderNodes()) + fn->compress(); } -static ProjectNode *updateCMakeInputs(CMakeListsNode *root, - const Utils::FileName &sourceDir, - const Utils::FileName &buildDir, - QList &sourceInputs, - QList &buildInputs, - QList &rootInputs) -{ - const bool hasInputs = !sourceInputs.isEmpty() || !buildInputs.isEmpty() || !rootInputs.isEmpty(); - ProjectNode *cmakeVFolder - = root->projectNode(CMakeInputsNode::inputsPathFromCMakeListsPath(root->filePath())); - if (!cmakeVFolder) { - if (hasInputs) { - cmakeVFolder = new CMakeInputsNode(root->filePath()); - root->addProjectNodes({ cmakeVFolder }); - } - } else { - if (!hasInputs) - root->removeProjectNodes({ cmakeVFolder }); - } - if (!hasInputs) - return nullptr; - - QList toKeep; - toKeep.append(setupCMakeVFolder(cmakeVFolder, sourceDir, 1000, - QCoreApplication::translate("CMakeProjectManager::Internal", ""), - sourceInputs)); - toKeep.append(setupCMakeVFolder(cmakeVFolder, buildDir, 100, - QCoreApplication::translate("CMakeProjectManager::Internal", ""), - buildInputs)); - toKeep.append(setupCMakeVFolder(cmakeVFolder, Utils::FileName(), 10, - QCoreApplication::translate("CMakeProjectManager::Internal", ""), - rootInputs)); - - // Clean out unused nodes in "CMake Files": - const QList tmp = filtered(cmakeVFolder->folderNodes(), [&toKeep](FolderNode *fn) { - return !toKeep.contains(fn); - }); - cmakeVFolder->removeFolderNodes(tmp); - - return cmakeVFolder; +static void addCMakeInputs(CMakeListsNode *root, + const Utils::FileName &sourceDir, + const Utils::FileName &buildDir, + QList &sourceInputs, + QList &buildInputs, + QList &rootInputs) +{ + ProjectNode *cmakeVFolder = new CMakeInputsNode(root->filePath()); + root->addNode(cmakeVFolder); + + addCMakeVFolder(cmakeVFolder, sourceDir, 1000, + QCoreApplication::translate("CMakeProjectManager::Internal::ServerModeReader", ""), + sourceInputs); + addCMakeVFolder(cmakeVFolder, buildDir, 100, + QCoreApplication::translate("CMakeProjectManager::Internal::ServerModeReader", ""), + buildInputs); + addCMakeVFolder(cmakeVFolder, Utils::FileName(), 10, + QCoreApplication::translate("CMakeProjectManager::Internal::ServerModeReader", ""), + rootInputs); } void ServerModeReader::generateProjectTree(CMakeListsNode *root, @@ -307,23 +272,22 @@ } m_cmakeInputsFileNodes.clear(); // Clean out, they are not going to be used anymore! - if (!m_projects.isEmpty()) - root->setDisplayName(m_projects.at(0)->name); - - QSet usedNodes; - usedNodes.insert(updateCMakeInputs(root, m_parameters.sourceDirectory, m_parameters.buildDirectory, - cmakeFilesSource, cmakeFilesBuild, cmakeFilesOther)); + const Project *topLevel = Utils::findOrDefault(m_projects, [this](const Project *p) { + return m_parameters.sourceDirectory == p->sourceDirectory; + }); + if (topLevel) + root->setDisplayName(topLevel->name); - usedNodes.unite(updateCMakeLists(root, cmakeLists)); - usedNodes.unite(updateProjects(root, m_projects, allFiles)); + if (!cmakeFilesSource.isEmpty() || !cmakeFilesBuild.isEmpty() || !cmakeFilesOther.isEmpty()) + addCMakeInputs(root, m_parameters.sourceDirectory, m_parameters.buildDirectory, + cmakeFilesSource, cmakeFilesBuild, cmakeFilesOther); - // Trim out unused nodes: - root->trim(usedNodes); + addCMakeLists(root, cmakeLists); + addProjects(root, m_projects, allFiles); } -QSet ServerModeReader::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) +void ServerModeReader::updateCodeModel(CppTools::RawProjectParts &rpps) { - QSet languages; int counter = 0; foreach (const FileGroup *fg, m_fileGroups) { ++counter; @@ -338,23 +302,29 @@ const QStringList flags = QtcProcess::splitArgs(fg->compileFlags); const QStringList includes = transform(fg->includePaths, [](const IncludePath *ip) { return ip->path.toString(); }); - ppBuilder.setProjectFile(fg->target->sourceDirectory.toString()); - ppBuilder.setDisplayName(fg->target->name + QString::number(counter)); - ppBuilder.setDefines(defineArg.toUtf8()); - ppBuilder.setIncludePaths(includes); - ppBuilder.setCFlags(flags); - ppBuilder.setCxxFlags(flags); + CppTools::RawProjectPart rpp; + rpp.setProjectFile(fg->target->sourceDirectory.toString() + "/CMakeLists.txt"); + rpp.setDisplayName(fg->target->name + QString::number(counter)); + rpp.setDefines(defineArg.toUtf8()); + rpp.setIncludePaths(includes); + + CppTools::RawProjectPartFlags cProjectFlags; + cProjectFlags.commandLineFlags = flags; + rpp.setFlagsForC(cProjectFlags); + + CppTools::RawProjectPartFlags cxxProjectFlags; + cxxProjectFlags.commandLineFlags = flags; + rpp.setFlagsForCxx(cxxProjectFlags); + rpp.setFiles(transform(fg->sources, &FileName::toString)); - languages.unite(QSet::fromList(ppBuilder.createProjectPartsForFiles(transform(fg->sources, &FileName::toString)))); + rpps.append(rpp); } qDeleteAll(m_projects); // Not used anymore! m_projects.clear(); m_targets.clear(); m_fileGroups.clear(); - - return languages; } void ServerModeReader::handleReply(const QVariantMap &data, const QString &inReplyTo) @@ -548,11 +518,8 @@ m_cmakeCache = config; } -QSet ServerModeReader::updateCMakeLists(CMakeListsNode *root, - const QList &cmakeLists) +void ServerModeReader::addCMakeLists(CMakeListsNode *root, const QList &cmakeLists) { - QSet usedNodes; - const QDir baseDir = QDir(m_parameters.sourceDirectory.toString()); QHash nodeHash; @@ -589,17 +556,15 @@ cmln = static_cast(parentNode->projectNode(fn->filePath())); if (!cmln) { cmln = new CMakeListsNode(fn->filePath()); - parentNode->addProjectNodes({ cmln }); + parentNode->addNode(cmln); } // Find or create CMakeLists.txt filenode below CMakeListsNode: FileNode *cmFn = cmln->fileNode(fn->filePath()); if (!cmFn) { cmFn = fn; - cmln->addFileNodes({ cmFn }); + cmln->addNode(cmFn); } - usedNodes.insert(cmFn); // register existing CMakeLists.txt filenode - // Update displayName of CMakeListsNode: const QString dn = prefix.isEmpty() ? k : k.mid(prefix.count() + 1); if (!dn.isEmpty()) @@ -607,38 +572,37 @@ knownNodes.insert(k, cmln); } - - return usedNodes; } static CMakeListsNode *findCMakeNode(CMakeListsNode *root, const Utils::FileName &dir) { - Utils::FileName stepDir = dir; + const Utils::FileName stepDir = dir; + const Utils::FileName topDir = root->filePath().parentDir(); - CMakeListsNode *base = root; - forever { - Utils::FileName stepLists = stepDir; - stepLists.appendPath("CMakeLists.txt"); + QStringList relative = stepDir.relativeChildPath(topDir).toString().split('/', QString::SkipEmptyParts); - CMakeListsNode *cmln = nullptr; - if (base->filePath() == stepLists) { - cmln = base; - } else { - ProjectNode *pcmln = base->projectNode(stepLists); - cmln = static_cast(pcmln); - } + CMakeListsNode *result = root; - if (!cmln) { - stepDir = stepDir.parentDir(); - if (stepDir.isEmpty()) - return nullptr; + QString relativePathElement; + while (!relative.isEmpty()) { + const QString nextDirPath = result->filePath().parentDir().toString(); + Utils::FileName nextFullPath; + // Some directory may not contain CMakeLists.txt file, skip it: + do { + relativePathElement += '/' + relative.takeFirst(); + nextFullPath = Utils::FileName::fromString(nextDirPath + relativePathElement + "/CMakeLists.txt"); + } while (!nextFullPath.exists() && !relative.isEmpty()); + result = static_cast(result->projectNode(nextFullPath)); + // Intermediate directory can contain CMakeLists.txt file + // that is not a part of the root node, skip it: + if (!result && !relative.isEmpty()) { + result = root; } else { - if (cmln->filePath().parentDir() == dir) - return cmln; - stepDir = dir; - base = cmln; + relativePathElement.clear(); } + QTC_ASSERT(result, return nullptr); } + return result; } static CMakeProjectNode *findOrCreateProjectNode(CMakeListsNode *root, const Utils::FileName &dir, @@ -653,18 +617,16 @@ CMakeProjectNode *pn = static_cast(cmln->projectNode(projectName)); if (!pn) { pn = new CMakeProjectNode(projectName); - cmln->addProjectNodes({ pn }); + cmln->addNode(pn); } pn->setDisplayName(displayName); return pn; } -QSet ServerModeReader::updateProjects(CMakeListsNode *root, - const QList &projects, - const QList &allFiles) +void ServerModeReader::addProjects(CMakeListsNode *root, + const QList &projects, + const QList &allFiles) { - QSet usedNodes; - QHash> includeFiles; for (const FileNode *f : allFiles) { if (f->fileType() != FileType::Header) @@ -675,15 +637,13 @@ for (const Project *p : projects) { CMakeProjectNode *pNode = findOrCreateProjectNode(root, p->sourceDirectory, p->name); QTC_ASSERT(pNode, continue); - usedNodes.insert(pNode); // Mark as leaf to keep. - - usedNodes.unite(updateTargets(root, p->targets, includeFiles)); + QTC_ASSERT(root, continue); + addTargets(root, p->targets, includeFiles); } - return usedNodes; } static CMakeTargetNode *findOrCreateTargetNode(CMakeListsNode *root, const Utils::FileName &dir, - const QString &displayName) + const QString &displayName) { CMakeListsNode *cmln = findCMakeNode(root, dir); QTC_ASSERT(cmln, return nullptr); @@ -694,32 +654,29 @@ CMakeTargetNode *tn = static_cast(cmln->projectNode(targetName)); if (!tn) { tn = new CMakeTargetNode(targetName); - cmln->addProjectNodes({ tn }); + cmln->addNode(tn); } tn->setDisplayName(displayName); return tn; } -QSet ServerModeReader::updateTargets(CMakeListsNode *root, - const QList &targets, - const QHash> &headers) +void ServerModeReader::addTargets(CMakeListsNode *root, + const QList &targets, + const QHash> &headers) { - QSet usedNodes; for (const Target *t : targets) { CMakeTargetNode *tNode = findOrCreateTargetNode(root, t->sourceDirectory, t->name); + QTC_ASSERT(tNode, qDebug() << "No target node for" << t->sourceDirectory << t->name; return); tNode->setTargetInformation(t->artifacts, t->type); - usedNodes.insert(tNode); // always keep the target node: FileGroups use buildTree! - - updateFileGroups(tNode, t->sourceDirectory, t->buildDirectory, t->fileGroups, headers); + addFileGroups(tNode, t->sourceDirectory, t->buildDirectory, t->fileGroups, headers); } - return usedNodes; } -void ServerModeReader::updateFileGroups(ProjectNode *targetRoot, - const Utils::FileName &sourceDirectory, - const Utils::FileName &buildDirectory, - const QList &fileGroups, - const QHash> &headers) +void ServerModeReader::addFileGroups(ProjectNode *targetRoot, + const Utils::FileName &sourceDirectory, + const Utils::FileName &buildDirectory, + const QList &fileGroups, + const QHash> &headers) { QList toList; QSet alreadyListed; @@ -735,18 +692,23 @@ toList.append(newFileNodes); // Add scanned header files: + const FileNameList headerPaths = headers.keys(); for (const IncludePath *i : f->includePaths) { - const QList &headerFiles = headers.value(i->path); - const QList unseenHeaders = Utils::filtered(headerFiles, [&alreadyListed](const FileNode *fn) { - const int count = alreadyListed.count(); - alreadyListed.insert(fn->filePath()); - return count != alreadyListed.count(); - }); - toList.append(Utils::transform(unseenHeaders, [](const FileNode *fn) -> FileNode * { - auto copy = new FileNode(fn->filePath(), fn->fileType(), fn->isGenerated()); - copy->setEnabled(false); - return copy; - })); + for (const FileName &hp : headerPaths) { + if (hp != i->path && !hp.isChildOf(i->path)) + continue; + const QList &headerFiles = headers.value(hp); + const QList unseenHeaders = Utils::filtered(headerFiles, [&alreadyListed](const FileNode *fn) { + const int count = alreadyListed.count(); + alreadyListed.insert(fn->filePath()); + return count != alreadyListed.count(); + }); + toList.append(Utils::transform(unseenHeaders, [](const FileNode *fn) { + auto copy = new FileNode(fn->filePath(), fn->fileType(), fn->isGenerated()); + copy->setEnabled(false); + return copy; + })); + } } } @@ -763,15 +725,9 @@ otherFileNodes.append(fn); } - QList toKeep; - toKeep.append(setupCMakeVFolder(targetRoot, sourceDirectory, 1000, tr(""), sourceFileNodes)); - toKeep.append(setupCMakeVFolder(targetRoot, buildDirectory, 100, tr(""), buildFileNodes)); - toKeep.append(setupCMakeVFolder(targetRoot, Utils::FileName(), 10, tr(""), otherFileNodes)); - - const QList toDelete - = filtered(targetRoot->folderNodes(), [&toKeep](FolderNode *fn) { return !toKeep.contains(fn); }); - if (!toDelete.isEmpty()) - targetRoot->removeFolderNodes(toDelete); + addCMakeVFolder(targetRoot, sourceDirectory, 1000, tr(""), sourceFileNodes); + addCMakeVFolder(targetRoot, buildDirectory, 100, tr(""), buildFileNodes); + addCMakeVFolder(targetRoot, Utils::FileName(), 10, tr(""), otherFileNodes); } } // namespace Internal diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/servermodereader.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/servermodereader.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/servermodereader.h 2017-01-16 23:14:38.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/servermodereader.h 2017-02-26 23:55:33.000000000 +0000 @@ -28,18 +28,8 @@ #include "builddirreader.h" #include "servermode.h" -#include - -#include -#include -#include - #include -QT_FORWARD_DECLARE_CLASS(QLocalSocket); - -namespace Utils { class QtcProcess; } - namespace CMakeProjectManager { namespace Internal { @@ -66,7 +56,7 @@ CMakeConfig takeParsedConfiguration() final; void generateProjectTree(CMakeListsNode *root, const QList &allFiles) override; - QSet updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) final; + void updateCodeModel(CppTools::RawProjectParts &rpps) final; protected: void handleReply(const QVariantMap &data, const QString &inReplyTo); @@ -120,19 +110,16 @@ void extractCMakeInputsData(const QVariantMap &data); void extractCacheData(const QVariantMap &data); - QSet updateCMakeLists(CMakeListsNode *root, - const QList &cmakeLists); - QSet updateProjects(CMakeListsNode *root, - const QList &projects, - const QList &allFiles); - QSet updateTargets(CMakeListsNode *root, - const QList &targets, - const QHash> &headers); - void updateFileGroups(ProjectExplorer::ProjectNode *targetRoot, - const Utils::FileName &sourceDirectory, - const Utils::FileName &buildDirectory, - const QList &fileGroups, - const QHash> &headers); + void addCMakeLists(CMakeListsNode *root, const QList &cmakeLists); + void addProjects(CMakeListsNode *root, const QList &projects, + const QList &allFiles); + void addTargets(CMakeListsNode *root, const QList &targets, + const QHash> &headers); + void addFileGroups(ProjectExplorer::ProjectNode *targetRoot, + const Utils::FileName &sourceDirectory, + const Utils::FileName &buildDirectory, + const QList &fileGroups, + const QHash> &headers); bool m_hasData = false; diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/simpleservermorereader.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/simpleservermorereader.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/simpleservermorereader.cpp 2017-01-16 23:14:38.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/simpleservermorereader.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -13,26 +13,14 @@ namespace CMakeProjectManager { namespace Internal { -static void sanitizeTree(CMakeListsNode *root) -{ - QSet uniqueFileNames; - QSet uniqueNodes; - foreach (FileNode *fn, root->recursiveFileNodes()) { - const int count = uniqueFileNames.count(); - uniqueFileNames.insert(fn->filePath()); - if (count != uniqueFileNames.count()) - uniqueNodes.insert(static_cast(fn)); - } - root->trim(uniqueNodes); - root->removeProjectNodes(root->projectNodes()); // Remove all project nodes -} - void SimpleServerMoreReader::generateProjectTree(CMakeListsNode *root, const QList &allFiles) { - if (m_projects.empty()) - return; + const Project *topLevel = Utils::findOrDefault(m_projects, [this](const Project *p) { + return m_parameters.sourceDirectory == p->sourceDirectory; + }); + if (topLevel) + root->setDisplayName(topLevel->name); - root->setDisplayName(m_projects.at(0)->name); // Compose sources list from the CMake data QList files = m_cmakeInputsFileNodes; @@ -59,13 +47,22 @@ m_cmakeInputsFileNodes.clear(); // Clean out, they are not going to be used anymore! QList added; - QList deleted; // Unused! - ProjectExplorer::compareSortedLists(files, allFiles, deleted, added, Node::sortByPath); - - QList fileNodes = files + Utils::transform(added, [](const FileNode *fn) { return new FileNode(*fn); }); + std::set_difference( + allFiles.begin(), + allFiles.end(), + files.begin(), + files.end(), + std::back_inserter(added), + Node::sortByPath + ); + + QList fileNodes = files + Utils::transform(added, [](const FileNode *fn) { + auto toAdd = new FileNode(fn->filePath(), fn->fileType(), fn->isGenerated()); + toAdd->setEnabled(fn->isEnabled()); + return toAdd; + }); - sanitizeTree(root); // Filter out duplicate nodes that e.g. the servermode reader introduces: - root->buildTree(fileNodes, m_parameters.sourceDirectory); + root->makeTree(fileNodes, m_parameters.sourceDirectory); } } // namespace Internal diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/tealeafreader.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/tealeafreader.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/tealeafreader.cpp 2017-01-16 23:14:38.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/tealeafreader.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -25,6 +25,7 @@ #include "tealeafreader.h" +#include "builddirmanager.h" #include "cmakebuildconfiguration.h" #include "cmakecbpparser.h" #include "cmakekitinformation.h" @@ -39,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -222,15 +222,16 @@ CMakeConfig TeaLeafReader::takeParsedConfiguration() { - CMakeConfig result; FileName cacheFile = m_parameters.buildDirectory; cacheFile.appendPath(QLatin1String("CMakeCache.txt")); - if (!cacheFile.exists()) - return result; QString errorMessage; - result = CMakeConfigItem::itemsFromFile(cacheFile, &errorMessage); - if (!errorMessage.isEmpty()) + CMakeConfig result = BuildDirManager::parseConfiguration(cacheFile, &errorMessage); + + if (!errorMessage.isEmpty()) { emit errorOccured(errorMessage); + return { }; + } + const FileName sourceOfBuildDir = FileName::fromUtf8(CMakeConfigItem::valueOf("CMAKE_HOME_DIRECTORY", result)); const FileName canonicalSourceOfBuildDir = FileUtils::canonicalPath(sourceOfBuildDir); @@ -239,24 +240,11 @@ emit errorOccured(tr("The build directory is not for %1 but for %2") .arg(canonicalSourceOfBuildDir.toUserOutput(), canonicalSourceDirectory.toUserOutput())); + return { }; } return result; } -static void sanitizeTree(CMakeListsNode *root) -{ - QSet uniqueFileNames; - QSet uniqueNodes; - foreach (FileNode *fn, root->recursiveFileNodes()) { - const int count = uniqueFileNames.count(); - uniqueFileNames.insert(fn->filePath()); - if (count != uniqueFileNames.count()) - uniqueNodes.insert(static_cast(fn)); - } - root->trim(uniqueNodes); - root->removeProjectNodes(root->projectNodes()); // Remove all project nodes -} - void TeaLeafReader::generateProjectTree(CMakeListsNode *root, const QList &allFiles) { root->setDisplayName(m_projectName); @@ -284,10 +272,6 @@ m_watchedFiles.insert(cm); } - QList added; - QList deleted; // Unused! - ProjectExplorer::compareSortedLists(m_files, allFiles, deleted, added, Node::sortByPath); - // FIXME: keep this code commented. It is planed to make configurable behavior to display // all files or only CMake one #if 0 @@ -302,20 +286,32 @@ const QList allIncludePaths = allIncludePathSet.toList(); const QList missingHeaders - = Utils::filtered(added, [&allIncludePaths](const FileNode *fn) -> bool { + = Utils::filtered(allFiles, [&allIncludePaths](const FileNode *fn) -> bool { if (fn->fileType() != FileType::Header) return false; return Utils::contains(allIncludePaths, [fn](const FileName &inc) { return fn->filePath().isChildOf(inc); }); }); - QList fileNodes = m_files + Utils::transform(missingHeaders, [](const FileNode *fn) { return new FileNode(*fn); }); + QList fileNodes = m_files + Utils::transform(missingHeaders, [](const FileNode *fn) { return new FileNode(fn->filePath(), fn->fileType(), fn->isGenerated()); }); #else - QList fileNodes = m_files + Utils::transform(added, [](const FileNode *fn) { return new FileNode(*fn); }); + QList added; + std::set_difference( + allFiles.begin(), + allFiles.end(), + m_files.begin(), + m_files.end(), + std::back_inserter(added), + Node::sortByPath + ); + QList fileNodes = m_files + Utils::transform(added, [](const FileNode *fn) { + auto toAdd = new FileNode(fn->filePath(), fn->fileType(), fn->isGenerated()); + toAdd->setEnabled(fn->isEnabled()); + return toAdd; + }); #endif - sanitizeTree(root); // Filter out duplicate nodes that e.g. the servermode reader introduces: - root->buildTree(fileNodes, m_parameters.sourceDirectory); + root->makeTree(fileNodes, m_parameters.sourceDirectory); m_files.clear(); // Some of the FileNodes in files() were deleted! } @@ -334,9 +330,8 @@ } } -QSet TeaLeafReader::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) +void TeaLeafReader::updateCodeModel(CppTools::RawProjectParts &rpps) { - QSet languages; const ToolChain *tcCxx = ToolChainManager::findToolChain(m_parameters.cxxToolChainId); const ToolChain *tcC = ToolChainManager::findToolChain(m_parameters.cToolChainId); const FileName sysroot = m_parameters.sysRoot; @@ -350,8 +345,8 @@ // CMake shuffles the include paths that it reports via the CodeBlocks generator // So remove the toolchain include paths, so that at least those end up in the correct // place. - auto cxxflags = getFlagsFor(cbt, targetDataCacheCxx, ToolChain::Language::Cxx); - auto cflags = getFlagsFor(cbt, targetDataCacheC, ToolChain::Language::C); + auto cxxflags = getFlagsFor(cbt, targetDataCacheCxx, ProjectExplorer::Constants::CXX_LANGUAGE_ID); + auto cflags = getFlagsFor(cbt, targetDataCacheC, ProjectExplorer::Constants::C_LANGUAGE_ID); QSet tcIncludes; QStringList includePaths; if (tcCxx || tcC) { @@ -361,20 +356,24 @@ includePaths = transform(cbt.includeFiles, &FileName::toString); } includePaths += m_parameters.buildDirectory.toString(); - ppBuilder.setIncludePaths(includePaths); - ppBuilder.setCFlags(cflags); - ppBuilder.setCxxFlags(cxxflags); - ppBuilder.setDefines(cbt.defines); - ppBuilder.setDisplayName(cbt.title); - - const QSet partLanguages - = QSet::fromList(ppBuilder.createProjectPartsForFiles( - transform(cbt.files, [](const FileName &fn) { return fn.toString(); }))); + CppTools::RawProjectPart rpp; + rpp.setProjectFile(QString()); // No project file information available! + rpp.setIncludePaths(includePaths); + + CppTools::RawProjectPartFlags cProjectFlags; + cProjectFlags.commandLineFlags = cflags; + rpp.setFlagsForC(cProjectFlags); + + CppTools::RawProjectPartFlags cxxProjectFlags; + cxxProjectFlags.commandLineFlags = cxxflags; + rpp.setFlagsForCxx(cxxProjectFlags); + + rpp.setDefines(cbt.defines); + rpp.setDisplayName(cbt.title); + rpp.setFiles(transform(cbt.files, [](const FileName &fn) { return fn.toString(); })); - languages.unite(partLanguages); + rpps.append(rpp); } - return languages; - } void TeaLeafReader::cleanUpProcess() @@ -553,7 +552,7 @@ QStringList TeaLeafReader::getFlagsFor(const CMakeBuildTarget &buildTarget, QHash &cache, - ToolChain::Language lang) + Id lang) { // check cache: auto it = cache.constFind(buildTarget.title); @@ -572,20 +571,16 @@ bool TeaLeafReader::extractFlagsFromMake(const CMakeBuildTarget &buildTarget, QHash &cache, - ToolChain::Language lang) + Id lang) { QString flagsPrefix; - switch (lang) - { - case ToolChain::Language::Cxx: + + if (lang == ProjectExplorer::Constants::CXX_LANGUAGE_ID) flagsPrefix = QLatin1String("CXX_FLAGS ="); - break; - case ToolChain::Language::C: + else if (lang == ProjectExplorer::Constants::C_LANGUAGE_ID) flagsPrefix = QLatin1String("C_FLAGS ="); - break; - default: + else return false; - } QString makeCommand = buildTarget.makeCommand.toString(); int startIndex = makeCommand.indexOf('\"'); @@ -631,24 +626,19 @@ bool TeaLeafReader::extractFlagsFromNinja(const CMakeBuildTarget &buildTarget, QHash &cache, - ProjectExplorer::ToolChain::Language lang) + Id lang) { Q_UNUSED(buildTarget) if (!cache.isEmpty()) // We fill the cache in one go! return false; QString compilerPrefix; - switch (lang) - { - case ToolChain::Language::Cxx: + if (lang == ProjectExplorer::Constants::CXX_LANGUAGE_ID) compilerPrefix = QLatin1String("CXX_COMPILER"); - break; - case ToolChain::Language::C: + else if (lang == ProjectExplorer::Constants::C_LANGUAGE_ID) compilerPrefix = QLatin1String("C_COMPILER"); - break; - default: + else return false; - } // Attempt to find build.ninja file and obtain FLAGS (CXX_FLAGS/C_FLAGS) from there if no suitable flags.make were // found diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/tealeafreader.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/tealeafreader.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/tealeafreader.h 2017-01-16 23:14:38.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/tealeafreader.h 2017-02-26 23:55:33.000000000 +0000 @@ -58,7 +58,7 @@ CMakeConfig takeParsedConfiguration() final; void generateProjectTree(CMakeListsNode *root, const QList &allFiles) final; - QSet updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) final; + void updateCodeModel(CppTools::RawProjectParts &rpps) final; private: void cleanUpProcess(); @@ -70,9 +70,9 @@ void processCMakeOutput(); void processCMakeError(); - QStringList getFlagsFor(const CMakeBuildTarget &buildTarget, QHash &cache, ProjectExplorer::ToolChain::Language lang); - bool extractFlagsFromMake(const CMakeBuildTarget &buildTarget, QHash &cache, ProjectExplorer::ToolChain::Language lang); - bool extractFlagsFromNinja(const CMakeBuildTarget &buildTarget, QHash &cache, ProjectExplorer::ToolChain::Language lang); + QStringList getFlagsFor(const CMakeBuildTarget &buildTarget, QHash &cache, Core::Id lang); + bool extractFlagsFromMake(const CMakeBuildTarget &buildTarget, QHash &cache, Core::Id lang); + bool extractFlagsFromNinja(const CMakeBuildTarget &buildTarget, QHash &cache, Core::Id lang); Utils::QtcProcess *m_cmakeProcess = nullptr; diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/treescanner.cpp qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/treescanner.cpp --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/treescanner.cpp 2017-01-16 23:14:38.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/treescanner.cpp 2017-02-26 23:55:33.000000000 +0000 @@ -50,6 +50,14 @@ 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::FileName &directory) { if (!m_futureWatcher.isFinished()) diff -Nru qtcreator-git-plugin-cmake2-20170117.517-3d39bac/treescanner.h qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/treescanner.h --- qtcreator-git-plugin-cmake2-20170117.517-3d39bac/treescanner.h 2017-01-16 23:14:38.000000000 +0000 +++ qtcreator-git-plugin-cmake2-20170227.573-9fcb7a7/treescanner.h 2017-02-26 23:55:33.000000000 +0000 @@ -53,6 +53,7 @@ using FileTypeFactory = std::function; explicit TreeScanner(QObject *parent = nullptr); + ~TreeScanner(); // Start scanning in given directory bool asyncScanForFiles(const Utils::FileName& directory);