From 8c90998fff6ec3cf85ad87b56175e86b6f0a93d0 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Mon, 6 Feb 2017 16:59:53 +0100 Subject: [PATCH] CppTools/ProjectManagers: Reduce ui blocking when loading projects ${AnyProject}::updateCppCodeModel() did two potentially not that cheap operations in the ui thread: (1) Querying the MimeDatabase for the mime type for the source files of the project. In 99.9% of the cases no files need to be read for this as the file extension will resolve the type. The expensiveness comes from the sheer number of files that can occur. (2) Calling compilers with the "(sub)project's compiler command line" to determine the macros. While the caches avoid redundant calls, the number of the unique compiler calls makes this still a ui-freezing experience. These two operations are moved into a worker thread. For this, the expensive compiler calls are encapsulated in thread safe lambdas ("runners") in order to keep the "mutexed" data minimal. The original API calls of the toolchains are implemented in terms of the runners. While adapting the project managers, remove also the calls to setProjectLanguage(). These are redundant because all of the project managers already set a proper value in the constructor. Also, currently there is no need (client) to report back detection of C sources in project parts. This also keeps CppProjectUpdater simple. There is still room for improvement: * Run the compiler calls in parallel instead of sequence. * Ensure that the mime type for a file is determined exactly once. Change-Id: I2efc4e132ee88e3c8f264012ec8fafe3d86c404f Reviewed-by: Tobias Hunger --- .../autotoolsproject.cpp | 46 +-- .../autotoolsproject.h | 4 +- .../cmakeprojectmanager/builddirmanager.cpp | 7 +- .../cmakeprojectmanager/builddirmanager.h | 4 +- .../cmakeprojectmanager/builddirreader.h | 6 +- .../cmakebuildconfiguration.cpp | 4 +- .../cmakebuildconfiguration.h | 5 +- .../cmakeprojectmanager/cmakeproject.cpp | 26 +- .../cmakeprojectmanager/cmakeproject.h | 4 +- .../cmakeprojectmanager/servermodereader.cpp | 28 +- .../cmakeprojectmanager/servermodereader.h | 2 +- .../cmakeprojectmanager/tealeafreader.cpp | 31 +- .../cmakeprojectmanager/tealeafreader.h | 2 +- .../cpptools/builtinindexingsupport.cpp | 41 +-- src/plugins/cpptools/builtinindexingsupport.h | 9 +- .../cpptools/cppbaseprojectpartbuilder.cpp | 308 ------------------ src/plugins/cpptools/cppindexingsupport.h | 5 +- src/plugins/cpptools/cppmodelmanager.cpp | 53 ++- src/plugins/cpptools/cppmodelmanager.h | 11 +- .../cpptools/cppprojectfilecategorizer.cpp | 7 +- .../cpptools/cppprojectfilecategorizer.h | 4 +- .../cpptools/cppprojectinfogenerator.cpp | 273 ++++++++++++++++ .../cpptools/cppprojectinfogenerator.h | 59 ++++ src/plugins/cpptools/cppprojectupdater.cpp | 111 +++++++ ...projectinterface.h => cppprojectupdater.h} | 62 ++-- src/plugins/cpptools/cpprawprojectpart.cpp | 124 +++++++ ...ojectpartbuilder.h => cpprawprojectpart.h} | 67 ++-- src/plugins/cpptools/cpptools.pro | 11 +- src/plugins/cpptools/cpptools.qbs | 11 +- .../cpptools/cpptoolsunittestfiles.pri | 5 +- src/plugins/cpptools/projectinfo.cpp | 39 ++- src/plugins/cpptools/projectinfo.h | 46 +++ src/plugins/cpptools/projectpartbuilder.cpp | 143 -------- .../genericprojectmanager/genericproject.cpp | 37 ++- .../genericprojectmanager/genericproject.h | 4 +- src/plugins/nim/project/nimtoolchain.cpp | 10 + src/plugins/nim/project/nimtoolchain.h | 2 + .../projectexplorer/abstractmsvctoolchain.cpp | 71 +++- .../projectexplorer/abstractmsvctoolchain.h | 8 + .../projectexplorer/customtoolchain.cpp | 81 +++-- src/plugins/projectexplorer/customtoolchain.h | 2 + src/plugins/projectexplorer/gcctoolchain.cpp | 288 +++++++++++----- src/plugins/projectexplorer/gcctoolchain.h | 63 +++- src/plugins/projectexplorer/msvctoolchain.cpp | 1 + src/plugins/projectexplorer/msvctoolchain.h | 1 + src/plugins/projectexplorer/toolchain.h | 14 +- .../projectexplorer/toolchainmanager.cpp | 2 + src/plugins/qbsprojectmanager/qbsproject.cpp | 75 +++-- src/plugins/qbsprojectmanager/qbsproject.h | 5 +- .../qmakeprojectmanager/qmakeproject.cpp | 52 +-- .../qmakeprojectmanager/qmakeproject.h | 3 +- src/plugins/qnx/qnxtoolchain.cpp | 40 +-- src/plugins/qnx/qnxtoolchain.h | 2 - .../mockup/projectexplorer/kitinformation.h | 16 +- tests/unit/mockup/projectexplorer/toolchain.h | 68 ++++ .../cppbaseprojectpartbuilder-test.cpp | 243 -------------- .../unittest/cppprojectinfogenerator-test.cpp | 170 ++++++++++ tests/unit/unittest/unittest.pro | 2 +- 58 files changed, 1667 insertions(+), 1151 deletions(-) delete mode 100644 src/plugins/cpptools/cppbaseprojectpartbuilder.cpp create mode 100644 src/plugins/cpptools/cppprojectinfogenerator.cpp create mode 100644 src/plugins/cpptools/cppprojectinfogenerator.h create mode 100644 src/plugins/cpptools/cppprojectupdater.cpp rename src/plugins/cpptools/{cppprojectinterface.h => cppprojectupdater.h} (53%) create mode 100644 src/plugins/cpptools/cpprawprojectpart.cpp rename src/plugins/cpptools/{cppbaseprojectpartbuilder.h => cpprawprojectpart.h} (56%) delete mode 100644 src/plugins/cpptools/projectpartbuilder.cpp rename src/plugins/cpptools/projectpartbuilder.h => tests/unit/mockup/projectexplorer/kitinformation.h (79%) create mode 100644 tests/unit/mockup/projectexplorer/toolchain.h delete mode 100644 tests/unit/unittest/cppbaseprojectpartbuilder-test.cpp create mode 100644 tests/unit/unittest/cppprojectinfogenerator-test.cpp diff --git a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp index 75d82c2e65f..eb83136e68d 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp +++ b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include #include #include @@ -70,7 +70,8 @@ using namespace AutotoolsProjectManager::Internal; using namespace ProjectExplorer; AutotoolsProject::AutotoolsProject(AutotoolsManager *manager, const QString &fileName) : - m_fileWatcher(new Utils::FileSystemWatcher(this)) + m_fileWatcher(new Utils::FileSystemWatcher(this)), + m_cppCodeModelUpdater(new CppTools::CppProjectUpdater(this)) { setId(Constants::AUTOTOOLS_PROJECT_ID); setProjectManager(manager); @@ -86,6 +87,8 @@ AutotoolsProject::AutotoolsProject(AutotoolsManager *manager, const QString &fil AutotoolsProject::~AutotoolsProject() { + delete m_cppCodeModelUpdater; + setRootProjectNode(0); if (m_makefileParserThread != 0) { @@ -270,42 +273,47 @@ static QStringList filterIncludes(const QString &absSrc, const QString &absBuild void AutotoolsProject::updateCppCodeModel() { - CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance(); + const Kit *k = nullptr; + if (Target *target = activeTarget()) + k = target->kit(); + else + k = KitManager::defaultKit(); + QTC_ASSERT(k, return); - m_codeModelFuture.cancel(); - CppTools::ProjectInfo pInfo(this); - CppTools::ProjectPartBuilder ppBuilder(pInfo); + ToolChain *cToolChain + = ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::C_LANGUAGE_ID); + ToolChain *cxxToolChain + = ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::CXX_LANGUAGE_ID); - ppBuilder.setProjectFile(projectFilePath().toString()); + m_cppCodeModelUpdater->cancel(); + + CppTools::RawProjectPart rpp; + rpp.setProjectFile(projectFilePath().toString()); CppTools::ProjectPart::QtVersion activeQtVersion = CppTools::ProjectPart::NoQt; - if (QtSupport::BaseQtVersion *qtVersion = - QtSupport::QtKitInformation::qtVersion(activeTarget()->kit())) { + if (QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(k)) { if (qtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0)) activeQtVersion = CppTools::ProjectPart::Qt4; else activeQtVersion = CppTools::ProjectPart::Qt5; } - ppBuilder.setQtVersion(activeQtVersion); + rpp.setQtVersion(activeQtVersion); const QStringList cflags = m_makefileParserThread->cflags(); QStringList cxxflags = m_makefileParserThread->cxxflags(); if (cxxflags.isEmpty()) cxxflags = cflags; - ppBuilder.setCFlags(cflags); - ppBuilder.setCxxFlags(cxxflags); + rpp.setFlagsForC({cToolChain, cflags}); + rpp.setFlagsForCxx({cxxToolChain, cxxflags}); const QString absSrc = projectDirectory().toString(); const Target *target = activeTarget(); const QString absBuild = (target && target->activeBuildConfiguration()) ? target->activeBuildConfiguration()->buildDirectory().toString() : QString(); - ppBuilder.setIncludePaths(filterIncludes(absSrc, absBuild, m_makefileParserThread->includePaths())); - ppBuilder.setDefines(m_makefileParserThread->defines()); + rpp.setIncludePaths(filterIncludes(absSrc, absBuild, m_makefileParserThread->includePaths())); + rpp.setDefines(m_makefileParserThread->defines()); + rpp.setFiles(m_files); - const QList languages = ppBuilder.createProjectPartsForFiles(m_files); - foreach (Core::Id language, languages) - setProjectLanguage(language, true); - - m_codeModelFuture = modelManager->updateProjectInfo(pInfo); + m_cppCodeModelUpdater->update({this, cToolChain, cxxToolChain, k, {rpp}}); } diff --git a/src/plugins/autotoolsprojectmanager/autotoolsproject.h b/src/plugins/autotoolsprojectmanager/autotoolsproject.h index d98686a81f0..7ee1017ae38 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsproject.h +++ b/src/plugins/autotoolsprojectmanager/autotoolsproject.h @@ -37,6 +37,8 @@ QT_FORWARD_DECLARE_CLASS(QDir) namespace Utils { class FileSystemWatcher; } +namespace CppTools { class CppProjectUpdater; } + namespace ProjectExplorer { class Node; class FolderNode; @@ -119,7 +121,7 @@ private: /// Responsible for parsing the makefiles asynchronously in a thread MakefileParserThread *m_makefileParserThread = nullptr; - QFuture m_codeModelFuture; + CppTools::CppProjectUpdater *m_cppCodeModelUpdater = nullptr; }; } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.cpp b/src/plugins/cmakeprojectmanager/builddirmanager.cpp index bbeb34deed2..9b57ce0ab59 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.cpp +++ b/src/plugins/cmakeprojectmanager/builddirmanager.cpp @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -289,10 +288,10 @@ void BuildDirManager::generateProjectTree(CMakeListsNode *root, const QListaddNode(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() diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.h b/src/plugins/cmakeprojectmanager/builddirmanager.h index 0bfac24365c..85b95884648 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.h +++ b/src/plugins/cmakeprojectmanager/builddirmanager.h @@ -37,8 +37,6 @@ #include #include -namespace CppTools { class ProjectPartBuilder; } - namespace ProjectExplorer { class FileNode; class IOutputParser; @@ -73,7 +71,7 @@ public: void generateProjectTree(CMakeListsNode *root, const QList &allFiles); - QSet updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder); + void updateCodeModel(CppTools::RawProjectParts &rpps); QList buildTargets() const; CMakeConfig parsedConfiguration() const; diff --git a/src/plugins/cmakeprojectmanager/builddirreader.h b/src/plugins/cmakeprojectmanager/builddirreader.h index f28c974ecca..b13de736f20 100644 --- a/src/plugins/cmakeprojectmanager/builddirreader.h +++ b/src/plugins/cmakeprojectmanager/builddirreader.h @@ -29,6 +29,8 @@ #include "cmakeproject.h" #include "cmaketool.h" +#include + #include #include #include @@ -36,8 +38,6 @@ #include #include -namespace CppTools { class ProjectPartBuilder; } - namespace CMakeProjectManager { namespace Internal { @@ -97,7 +97,7 @@ public: 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 --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 10468ac7900..960bb9c59a8 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -220,9 +220,9 @@ void CMakeBuildConfiguration::generateProjectTree(CMakeListsNode *root, 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, diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h index ce561412b1c..1afd9c33d7b 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h @@ -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 @@ public: 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, diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 6d022141eaf..d89e4e86a02 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -33,11 +33,11 @@ #include "cmakeprojectmanager.h" #include -#include +#include +#include #include #include #include -#include #include #include #include @@ -74,6 +74,7 @@ using namespace Internal; \class CMakeProject */ CMakeProject::CMakeProject(CMakeManager *manager, const FileName &fileName) + : m_cppCodeModelUpdater(new CppTools::CppProjectUpdater(this)) { setId(CMakeProjectManager::Constants::CMAKEPROJECT_ID); setProjectManager(manager); @@ -133,8 +134,8 @@ CMakeProject::~CMakeProject() future.cancel(); future.waitForFinished(); } + delete m_cppCodeModelUpdater; setRootProjectNode(nullptr); - m_codeModelFuture.cancel(); qDeleteAll(m_extraCompilers); qDeleteAll(m_allFiles); } @@ -165,10 +166,6 @@ void CMakeProject::updateProjectData(CMakeBuildConfiguration *bc) 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)) @@ -177,14 +174,17 @@ void CMakeProject::updateProjectData(CMakeBuildConfiguration *bc) 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(); diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h index 959aea47d30..7d8711c9f19 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.h +++ b/src/plugins/cmakeprojectmanager/cmakeproject.h @@ -43,6 +43,8 @@ QT_BEGIN_NAMESPACE class QFileSystemWatcher; QT_END_NAMESPACE +namespace CppTools { class CppProjectUpdater; } + namespace CMakeProjectManager { namespace Internal { @@ -134,7 +136,7 @@ private: // 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; diff --git a/src/plugins/cmakeprojectmanager/servermodereader.cpp b/src/plugins/cmakeprojectmanager/servermodereader.cpp index 23db99ff31d..c177e303b79 100644 --- a/src/plugins/cmakeprojectmanager/servermodereader.cpp +++ b/src/plugins/cmakeprojectmanager/servermodereader.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -287,9 +286,8 @@ void ServerModeReader::generateProjectTree(CMakeListsNode *root, 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; @@ -304,23 +302,29 @@ QSet ServerModeReader::updateCodeModel(CppTools::ProjectPartBuilder &p 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() + "/CMakeLists.txt"); - 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); - languages.unite(QSet::fromList(ppBuilder.createProjectPartsForFiles(transform(fg->sources, &FileName::toString)))); + CppTools::RawProjectPartFlags cxxProjectFlags; + cxxProjectFlags.commandLineFlags = flags; + rpp.setFlagsForCxx(cxxProjectFlags); + + rpp.setFiles(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) diff --git a/src/plugins/cmakeprojectmanager/servermodereader.h b/src/plugins/cmakeprojectmanager/servermodereader.h index fbefb58607c..36d75fc2ded 100644 --- a/src/plugins/cmakeprojectmanager/servermodereader.h +++ b/src/plugins/cmakeprojectmanager/servermodereader.h @@ -56,7 +56,7 @@ public: 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 handleReply(const QVariantMap &data, const QString &inReplyTo); diff --git a/src/plugins/cmakeprojectmanager/tealeafreader.cpp b/src/plugins/cmakeprojectmanager/tealeafreader.cpp index 836dad964d4..47ca819c1bd 100644 --- a/src/plugins/cmakeprojectmanager/tealeafreader.cpp +++ b/src/plugins/cmakeprojectmanager/tealeafreader.cpp @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -312,9 +311,8 @@ static void processCMakeIncludes(const CMakeBuildTarget &cbt, const ToolChain *t } } -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; @@ -339,21 +337,24 @@ QSet TeaLeafReader::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) includePaths = transform(cbt.includeFiles, &FileName::toString); } includePaths += m_parameters.buildDirectory.toString(); - ppBuilder.setProjectFile(QString()); // No project file information available! - ppBuilder.setIncludePaths(includePaths); - ppBuilder.setCFlags(cflags); - ppBuilder.setCxxFlags(cxxflags); - ppBuilder.setDefines(cbt.defines); - ppBuilder.setDisplayName(cbt.title); + CppTools::RawProjectPart rpp; + rpp.setProjectFile(QString()); // No project file information available! + rpp.setIncludePaths(includePaths); - const QSet partLanguages - = QSet::fromList(ppBuilder.createProjectPartsForFiles( - transform(cbt.files, [](const FileName &fn) { return fn.toString(); }))); + CppTools::RawProjectPartFlags cProjectFlags; + cProjectFlags.commandLineFlags = cflags; + rpp.setFlagsForC(cProjectFlags); - languages.unite(partLanguages); + 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(); })); + + rpps.append(rpp); } - return languages; - } void TeaLeafReader::cleanUpProcess() diff --git a/src/plugins/cmakeprojectmanager/tealeafreader.h b/src/plugins/cmakeprojectmanager/tealeafreader.h index 4eca9b0af83..3aa258580a5 100644 --- a/src/plugins/cmakeprojectmanager/tealeafreader.h +++ b/src/plugins/cmakeprojectmanager/tealeafreader.h @@ -58,7 +58,7 @@ public: 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(); diff --git a/src/plugins/cpptools/builtinindexingsupport.cpp b/src/plugins/cpptools/builtinindexingsupport.cpp index c2ae6096c08..eadcda99bd2 100644 --- a/src/plugins/cpptools/builtinindexingsupport.cpp +++ b/src/plugins/cpptools/builtinindexingsupport.cpp @@ -133,7 +133,9 @@ void classifyFiles(const QSet &files, QStringList *headers, QStringList } } -void indexFindErrors(QFutureInterface &future, const ParseParams params) +void indexFindErrors(QFutureInterface &indexingFuture, + const QFutureInterface &superFuture, + const ParseParams params) { QStringList sources, headers; classifyFiles(params.sourceFiles, &headers, &sources); @@ -146,9 +148,7 @@ void indexFindErrors(QFutureInterface &future, const ParseParams params) timer.start(); for (int i = 0, end = files.size(); i < end ; ++i) { - if (future.isPaused()) - future.waitForResume(); - if (future.isCanceled()) + if (indexingFuture.isCanceled() || superFuture.isCanceled()) break; const QString file = files.at(i); @@ -170,7 +170,7 @@ void indexFindErrors(QFutureInterface &future, const ParseParams params) document->releaseSourceAndAST(); - future.setProgressValue(files.size() - (files.size() - (i + 1))); + indexingFuture.setProgressValue(files.size() - (files.size() - (i + 1))); } const QTime format = QTime(0, 0, 0, 0).addMSecs(timer.elapsed() + 500); @@ -178,7 +178,9 @@ void indexFindErrors(QFutureInterface &future, const ParseParams params) qDebug("FindErrorsIndexing: Finished after %s.", qPrintable(time)); } -void index(QFutureInterface &future, const ParseParams params) +void index(QFutureInterface &indexingFuture, + const QFutureInterface &superFuture, + const ParseParams params) { QScopedPointer sourceProcessor(CppModelManager::createSourceProcessor()); sourceProcessor->setFileSizeLimitInMb(params.indexerFileSizeLimitInMb); @@ -205,10 +207,7 @@ void index(QFutureInterface &future, const ParseParams params) const CPlusPlus::LanguageFeatures defaultFeatures = CPlusPlus::LanguageFeatures::defaultFeatures(); for (int i = 0; i < files.size(); ++i) { - if (future.isPaused()) - future.waitForResume(); - - if (future.isCanceled()) + if (indexingFuture.isCanceled() || superFuture.isCanceled()) break; const QString fileName = files.at(i); @@ -233,27 +232,29 @@ void index(QFutureInterface &future, const ParseParams params) sourceProcessor->setHeaderPaths(headerPaths); sourceProcessor->run(fileName); - future.setProgressValue(files.size() - sourceProcessor->todo().size()); + indexingFuture.setProgressValue(files.size() - sourceProcessor->todo().size()); if (isSourceFile) sourceProcessor->resetEnvironment(); } } -void parse(QFutureInterface &future, const ParseParams params) +void parse(QFutureInterface &indexingFuture, + const QFutureInterface &superFuture, + const ParseParams params) { const QSet &files = params.sourceFiles; if (files.isEmpty()) return; - future.setProgressRange(0, files.size()); + indexingFuture.setProgressRange(0, files.size()); if (FindErrorsIndexing) - indexFindErrors(future, params); + indexFindErrors(indexingFuture, superFuture, params); else - index(future, params); + index(indexingFuture, superFuture, params); - future.setProgressValue(files.size()); + indexingFuture.setProgressValue(files.size()); CppModelManager::instance()->finishedRefreshingSourceFiles(files); } @@ -345,8 +346,10 @@ BuiltinIndexingSupport::BuiltinIndexingSupport() BuiltinIndexingSupport::~BuiltinIndexingSupport() {} -QFuture BuiltinIndexingSupport::refreshSourceFiles(const QSet &sourceFiles, - CppModelManager::ProgressNotificationMode mode) +QFuture BuiltinIndexingSupport::refreshSourceFiles( + const QFutureInterface &superFuture, + const QSet &sourceFiles, + CppModelManager::ProgressNotificationMode mode) { CppModelManager *mgr = CppModelManager::instance(); @@ -357,7 +360,7 @@ QFuture BuiltinIndexingSupport::refreshSourceFiles(const QSet &so params.workingCopy.insert(mgr->configurationFileName(), mgr->definedMacros()); params.sourceFiles = sourceFiles; - QFuture result = Utils::runAsync(mgr->sharedThreadPool(), parse, params); + QFuture result = Utils::runAsync(mgr->sharedThreadPool(), parse, superFuture, params); if (m_synchronizer.futures().size() > 10) { QList > futures = m_synchronizer.futures(); diff --git a/src/plugins/cpptools/builtinindexingsupport.h b/src/plugins/cpptools/builtinindexingsupport.h index 82414338ecd..ad4f06d52e2 100644 --- a/src/plugins/cpptools/builtinindexingsupport.h +++ b/src/plugins/cpptools/builtinindexingsupport.h @@ -38,10 +38,11 @@ public: BuiltinIndexingSupport(); ~BuiltinIndexingSupport(); - virtual QFuture refreshSourceFiles(const QSet &sourceFiles, - CppModelManager::ProgressNotificationMode mode); - virtual SymbolSearcher *createSymbolSearcher(SymbolSearcher::Parameters parameters, - QSet fileNames); + QFuture refreshSourceFiles(const QFutureInterface &superFuture, + const QSet &sourceFiles, + CppModelManager::ProgressNotificationMode mode) override; + SymbolSearcher *createSymbolSearcher(SymbolSearcher::Parameters parameters, + QSet fileNames) override; public: static bool isFindErrorsIndexingActive(); diff --git a/src/plugins/cpptools/cppbaseprojectpartbuilder.cpp b/src/plugins/cpptools/cppbaseprojectpartbuilder.cpp deleted file mode 100644 index 194ed0df29a..00000000000 --- a/src/plugins/cpptools/cppbaseprojectpartbuilder.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "cppbaseprojectpartbuilder.h" - -#include "cppprojectfile.h" -#include "cppprojectfilecategorizer.h" -#include "projectinfo.h" - -#include -#include -#include - -#include - -namespace CppTools { - -BaseProjectPartBuilder::BaseProjectPartBuilder(ProjectInterface *project, - ProjectInfo &projectInfo) - : m_project(project) - , m_projectInfo(projectInfo) - , m_templatePart(new ProjectPart) -{ - QTC_CHECK(m_project); - m_templatePart->project = projectInfo.project(); - m_templatePart->displayName = m_project->displayName(); - m_templatePart->projectFile = m_project->projectFilePath(); -} - -void BaseProjectPartBuilder::setQtVersion(ProjectPart::QtVersion qtVersion) -{ - m_templatePart->qtVersion = qtVersion; -} - -void BaseProjectPartBuilder::setCFlags(const QStringList &flags) -{ - m_cFlags = flags; -} - -void BaseProjectPartBuilder::setCxxFlags(const QStringList &flags) -{ - m_cxxFlags = flags; -} - -void BaseProjectPartBuilder::setDefines(const QByteArray &defines) -{ - m_templatePart->projectDefines = defines; -} - -void BaseProjectPartBuilder::setHeaderPaths(const ProjectPartHeaderPaths &headerPaths) -{ - m_templatePart->headerPaths = headerPaths; -} - -void BaseProjectPartBuilder::setIncludePaths(const QStringList &includePaths) -{ - m_templatePart->headerPaths.clear(); - - foreach (const QString &includeFile, includePaths) { - ProjectPartHeaderPath hp(includeFile, ProjectPartHeaderPath::IncludePath); - - // The simple project managers are utterly ignorant of frameworks on OSX, and won't report - // framework paths. The work-around is to check if the include path ends in ".framework", - // and if so, add the parent directory as framework path. - if (includeFile.endsWith(QLatin1String(".framework"))) { - const int slashIdx = includeFile.lastIndexOf(QLatin1Char('/')); - if (slashIdx != -1) { - hp = ProjectPartHeaderPath(includeFile.left(slashIdx), - ProjectPartHeaderPath::FrameworkPath); - } - } - - m_templatePart->headerPaths += hp; - } -} - -void BaseProjectPartBuilder::setPreCompiledHeaders(const QStringList &preCompiledHeaders) -{ - m_templatePart->precompiledHeaders = preCompiledHeaders; -} - -void BaseProjectPartBuilder::setSelectedForBuilding(bool yesno) -{ - m_templatePart->selectedForBuilding = yesno; -} - -void BaseProjectPartBuilder::setProjectFile(const QString &projectFile) -{ - m_templatePart->projectFile = projectFile; -} - -void BaseProjectPartBuilder::setDisplayName(const QString &displayName) -{ - m_templatePart->displayName = displayName; -} - -void BaseProjectPartBuilder::setConfigFileName(const QString &configFileName) -{ - m_templatePart->projectConfigFile = configFileName; -} - -QList BaseProjectPartBuilder::createProjectPartsForFiles(const QStringList &filePaths, - FileClassifier fileClassifier) -{ - QList languages; - - ProjectFileCategorizer cat(m_templatePart->displayName, filePaths, fileClassifier); - - if (cat.hasParts()) { - if (cat.hasCxxSources()) { - languages += ProjectExplorer::Constants::CXX_LANGUAGE_ID; - createProjectPart(cat.cxxSources(), - cat.partName("C++"), - ProjectPart::LatestCxxVersion, - ProjectPart::NoExtensions); - } - - if (cat.hasObjcxxSources()) { - languages += ProjectExplorer::Constants::CXX_LANGUAGE_ID; - createProjectPart(cat.objcxxSources(), - cat.partName("Obj-C++"), - ProjectPart::LatestCxxVersion, - ProjectPart::ObjectiveCExtensions); - } - - if (cat.hasCSources()) { - languages += ProjectExplorer::Constants::C_LANGUAGE_ID; - createProjectPart(cat.cSources(), - cat.partName("C"), - ProjectPart::LatestCVersion, - ProjectPart::NoExtensions); - } - - if (cat.hasObjcSources()) { - languages += ProjectExplorer::Constants::C_LANGUAGE_ID; - createProjectPart(cat.objcSources(), - cat.partName("Obj-C"), - ProjectPart::LatestCVersion, - ProjectPart::ObjectiveCExtensions); - } - } - - return languages; -} - -namespace { - -class ToolChainEvaluator -{ -public: - ToolChainEvaluator(ProjectPart &projectPart, - const ToolChainInterface &toolChain) - : m_projectPart(projectPart) - , m_toolChain(toolChain) - , m_compilerFlags(toolChain.compilerFlags()) - { - } - - void evaluate() - { - mapLanguageVersion(); - mapLanguageExtensions(); - - addHeaderPaths(); - m_projectPart.toolchainDefines = m_toolChain.predefinedMacros(); - - m_projectPart.warningFlags = m_toolChain.warningFlags(); - m_projectPart.toolchainType = m_toolChain.type(); - m_projectPart.isMsvc2015Toolchain = m_toolChain.isMsvc2015Toolchain(); - m_projectPart.toolChainWordWidth = mapWordWith(m_toolChain.wordWidth()); - m_projectPart.toolChainTargetTriple = m_toolChain.targetTriple(); - } - -private: - static ProjectPart::ToolChainWordWidth mapWordWith(unsigned wordWidth) - { - return wordWidth == 64 - ? ProjectPart::WordWidth64Bit - : ProjectPart::WordWidth32Bit; - } - - void mapLanguageVersion() - { - using namespace ProjectExplorer; - ProjectPart::LanguageVersion &languageVersion = m_projectPart.languageVersion; - - if (m_compilerFlags & ToolChain::StandardC11) - languageVersion = ProjectPart::C11; - else if (m_compilerFlags & ToolChain::StandardC99) - languageVersion = ProjectPart::C99; - else if (m_compilerFlags & ToolChain::StandardCxx17) - languageVersion = ProjectPart::CXX17; - else if (m_compilerFlags & ToolChain::StandardCxx14) - languageVersion = ProjectPart::CXX14; - else if (m_compilerFlags & ToolChain::StandardCxx11) - languageVersion = ProjectPart::CXX11; - else if (m_compilerFlags & ToolChain::StandardCxx98) - languageVersion = ProjectPart::CXX98; - } - - void mapLanguageExtensions() - { - using namespace ProjectExplorer; - ProjectPart::LanguageExtensions &languageExtensions = m_projectPart.languageExtensions; - - if (m_compilerFlags & ToolChain::BorlandExtensions) - languageExtensions |= ProjectPart::BorlandExtensions; - if (m_compilerFlags & ToolChain::GnuExtensions) - languageExtensions |= ProjectPart::GnuExtensions; - if (m_compilerFlags & ToolChain::MicrosoftExtensions) - languageExtensions |= ProjectPart::MicrosoftExtensions; - if (m_compilerFlags & ToolChain::OpenMP) - languageExtensions |= ProjectPart::OpenMPExtensions; - if (m_compilerFlags & ToolChain::ObjectiveC) - languageExtensions |= ProjectPart::ObjectiveCExtensions; - } - - static ProjectPartHeaderPath toProjectPartHeaderPath( - const ProjectExplorer::HeaderPath &headerPath) - { - const ProjectPartHeaderPath::Type headerPathType = - headerPath.kind() == ProjectExplorer::HeaderPath::FrameworkHeaderPath - ? ProjectPartHeaderPath::FrameworkPath - : ProjectPartHeaderPath::IncludePath; - - return ProjectPartHeaderPath(headerPath.path(), headerPathType); - } - - void addHeaderPaths() - { - ProjectPartHeaderPaths &headerPaths = m_projectPart.headerPaths; - - foreach (const ProjectExplorer::HeaderPath &header, m_toolChain.systemHeaderPaths()) { - const ProjectPartHeaderPath headerPath = toProjectPartHeaderPath(header); - if (!headerPaths.contains(headerPath)) - headerPaths.push_back(headerPath); - } - } - -private: - ProjectPart &m_projectPart; - - const ToolChainInterface &m_toolChain; - const ProjectExplorer::ToolChain::CompilerFlags m_compilerFlags; -}; - -} // anynomous - -void BaseProjectPartBuilder::createProjectPart(const ProjectFiles &projectFiles, - const QString &partName, - ProjectPart::LanguageVersion languageVersion, - ProjectPart::LanguageExtensions languageExtensions) -{ - ProjectPart::Ptr part(m_templatePart->copy()); - part->displayName = partName; - part->files = projectFiles; - part->languageVersion = languageVersion; - QTC_ASSERT(part->project, return); - - // TODO: If not toolchain is set, show a warning - if (const ToolChainInterfacePtr toolChain = selectToolChain(languageVersion)) { - ToolChainEvaluator evaluator(*part.data(), *toolChain.get()); - evaluator.evaluate(); - } - - part->languageExtensions |= languageExtensions; - part->updateLanguageFeatures(); - - m_projectInfo.appendProjectPart(part); -} - -ToolChainInterfacePtr BaseProjectPartBuilder::selectToolChain( - ProjectPart::LanguageVersion languageVersion) -{ - ToolChainInterfacePtr toolChain = nullptr; - - if (languageVersion <= ProjectPart::LatestCVersion) - toolChain = m_project->toolChain(ProjectExplorer::Constants::C_LANGUAGE_ID, m_cFlags); - - if (!toolChain) // Use Cxx toolchain for C projects without C compiler in kit and for C++ code - toolChain = m_project->toolChain(ProjectExplorer::Constants::CXX_LANGUAGE_ID, m_cxxFlags); - - return toolChain; -} - -} // namespace CppTools diff --git a/src/plugins/cpptools/cppindexingsupport.h b/src/plugins/cpptools/cppindexingsupport.h index e97bb50f675..880e88eeac1 100644 --- a/src/plugins/cpptools/cppindexingsupport.h +++ b/src/plugins/cpptools/cppindexingsupport.h @@ -78,8 +78,9 @@ class CPPTOOLS_EXPORT CppIndexingSupport public: virtual ~CppIndexingSupport() = 0; - virtual QFuture refreshSourceFiles(const QSet &sourceFiles, - CppModelManager::ProgressNotificationMode mode) = 0; + virtual QFuture refreshSourceFiles(const QFutureInterface &superFuture, + const QSet &sourceFiles, + CppModelManager::ProgressNotificationMode mode) = 0; virtual SymbolSearcher *createSymbolSearcher(SymbolSearcher::Parameters parameters, QSet fileNames) = 0; }; diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 965557549b0..4cb5ce107e1 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -630,6 +630,14 @@ static QSet tooBigFilesRemoved(const QSet &files, int fileSize QFuture CppModelManager::updateSourceFiles(const QSet &sourceFiles, ProgressNotificationMode mode) +{ + const QFutureInterface dummy; + return updateSourceFiles(dummy, sourceFiles, mode); +} + +QFuture CppModelManager::updateSourceFiles(const QFutureInterface &superFuture, + const QSet &sourceFiles, + ProgressNotificationMode mode) { if (sourceFiles.isEmpty() || !d->m_indexerEnabled) return QFuture(); @@ -637,8 +645,8 @@ QFuture CppModelManager::updateSourceFiles(const QSet &sourceFile const QSet filteredFiles = tooBigFilesRemoved(sourceFiles, indexerFileSizeLimitInMb()); if (d->m_indexingSupporter) - d->m_indexingSupporter->refreshSourceFiles(filteredFiles, mode); - return d->m_internalIndexingSupport->refreshSourceFiles(filteredFiles, mode); + d->m_indexingSupporter->refreshSourceFiles(superFuture, filteredFiles, mode); + return d->m_internalIndexingSupport->refreshSourceFiles(superFuture, filteredFiles, mode); } QList CppModelManager::projectInfos() const @@ -774,23 +782,25 @@ void CppModelManager::recalculateProjectPartMappings() d->m_symbolFinder.clearCache(); } -void CppModelManager::watchForCanceledProjectIndexer(QFuture future, +void CppModelManager::watchForCanceledProjectIndexer(const QVector> &futures, ProjectExplorer::Project *project) { d->m_projectToIndexerCanceled.insert(project, false); - if (future.isCanceled() || future.isFinished()) - return; + for (const QFuture &future : futures) { + if (future.isCanceled() || future.isFinished()) + continue; - QFutureWatcher *watcher = new QFutureWatcher(); - connect(watcher, &QFutureWatcher::canceled, this, [this, project]() { - if (d->m_projectToIndexerCanceled.contains(project)) // Project not yet removed - d->m_projectToIndexerCanceled.insert(project, true); - }); - connect(watcher, &QFutureWatcher::finished, this, [watcher]() { - watcher->deleteLater(); - }); - watcher->setFuture(future); + QFutureWatcher *watcher = new QFutureWatcher(); + connect(watcher, &QFutureWatcher::canceled, this, [this, project]() { + if (d->m_projectToIndexerCanceled.contains(project)) // Project not yet removed + d->m_projectToIndexerCanceled.insert(project, true); + }); + connect(watcher, &QFutureWatcher::finished, this, [watcher]() { + watcher->deleteLater(); + }); + watcher->setFuture(future); + } } void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) const @@ -823,6 +833,13 @@ void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) const } QFuture CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo) +{ + QFutureInterface dummy; + return updateProjectInfo(dummy, newProjectInfo); +} + +QFuture CppModelManager::updateProjectInfo(QFutureInterface &futureInterface, + const ProjectInfo &newProjectInfo) { if (!newProjectInfo.isValid()) return QFuture(); @@ -908,10 +925,10 @@ QFuture CppModelManager::updateProjectInfo(const ProjectInfo &newProjectIn updateCppEditorDocuments(/*projectsUpdated = */ true); // Trigger reindexing - QFuture indexerFuture = updateSourceFiles(filesToReindex, ForcedProgressNotification); - watchForCanceledProjectIndexer(indexerFuture, project); - - return indexerFuture; + const QFuture indexingFuture = updateSourceFiles(futureInterface, filesToReindex, + ForcedProgressNotification); + watchForCanceledProjectIndexer({futureInterface.future(), indexingFuture}, project); + return indexingFuture; } ProjectInfo CppModelManager::updateCompilerCallDataForProject( diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index 6fe5897f9a2..ce2a042bc9d 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -86,7 +86,10 @@ public: }; QFuture updateSourceFiles(const QSet &sourceFiles, - ProgressNotificationMode mode = ReservedProgressNotification); + ProgressNotificationMode mode = ReservedProgressNotification); + QFuture updateSourceFiles(const QFutureInterface &superFuture, + const QSet &sourceFiles, + ProgressNotificationMode mode = ReservedProgressNotification); void updateCppEditorDocuments(bool projectsUpdated = false) const; WorkingCopy workingCopy() const; QByteArray codeModelConfiguration() const; @@ -94,6 +97,9 @@ public: QList projectInfos() const; ProjectInfo projectInfo(ProjectExplorer::Project *project) const; QFuture updateProjectInfo(const ProjectInfo &newProjectInfo); + QFuture updateProjectInfo(QFutureInterface &futureInterface, + const ProjectInfo &newProjectInfo); + ProjectInfo updateCompilerCallDataForProject(ProjectExplorer::Project *project, ProjectInfo::CompilerCallData &compilerCallData); @@ -210,7 +216,8 @@ private: void initializeBuiltinModelManagerSupport(); void delayedGC(); void recalculateProjectPartMappings(); - void watchForCanceledProjectIndexer(QFuture future, ProjectExplorer::Project *project); + void watchForCanceledProjectIndexer(const QVector > &futures, + ProjectExplorer::Project *project); void replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot); void removeFilesFromSnapshot(const QSet &removedFiles); diff --git a/src/plugins/cpptools/cppprojectfilecategorizer.cpp b/src/plugins/cpptools/cppprojectfilecategorizer.cpp index bf0666e9afb..063dfd5dbac 100644 --- a/src/plugins/cpptools/cppprojectfilecategorizer.cpp +++ b/src/plugins/cpptools/cppprojectfilecategorizer.cpp @@ -29,7 +29,7 @@ namespace CppTools { ProjectFileCategorizer::ProjectFileCategorizer(const QString &projectPartName, const QStringList &filePaths, - ProjectPartBuilder::FileClassifier fileClassifier) + FileClassifier fileClassifier) : m_partName(projectPartName) { const QStringList ambiguousHeaders = classifyFiles(filePaths, fileClassifier); @@ -50,9 +50,8 @@ QString ProjectFileCategorizer::partName(const QString &languageName) const return m_partName; } -QStringList ProjectFileCategorizer::classifyFiles( - const QStringList &filePaths, - ProjectPartBuilder::FileClassifier fileClassifier) +QStringList ProjectFileCategorizer::classifyFiles(const QStringList &filePaths, + FileClassifier fileClassifier) { QStringList ambiguousHeaders; diff --git a/src/plugins/cpptools/cppprojectfilecategorizer.h b/src/plugins/cpptools/cppprojectfilecategorizer.h index a45c9bf99bc..0d52767876a 100644 --- a/src/plugins/cpptools/cppprojectfilecategorizer.h +++ b/src/plugins/cpptools/cppprojectfilecategorizer.h @@ -26,7 +26,7 @@ #pragma once #include "cppprojectfile.h" -#include "projectpartbuilder.h" +#include "cpprawprojectpart.h" #include #include @@ -36,7 +36,7 @@ namespace CppTools { class ProjectFileCategorizer { public: - using FileClassifier = ProjectPartBuilder::FileClassifier; + using FileClassifier = RawProjectPart::FileClassifier; public: ProjectFileCategorizer(const QString &projectPartName, diff --git a/src/plugins/cpptools/cppprojectinfogenerator.cpp b/src/plugins/cpptools/cppprojectinfogenerator.cpp new file mode 100644 index 00000000000..56216ccd4a6 --- /dev/null +++ b/src/plugins/cpptools/cppprojectinfogenerator.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "cppprojectinfogenerator.h" + +#include "cppprojectfilecategorizer.h" + +#include + +namespace CppTools { +namespace Internal { + +namespace { + +class ToolChainEvaluator +{ +public: + ToolChainEvaluator(ProjectPart &projectPart, + const RawProjectPartFlags &flags, + const ToolChainInfo &tcInfo) + : m_projectPart(projectPart) + , m_flags(flags) + , m_tcInfo(tcInfo) + { + } + + void evaluate() + { + m_projectPart.toolchainType = m_tcInfo.type; + m_projectPart.isMsvc2015Toolchain = m_tcInfo.isMsvc2015ToolChain; + m_projectPart.toolChainWordWidth = mapWordWith(m_tcInfo.wordWidth); + m_projectPart.toolChainTargetTriple = m_tcInfo.targetTriple; + + m_projectPart.warningFlags = m_flags.warningFlags; + + mapLanguageVersion(); + mapLanguageExtensions(); + + addHeaderPaths(); + addDefines(); + } + +private: + static ProjectPart::ToolChainWordWidth mapWordWith(unsigned wordWidth) + { + return wordWidth == 64 + ? ProjectPart::WordWidth64Bit + : ProjectPart::WordWidth32Bit; + } + + void mapLanguageVersion() + { + using namespace ProjectExplorer; + + const ToolChain::CompilerFlags &compilerFlags = m_flags.compilerFlags; + ProjectPart::LanguageVersion &languageVersion = m_projectPart.languageVersion; + + if (compilerFlags & ToolChain::StandardC11) + languageVersion = ProjectPart::C11; + else if (compilerFlags & ToolChain::StandardC99) + languageVersion = ProjectPart::C99; + else if (compilerFlags & ToolChain::StandardCxx17) + languageVersion = ProjectPart::CXX17; + else if (compilerFlags & ToolChain::StandardCxx14) + languageVersion = ProjectPart::CXX14; + else if (compilerFlags & ToolChain::StandardCxx11) + languageVersion = ProjectPart::CXX11; + else if (compilerFlags & ToolChain::StandardCxx98) + languageVersion = ProjectPart::CXX98; + } + + void mapLanguageExtensions() + { + using namespace ProjectExplorer; + + const ToolChain::CompilerFlags &compilerFlags = m_flags.compilerFlags; + ProjectPart::LanguageExtensions &languageExtensions = m_projectPart.languageExtensions; + + if (compilerFlags & ToolChain::BorlandExtensions) + languageExtensions |= ProjectPart::BorlandExtensions; + if (compilerFlags & ToolChain::GnuExtensions) + languageExtensions |= ProjectPart::GnuExtensions; + if (compilerFlags & ToolChain::MicrosoftExtensions) + languageExtensions |= ProjectPart::MicrosoftExtensions; + if (compilerFlags & ToolChain::OpenMP) + languageExtensions |= ProjectPart::OpenMPExtensions; + if (compilerFlags & ToolChain::ObjectiveC) + languageExtensions |= ProjectPart::ObjectiveCExtensions; + } + + static ProjectPartHeaderPath toProjectPartHeaderPath( + const ProjectExplorer::HeaderPath &headerPath) + { + const ProjectPartHeaderPath::Type headerPathType = + headerPath.kind() == ProjectExplorer::HeaderPath::FrameworkHeaderPath + ? ProjectPartHeaderPath::FrameworkPath + : ProjectPartHeaderPath::IncludePath; + + return ProjectPartHeaderPath(headerPath.path(), headerPathType); + } + + void addHeaderPaths() + { + if (!m_tcInfo.headerPathsRunner) + return; // No compiler set in kit. + + const QList systemHeaderPaths + = m_tcInfo.headerPathsRunner(m_flags.commandLineFlags, + m_tcInfo.sysRoothPath); + + ProjectPartHeaderPaths &headerPaths = m_projectPart.headerPaths; + for (const ProjectExplorer::HeaderPath &header : systemHeaderPaths) { + const ProjectPartHeaderPath headerPath = toProjectPartHeaderPath(header); + if (!headerPaths.contains(headerPath)) + headerPaths.push_back(headerPath); + } + } + + void addDefines() + { + if (!m_tcInfo.predefinedMacrosRunner) + return; // No compiler set in kit. + + m_projectPart.toolchainDefines = m_tcInfo.predefinedMacrosRunner(m_flags.commandLineFlags); + } + +private: + ProjectPart &m_projectPart; + const RawProjectPartFlags &m_flags; + const ToolChainInfo &m_tcInfo; +}; + +} // anonymous namespace + +ProjectInfoGenerator::ProjectInfoGenerator(const QFutureInterface &futureInterface, + const ProjectUpdateInfo &projectUpdateInfo) + : m_futureInterface(futureInterface) + , m_projectUpdateInfo(projectUpdateInfo) +{ +} + +ProjectInfo ProjectInfoGenerator::generate() +{ + m_projectInfo = ProjectInfo(m_projectUpdateInfo.project); + + for (const RawProjectPart &rpp : m_projectUpdateInfo.rawProjectParts) { + if (m_futureInterface.isCanceled()) + return ProjectInfo(); + + createProjectParts(rpp); + } + + return m_projectInfo; +} + +static ProjectPart::Ptr projectPartFromRawProjectPart(const RawProjectPart &rawProjectPart) +{ + ProjectPart::Ptr part(new ProjectPart); + part->projectFile = rawProjectPart.projectFile; + part->projectConfigFile = rawProjectPart.projectConfigFile; + part->qtVersion = rawProjectPart.qtVersion; + part->projectDefines = rawProjectPart.projectDefines; + part->headerPaths = rawProjectPart.headerPaths; + part->precompiledHeaders = rawProjectPart.precompiledHeaders; + part->selectedForBuilding = rawProjectPart.selectedForBuilding; + + return part; +} + +void ProjectInfoGenerator::createProjectParts(const RawProjectPart &rawProjectPart) +{ + ProjectFileCategorizer cat(rawProjectPart.displayName, + rawProjectPart.files, + rawProjectPart.fileClassifier); + + if (cat.hasParts()) { + const ProjectPart::Ptr part = projectPartFromRawProjectPart(rawProjectPart); + + if (cat.hasCxxSources()) { + createProjectPart(rawProjectPart, + part, + cat.cxxSources(), + cat.partName("C++"), + ProjectPart::LatestCxxVersion, + ProjectPart::NoExtensions); + } + + if (cat.hasObjcxxSources()) { + createProjectPart(rawProjectPart, + part, + cat.objcxxSources(), + cat.partName("Obj-C++"), + ProjectPart::LatestCxxVersion, + ProjectPart::ObjectiveCExtensions); + } + + if (cat.hasCSources()) { + createProjectPart(rawProjectPart, + part, + cat.cSources(), + cat.partName("C"), + ProjectPart::LatestCVersion, + ProjectPart::NoExtensions); + } + + if (cat.hasObjcSources()) { + createProjectPart(rawProjectPart, + part, + cat.objcSources(), + cat.partName("Obj-C"), + ProjectPart::LatestCVersion, + ProjectPart::ObjectiveCExtensions); + } + } +} + +void ProjectInfoGenerator::createProjectPart(const RawProjectPart &rawProjectPart, + const ProjectPart::Ptr &templateProjectPart, + const ProjectFiles &projectFiles, + const QString &partName, + ProjectPart::LanguageVersion languageVersion, + ProjectPart::LanguageExtensions languageExtensions) +{ + ProjectPart::Ptr part(templateProjectPart->copy()); + part->displayName = partName; + part->files = projectFiles; + part->languageVersion = languageVersion; + + RawProjectPartFlags flags; + ToolChainInfo tcInfo; + if (languageVersion <= ProjectPart::LatestCVersion) { + flags = rawProjectPart.flagsForC; + tcInfo = m_projectUpdateInfo.cToolChainInfo; + } + // Use Cxx toolchain for C projects without C compiler in kit and for C++ code + if (!tcInfo.isValid()) { + flags = rawProjectPart.flagsForCxx; + tcInfo = m_projectUpdateInfo.cxxToolChainInfo; + } + // TODO: If no toolchain is set, show a warning + ToolChainEvaluator evaluator(*part.data(), flags, tcInfo); + evaluator.evaluate(); + + part->languageExtensions |= languageExtensions; + part->updateLanguageFeatures(); + + m_projectInfo.appendProjectPart(part); +} + +} // namespace Internal +} // namespace CppTools diff --git a/src/plugins/cpptools/cppprojectinfogenerator.h b/src/plugins/cpptools/cppprojectinfogenerator.h new file mode 100644 index 00000000000..f1972cd5c2c --- /dev/null +++ b/src/plugins/cpptools/cppprojectinfogenerator.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "projectinfo.h" + +#include + +namespace CppTools { +namespace Internal { + +class ProjectInfoGenerator +{ +public: + ProjectInfoGenerator(const QFutureInterface &futureInterface, + const ProjectUpdateInfo &projectUpdateInfo); + + ProjectInfo generate(); + +private: + void createProjectParts(const RawProjectPart &rawProjectPart); + void createProjectPart(const RawProjectPart &rawProjectPart, + const ProjectPart::Ptr &templateProjectPart, + const ProjectFiles &projectFiles, + const QString &partName, + ProjectPart::LanguageVersion languageVersion, + ProjectPart::LanguageExtensions languageExtensions); + +private: + const QFutureInterface &m_futureInterface; + const ProjectUpdateInfo &m_projectUpdateInfo; + + ProjectInfo m_projectInfo; +}; +} // namespace Internal +} // namespace CppTools diff --git a/src/plugins/cpptools/cppprojectupdater.cpp b/src/plugins/cpptools/cppprojectupdater.cpp new file mode 100644 index 00000000000..33a2b1df585 --- /dev/null +++ b/src/plugins/cpptools/cppprojectupdater.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "cppprojectupdater.h" + +#include "cppmodelmanager.h" +#include "cppprojectinfogenerator.h" + +#include + +#include +#include + +namespace CppTools { + +CppProjectUpdater::CppProjectUpdater(ProjectExplorer::Project *project) + : m_project(project) +{ + connect(&m_generateFutureWatcher, &QFutureWatcher::finished, + this, &CppProjectUpdater::onProjectInfoGenerated); +} + +CppProjectUpdater::~CppProjectUpdater() +{ + cancelAndWaitForFinished(); +} + +void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo) +{ + // Stop previous update. + cancel(); + m_futureInterface.waitForFinished(); + m_futureInterface = QFutureInterface(); + + m_projectUpdateInfo = projectUpdateInfo; + + // Ensure that we do not operate on a deleted toolchain. + using namespace ProjectExplorer; + connect(ToolChainManager::instance(), &ToolChainManager::toolChainRemoved, + this, &CppProjectUpdater::onToolChainRemoved); + + // Run the project info generator in a worker thread and continue if that one is finished. + const QFutureInterface &futureInterface = m_futureInterface; + const QFuture future = Utils::runAsync([=]() { + Internal::ProjectInfoGenerator generator(futureInterface, projectUpdateInfo); + return generator.generate(); + }); + m_generateFutureWatcher.setFuture(future); +} + +void CppProjectUpdater::cancel() +{ + disconnect(&m_generateFutureWatcher); + m_futureInterface.cancel(); +} + +void CppProjectUpdater::cancelAndWaitForFinished() +{ + cancel(); + m_futureInterface.waitForFinished(); +} + +void CppProjectUpdater::onToolChainRemoved(ProjectExplorer::ToolChain *t) +{ + QTC_ASSERT(t, return); + if (t == m_projectUpdateInfo.cToolChain || t == m_projectUpdateInfo.cxxToolChain) + cancelAndWaitForFinished(); +} + +void CppProjectUpdater::onProjectInfoGenerated() +{ + // From now on we do not access the toolchain anymore, so disconnect. + using namespace ProjectExplorer; + disconnect(ToolChainManager::instance(), &ToolChainManager::toolChainRemoved, + this, &CppProjectUpdater::onToolChainRemoved); + + if (m_futureInterface.isCanceled()) + return; + + QFuture future = CppModelManager::instance() + ->updateProjectInfo(m_futureInterface, m_generateFutureWatcher.result()); + QTC_CHECK(future != QFuture()); + + const ProjectInfo projectInfo = CppModelManager::instance()->projectInfo(m_project); + QTC_CHECK(projectInfo.isValid()); + emit projectInfoUpdated(projectInfo); +} + +} // namespace CppTools diff --git a/src/plugins/cpptools/cppprojectinterface.h b/src/plugins/cpptools/cppprojectupdater.h similarity index 53% rename from src/plugins/cpptools/cppprojectinterface.h rename to src/plugins/cpptools/cppprojectupdater.h index dd03430cb82..60e470b0f73 100644 --- a/src/plugins/cpptools/cppprojectinterface.h +++ b/src/plugins/cpptools/cppprojectupdater.h @@ -25,55 +25,45 @@ #pragma once -#include +#include "cpptools_global.h" +#include "projectinfo.h" -#include +#include -QT_BEGIN_NAMESPACE -class QString; -QT_END_NAMESPACE - -namespace Core { -class Id; -} - -namespace ProjectExplorer { -class Project; -class ToolChain; -} +#include +#include namespace CppTools { -class ToolChainInterface +class ProjectInfo; +class ProjectUpdateInfo; + +class CPPTOOLS_EXPORT CppProjectUpdater : public QObject { + Q_OBJECT + public: - virtual ~ToolChainInterface() {} + CppProjectUpdater(ProjectExplorer::Project *project); + ~CppProjectUpdater(); - virtual Core::Id type() const = 0; - virtual bool isMsvc2015Toolchain() const = 0; + void update(const ProjectUpdateInfo &projectUpdateInfo); + void cancel(); - virtual unsigned wordWidth() const = 0; - virtual QString targetTriple() const = 0; +signals: + void projectInfoUpdated(const ProjectInfo &projectInfo); - virtual QByteArray predefinedMacros() const = 0; - virtual QList systemHeaderPaths() const = 0; +private: + void cancelAndWaitForFinished(); - virtual ProjectExplorer::WarningFlags warningFlags() const = 0; - virtual ProjectExplorer::ToolChain::CompilerFlags compilerFlags() const = 0; -}; + void onToolChainRemoved(ProjectExplorer::ToolChain *); + void onProjectInfoGenerated(); -using ToolChainInterfacePtr = std::unique_ptr; +private: + ProjectExplorer::Project * const m_project; + ProjectUpdateInfo m_projectUpdateInfo; -class ProjectInterface -{ -public: - virtual ~ProjectInterface() {} - - virtual QString displayName() const = 0; - virtual QString projectFilePath() const = 0; - - virtual ToolChainInterfacePtr toolChain(Core::Id language, - const QStringList &commandLineFlags) const = 0; + QFutureInterface m_futureInterface; + QFutureWatcher m_generateFutureWatcher; }; } // namespace CppTools diff --git a/src/plugins/cpptools/cpprawprojectpart.cpp b/src/plugins/cpptools/cpprawprojectpart.cpp new file mode 100644 index 00000000000..4df3458576b --- /dev/null +++ b/src/plugins/cpptools/cpprawprojectpart.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "cpprawprojectpart.h" + +#include +#include +#include + +namespace CppTools { + +RawProjectPartFlags::RawProjectPartFlags(const ProjectExplorer::ToolChain *toolChain, + const QStringList &commandLineFlags) +{ + // Keep the following cheap/non-blocking for the ui thread. Expensive + // operations are encapsulated in ToolChainInfo as "runners". + if (toolChain) { + this->commandLineFlags = commandLineFlags; + warningFlags = toolChain->warningFlags(commandLineFlags); + compilerFlags = toolChain->compilerFlags(commandLineFlags); + } +} + +void RawProjectPart::setDisplayName(const QString &displayName) +{ + this->displayName = displayName; +} + +void RawProjectPart::setFiles(const QStringList &files, FileClassifier fileClassifier) +{ + this->files = files; + this->fileClassifier = fileClassifier; +} + +void RawProjectPart::setProjectFile(const QString &projectFile) +{ + this->projectFile = projectFile; +} + +void RawProjectPart::setConfigFileName(const QString &configFileName) +{ + this->projectConfigFile = configFileName; +} + +void RawProjectPart::setQtVersion(ProjectPart::QtVersion qtVersion) +{ + this->qtVersion = qtVersion; +} + +void RawProjectPart::setDefines(const QByteArray &defines) +{ + this->projectDefines = defines; +} + +void RawProjectPart::setHeaderPaths(const ProjectPartHeaderPaths &headerPaths) +{ + this->headerPaths = headerPaths; +} + +void RawProjectPart::setIncludePaths(const QStringList &includePaths) +{ + headerPaths.clear(); + + foreach (const QString &includeFile, includePaths) { + ProjectPartHeaderPath hp(includeFile, ProjectPartHeaderPath::IncludePath); + + // The simple project managers are utterly ignorant of frameworks on macOS, and won't report + // framework paths. The work-around is to check if the include path ends in ".framework", + // and if so, add the parent directory as framework path. + if (includeFile.endsWith(QLatin1String(".framework"))) { + const int slashIdx = includeFile.lastIndexOf(QLatin1Char('/')); + if (slashIdx != -1) { + hp = ProjectPartHeaderPath(includeFile.left(slashIdx), + ProjectPartHeaderPath::FrameworkPath); + } + } + + headerPaths += hp; + } +} + +void RawProjectPart::setPreCompiledHeaders(const QStringList &preCompiledHeaders) +{ + this->precompiledHeaders = preCompiledHeaders; +} + +void RawProjectPart::setSelectedForBuilding(bool yesno) +{ + this->selectedForBuilding = yesno; +} + +void RawProjectPart::setFlagsForC(const RawProjectPartFlags &flags) +{ + flagsForC = flags; +} + +void RawProjectPart::setFlagsForCxx(const RawProjectPartFlags &flags) +{ + flagsForCxx = flags; +} + +} // namespace CppTools diff --git a/src/plugins/cpptools/cppbaseprojectpartbuilder.h b/src/plugins/cpptools/cpprawprojectpart.h similarity index 56% rename from src/plugins/cpptools/cppbaseprojectpartbuilder.h rename to src/plugins/cpptools/cpprawprojectpart.h index 779053dfbc7..7e6d36f4599 100644 --- a/src/plugins/cpptools/cppbaseprojectpartbuilder.h +++ b/src/plugins/cpptools/cpprawprojectpart.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -26,32 +26,45 @@ #pragma once #include "cpptools_global.h" - -#include "cppprojectinterface.h" #include "projectpart.h" +#include + #include -#include namespace CppTools { -class ProjectInfo; - -class CPPTOOLS_EXPORT BaseProjectPartBuilder +class CPPTOOLS_EXPORT RawProjectPartFlags { public: - BaseProjectPartBuilder(ProjectInterface *project, ProjectInfo &projectInfo); + RawProjectPartFlags() = default; + RawProjectPartFlags(const ProjectExplorer::ToolChain *toolChain, + const QStringList &commandLineFlags); + +public: + QStringList commandLineFlags; + // The following are deduced from commandLineFlags. + ProjectExplorer::WarningFlags warningFlags = ProjectExplorer::WarningFlags::Default; + ProjectExplorer::ToolChain::CompilerFlags compilerFlags + = ProjectExplorer::ToolChain::CompilerFlag::NoFlags; +}; + +class CPPTOOLS_EXPORT RawProjectPart +{ +public: + RawProjectPart() {} void setDisplayName(const QString &displayName); + // FileClassifier must be thread-safe. + using FileClassifier = std::function; + void setFiles(const QStringList &files, FileClassifier fileClassifier = FileClassifier()); + void setProjectFile(const QString &projectFile); void setConfigFileName(const QString &configFileName); void setQtVersion(ProjectPart::QtVersion qtVersion); - void setCFlags(const QStringList &flags); - void setCxxFlags(const QStringList &flags); - void setDefines(const QByteArray &defines); void setHeaderPaths(const ProjectPartHeaderPaths &headerPaths); void setIncludePaths(const QStringList &includePaths); @@ -60,24 +73,26 @@ public: void setSelectedForBuilding(bool yesno); - using FileClassifier = std::function; - QList createProjectPartsForFiles(const QStringList &filePaths, - FileClassifier fileClassifier = FileClassifier()); + void setFlagsForC(const RawProjectPartFlags &flags); + void setFlagsForCxx(const RawProjectPartFlags &flags); -private: - void createProjectPart(const ProjectFiles &projectFiles, - const QString &partName, - ProjectPart::LanguageVersion languageVersion, - ProjectPart::LanguageExtensions languageExtensions); - ToolChainInterfacePtr selectToolChain(ProjectPart::LanguageVersion languageVersion); +public: + QString displayName; + QString projectFile; + QString projectConfigFile; // currently only used by the Generic Project Manager + QStringList precompiledHeaders; + ProjectPartHeaderPaths headerPaths; + QByteArray projectDefines; + ProjectPart::QtVersion qtVersion = ProjectPart::UnknownQt; + bool selectedForBuilding = true; -private: - std::unique_ptr m_project; - ProjectInfo &m_projectInfo; + RawProjectPartFlags flagsForC; + RawProjectPartFlags flagsForCxx; - ProjectPart::Ptr m_templatePart; - QStringList m_cFlags; - QStringList m_cxxFlags; + QStringList files; + FileClassifier fileClassifier; }; +using RawProjectParts = QVector; + } // namespace CppTools diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index f65ffdd7645..3fffe2dfd3d 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -42,7 +42,9 @@ HEADERS += \ cppmodelmanagersupportinternal.h \ cpppointerdeclarationformatter.h \ cppprojectfile.h \ + cppprojectupdater.h \ cppqtstyleindenter.h \ + cpprawprojectpart.h \ cpprefactoringchanges.h \ cppselectionchanger.h \ cppsemanticinfo.h \ @@ -76,13 +78,11 @@ HEADERS += \ projectpart.h \ projectpartheaderpath.h \ projectinfo.h \ - projectpartbuilder.h \ + cppprojectinfogenerator.h \ compileroptionsbuilder.h \ refactoringengineinterface.h \ cppprojectfilecategorizer.h \ clangcompileroptionsbuilder.h \ - cppprojectinterface.h \ - cppbaseprojectpartbuilder.h \ cppprojectpartchooser.h \ SOURCES += \ @@ -125,7 +125,9 @@ SOURCES += \ cppmodelmanagersupportinternal.cpp \ cpppointerdeclarationformatter.cpp \ cppprojectfile.cpp \ + cppprojectupdater.cpp \ cppqtstyleindenter.cpp \ + cpprawprojectpart.cpp \ cpprefactoringchanges.cpp \ cppselectionchanger.cpp \ cppsemanticinfo.cpp \ @@ -154,10 +156,9 @@ SOURCES += \ cpptoolsbridgeqtcreatorimplementation.cpp \ projectpart.cpp \ projectinfo.cpp \ - projectpartbuilder.cpp \ + cppprojectinfogenerator.cpp \ compileroptionsbuilder.cpp \ cppprojectfilecategorizer.cpp \ - cppbaseprojectpartbuilder.cpp \ clangcompileroptionsbuilder.cpp \ cppprojectpartchooser.cpp \ diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs index a812ca1c12e..6bfb639c5ef 100644 --- a/src/plugins/cpptools/cpptools.qbs +++ b/src/plugins/cpptools/cpptools.qbs @@ -51,8 +51,6 @@ Project { "clangdiagnosticconfigswidget.ui", "compileroptionsbuilder.cpp", "compileroptionsbuilder.h", - "cppbaseprojectpartbuilder.cpp", - "cppbaseprojectpartbuilder.h", "cppchecksymbols.cpp", "cppchecksymbols.h", "cppclassesfilter.cpp", @@ -118,11 +116,16 @@ Project { "cppprojectfile.h", "cppprojectfilecategorizer.cpp", "cppprojectfilecategorizer.h", - "cppprojectinterface.h", + "cppprojectinfogenerator.cpp", + "cppprojectinfogenerator.h", "cppprojectpartchooser.cpp", "cppprojectpartchooser.h", + "cppprojectupdater.cpp", + "cppprojectupdater.h", "cppqtstyleindenter.cpp", "cppqtstyleindenter.h", + "cpprawprojectpart.cpp", + "cpprawprojectpart.h", "cpprefactoringchanges.cpp", "cpprefactoringchanges.h", "cppselectionchanger.cpp", @@ -170,8 +173,6 @@ Project { "projectinfo.h", "projectpart.cpp", "projectpart.h", - "projectpartbuilder.cpp", - "projectpartbuilder.h", "projectpartheaderpath.h", "searchsymbols.cpp", "searchsymbols.h", diff --git a/src/plugins/cpptools/cpptoolsunittestfiles.pri b/src/plugins/cpptools/cpptoolsunittestfiles.pri index 317ba52b708..6960ccbde94 100644 --- a/src/plugins/cpptools/cpptoolsunittestfiles.pri +++ b/src/plugins/cpptools/cpptoolsunittestfiles.pri @@ -15,9 +15,8 @@ HEADERS += \ $$PWD/compileroptionsbuilder.h \ $$PWD/cppprojectfilecategorizer.h \ $$PWD/clangcompileroptionsbuilder.h \ - $$PWD/cppbaseprojectpartbuilder.h \ $$PWD/projectinfo.h \ - $$PWD/cppprojectinterface.h \ + $$PWD/cppprojectinfogenerator.cpp \ $$PWD/cppprojectpartchooser.h \ SOURCES += \ @@ -27,6 +26,6 @@ SOURCES += \ $$PWD/compileroptionsbuilder.cpp \ $$PWD/cppprojectfilecategorizer.cpp \ $$PWD/clangcompileroptionsbuilder.cpp \ - $$PWD/cppbaseprojectpartbuilder.cpp \ $$PWD/projectinfo.cpp \ + $$PWD/cppprojectinfogenerator.cpp \ $$PWD/cppprojectpartchooser.cpp \ diff --git a/src/plugins/cpptools/projectinfo.cpp b/src/plugins/cpptools/projectinfo.cpp index 919cbbbc9f2..e0db5fd54f3 100644 --- a/src/plugins/cpptools/projectinfo.cpp +++ b/src/plugins/cpptools/projectinfo.cpp @@ -25,10 +25,47 @@ #include "projectinfo.h" -#include +#include +#include +#include namespace CppTools { +ToolChainInfo::ToolChainInfo(const ProjectExplorer::ToolChain *toolChain, + const ProjectExplorer::Kit *kit) +{ + if (toolChain) { + // Keep the following cheap/non-blocking for the ui thread... + type = toolChain->typeId(); + isMsvc2015ToolChain + = toolChain->targetAbi().osFlavor() == ProjectExplorer::Abi::WindowsMsvc2015Flavor; + wordWidth = toolChain->targetAbi().wordWidth(); + targetTriple = type == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID + ? QLatin1String("i686-pc-windows-msvc") + : toolChain->originalTargetTriple(); // OK, compiler run is already cached. + + // ...and save the potentially expensive operations for later so that + // they can be run from a worker thread. + sysRoothPath = ProjectExplorer::SysRootKitInformation::sysRoot(kit).toString(); + headerPathsRunner = toolChain->createSystemHeaderPathsRunner(); + predefinedMacrosRunner = toolChain->createPredefinedMacrosRunner(); + } +} + +ProjectUpdateInfo::ProjectUpdateInfo(ProjectExplorer::Project *project, + const ProjectExplorer::ToolChain *cToolChain, + const ProjectExplorer::ToolChain *cxxToolChain, + const ProjectExplorer::Kit *kit, + const RawProjectParts &rawProjectParts) + : project(project) + , rawProjectParts(rawProjectParts) + , cToolChain(cToolChain) + , cxxToolChain(cxxToolChain) + , cToolChainInfo(ToolChainInfo(cToolChain, kit)) + , cxxToolChainInfo(ToolChainInfo(cxxToolChain, kit)) +{ +} + ProjectInfo::ProjectInfo(QPointer project) : m_project(project) { diff --git a/src/plugins/cpptools/projectinfo.h b/src/plugins/cpptools/projectinfo.h index 758775d4929..9096322989d 100644 --- a/src/plugins/cpptools/projectinfo.h +++ b/src/plugins/cpptools/projectinfo.h @@ -27,8 +27,12 @@ #include "cpptools_global.h" +#include "cpprawprojectpart.h" #include "projectpart.h" +#include +#include + #include #include #include @@ -36,6 +40,48 @@ namespace CppTools { +class ToolChainInfo +{ +public: + ToolChainInfo() = default; + ToolChainInfo(const ProjectExplorer::ToolChain *toolChain, + const ProjectExplorer::Kit *kit); + + bool isValid() const { return type.isValid(); } + +public: + Core::Id type; + bool isMsvc2015ToolChain = false; + unsigned wordWidth = 0; + QString targetTriple; + + QString sysRoothPath; // For headerPathsRunner. + ProjectExplorer::ToolChain::SystemHeaderPathsRunner headerPathsRunner; + ProjectExplorer::ToolChain::PredefinedMacrosRunner predefinedMacrosRunner; +}; + +class CPPTOOLS_EXPORT ProjectUpdateInfo +{ +public: + ProjectUpdateInfo() = default; + ProjectUpdateInfo(ProjectExplorer::Project *project, + const ProjectExplorer::ToolChain *cToolChain, + const ProjectExplorer::ToolChain *cxxToolChain, + const ProjectExplorer::Kit *kit, + const RawProjectParts &rawProjectParts); + bool isValid() const { return project && !rawProjectParts.isEmpty(); } + +public: + QPointer project; + QVector rawProjectParts; + + const ProjectExplorer::ToolChain *cToolChain = nullptr; + const ProjectExplorer::ToolChain *cxxToolChain = nullptr; + + ToolChainInfo cToolChainInfo; + ToolChainInfo cxxToolChainInfo; +}; + class CPPTOOLS_EXPORT ProjectInfo { public: diff --git a/src/plugins/cpptools/projectpartbuilder.cpp b/src/plugins/cpptools/projectpartbuilder.cpp deleted file mode 100644 index a2efe98b133..00000000000 --- a/src/plugins/cpptools/projectpartbuilder.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "projectpartbuilder.h" - -#include "projectinfo.h" - -#include -#include -#include -#include -#include -#include - -namespace CppTools { - -class ToolChainImpl : public ToolChainInterface -{ -public: - ToolChainImpl(ProjectExplorer::ToolChain &toolChain, - const ProjectExplorer::Kit *kit, - const QStringList &commandLineFlags) - : m_toolChain(toolChain) - , m_kit(kit) - , m_commandLineFlags(commandLineFlags) - { - } - - Core::Id type() const override - { - return m_toolChain.typeId(); - } - - bool isMsvc2015Toolchain() const override - { - return m_toolChain.targetAbi().osFlavor() == ProjectExplorer::Abi::WindowsMsvc2015Flavor; - } - - unsigned wordWidth() const override - { - return m_toolChain.targetAbi().wordWidth(); - } - - QString targetTriple() const override - { - if (type() == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) - return QLatin1String("i686-pc-windows-msvc"); - - return m_toolChain.originalTargetTriple(); - } - - QByteArray predefinedMacros() const override - { - return m_toolChain.predefinedMacros(m_commandLineFlags); - } - - QList systemHeaderPaths() const override - { - return m_toolChain.systemHeaderPaths( - m_commandLineFlags, - ProjectExplorer::SysRootKitInformation::sysRoot(m_kit)); - } - - ProjectExplorer::WarningFlags warningFlags() const override - { - return m_toolChain.warningFlags(m_commandLineFlags); - } - - ProjectExplorer::ToolChain::CompilerFlags compilerFlags() const override - { - return m_toolChain.compilerFlags(m_commandLineFlags); - } - -private: - ProjectExplorer::ToolChain &m_toolChain; - const ProjectExplorer::Kit *m_kit = nullptr; - const QStringList m_commandLineFlags; -}; - -class ProjectImpl : public ProjectInterface -{ -public: - ProjectImpl(ProjectExplorer::Project &project) - : m_project(project) - { - if (ProjectExplorer::Target *activeTarget = m_project.activeTarget()) - m_kit = activeTarget->kit(); - } - - QString displayName() const override - { - return m_project.displayName(); - } - - QString projectFilePath() const override - { - return m_project.projectFilePath().toString(); - } - - ToolChainInterfacePtr toolChain(Core::Id language, - const QStringList &commandLineFlags) const override - { - using namespace ProjectExplorer; - - if (ProjectExplorer::ToolChain *t = ToolChainKitInformation::toolChain(m_kit, language)) - return ToolChainInterfacePtr(new ToolChainImpl(*t, m_kit, commandLineFlags)); - - return ToolChainInterfacePtr(); - } - -private: - ProjectExplorer::Project &m_project; - ProjectExplorer::Kit *m_kit = nullptr; -}; - -ProjectPartBuilder::ProjectPartBuilder(ProjectInfo &projectInfo) - : BaseProjectPartBuilder(new ProjectImpl(*projectInfo.project().data()), projectInfo) -{ -} - -} // namespace CppTools diff --git a/src/plugins/genericprojectmanager/genericproject.cpp b/src/plugins/genericprojectmanager/genericproject.cpp index a06a6fba105..a6d3bff6a76 100644 --- a/src/plugins/genericprojectmanager/genericproject.cpp +++ b/src/plugins/genericprojectmanager/genericproject.cpp @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include #include @@ -66,6 +66,7 @@ namespace Internal { //////////////////////////////////////////////////////////////////////////////////// GenericProject::GenericProject(Manager *manager, const QString &fileName) + : m_cppCodeModelUpdater(new CppTools::CppProjectUpdater(this)) { setId(Constants::GENERICPROJECT_ID); setProjectManager(manager); @@ -96,7 +97,7 @@ GenericProject::GenericProject(Manager *manager, const QString &fileName) GenericProject::~GenericProject() { - m_codeModelFuture.cancel(); + delete m_cppCodeModelUpdater; projectManager()->unregisterProject(this); } @@ -338,12 +339,19 @@ QStringList GenericProject::processEntries(const QStringList &paths, void GenericProject::refreshCppCodeModel() { - CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance(); + const Kit *k = nullptr; + if (Target *target = activeTarget()) + k = target->kit(); + else + k = KitManager::defaultKit(); + QTC_ASSERT(k, return); - m_codeModelFuture.cancel(); + ToolChain *cToolChain + = ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::C_LANGUAGE_ID); + ToolChain *cxxToolChain + = ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::CXX_LANGUAGE_ID); - CppTools::ProjectInfo pInfo(this); - CppTools::ProjectPartBuilder ppBuilder(pInfo); + m_cppCodeModelUpdater->cancel(); CppTools::ProjectPart::QtVersion activeQtVersion = CppTools::ProjectPart::NoQt; if (QtSupport::BaseQtVersion *qtVersion = @@ -354,16 +362,15 @@ void GenericProject::refreshCppCodeModel() activeQtVersion = CppTools::ProjectPart::Qt5; } - ppBuilder.setProjectFile(projectFilePath().toString()); - ppBuilder.setQtVersion(activeQtVersion); - ppBuilder.setIncludePaths(projectIncludePaths()); - ppBuilder.setConfigFileName(configFileName()); + CppTools::RawProjectPart rpp; + rpp.setProjectFile(projectFilePath().toString()); + rpp.setQtVersion(activeQtVersion); + rpp.setIncludePaths(projectIncludePaths()); + rpp.setConfigFileName(configFileName()); + rpp.setFiles(files()); - const QList languages = ppBuilder.createProjectPartsForFiles(files()); - for (Id language : languages) - setProjectLanguage(language, true); - - m_codeModelFuture = modelManager->updateProjectInfo(pInfo); + const CppTools::ProjectUpdateInfo projectInfoUpdate(this, cToolChain, cxxToolChain, k, {rpp}); + m_cppCodeModelUpdater->update(projectInfoUpdate); } void GenericProject::activeTargetWasChanged() diff --git a/src/plugins/genericprojectmanager/genericproject.h b/src/plugins/genericprojectmanager/genericproject.h index 59655c39cdf..815f8024b06 100644 --- a/src/plugins/genericprojectmanager/genericproject.h +++ b/src/plugins/genericprojectmanager/genericproject.h @@ -37,6 +37,8 @@ #include +namespace CppTools { class CppProjectUpdater; } + namespace GenericProjectManager { namespace Internal { @@ -104,7 +106,7 @@ private: QStringList m_rawProjectIncludePaths; QStringList m_projectIncludePaths; - QFuture m_codeModelFuture; + CppTools::CppProjectUpdater *m_cppCodeModelUpdater = nullptr; ProjectExplorer::Target *m_activeTarget = nullptr; }; diff --git a/src/plugins/nim/project/nimtoolchain.cpp b/src/plugins/nim/project/nimtoolchain.cpp index 2b26908267d..1c34ec24058 100644 --- a/src/plugins/nim/project/nimtoolchain.cpp +++ b/src/plugins/nim/project/nimtoolchain.cpp @@ -76,6 +76,11 @@ bool NimToolChain::isValid() const return fi.isExecutable(); } +ToolChain::PredefinedMacrosRunner NimToolChain::createPredefinedMacrosRunner() const +{ + return ToolChain::PredefinedMacrosRunner(); +} + QByteArray NimToolChain::predefinedMacros(const QStringList &) const { return QByteArray(); @@ -91,6 +96,11 @@ WarningFlags NimToolChain::warningFlags(const QStringList &) const return WarningFlags::NoWarnings; } +ToolChain::SystemHeaderPathsRunner NimToolChain::createSystemHeaderPathsRunner() const +{ + return ToolChain::SystemHeaderPathsRunner(); +} + QList NimToolChain::systemHeaderPaths(const QStringList &, const FileName &) const { return QList(); diff --git a/src/plugins/nim/project/nimtoolchain.h b/src/plugins/nim/project/nimtoolchain.h index 2bb59aac82d..7061a9751bc 100644 --- a/src/plugins/nim/project/nimtoolchain.h +++ b/src/plugins/nim/project/nimtoolchain.h @@ -40,10 +40,12 @@ public: ProjectExplorer::Abi targetAbi() const override; bool isValid() const override; + PredefinedMacrosRunner createPredefinedMacrosRunner() const override; QByteArray predefinedMacros(const QStringList &flags) const final; CompilerFlags compilerFlags(const QStringList &flags) const final; ProjectExplorer::WarningFlags warningFlags(const QStringList &flags) const final; + SystemHeaderPathsRunner createSystemHeaderPathsRunner() const override; QList systemHeaderPaths(const QStringList &flags, const Utils::FileName &sysRoot) const final; void addToEnvironment(Utils::Environment &env) const final; diff --git a/src/plugins/projectexplorer/abstractmsvctoolchain.cpp b/src/plugins/projectexplorer/abstractmsvctoolchain.cpp index 551a316bba4..c66be6cc4b2 100644 --- a/src/plugins/projectexplorer/abstractmsvctoolchain.cpp +++ b/src/plugins/projectexplorer/abstractmsvctoolchain.cpp @@ -47,7 +47,9 @@ namespace Internal { AbstractMsvcToolChain::AbstractMsvcToolChain(Core::Id typeId, Core::Id l, Detection d, const Abi &abi, const QString& vcvarsBat) : ToolChain(typeId, d), + m_predefinedMacrosMutex(new QMutex), m_lastEnvironment(Utils::Environment::systemEnvironment()), + m_headerPathsMutex(new QMutex), m_abi(abi), m_vcvarsBat(vcvarsBat) { @@ -63,6 +65,25 @@ AbstractMsvcToolChain::AbstractMsvcToolChain(Core::Id typeId, Detection d) : m_lastEnvironment(Utils::Environment::systemEnvironment()) { } +AbstractMsvcToolChain::~AbstractMsvcToolChain() +{ + delete m_predefinedMacrosMutex; + delete m_headerPathsMutex; +} + +AbstractMsvcToolChain::AbstractMsvcToolChain(const AbstractMsvcToolChain &other) + : ToolChain(other), + m_debuggerCommand(other.m_debuggerCommand), + m_predefinedMacrosMutex(new QMutex), + m_predefinedMacros(other.m_predefinedMacros), + m_lastEnvironment(other.m_lastEnvironment), + m_resultEnvironment(other.m_resultEnvironment), + m_headerPathsMutex(new QMutex), + m_abi(other.m_abi), + m_vcvarsBat(other.m_vcvarsBat) +{ +} + Abi AbstractMsvcToolChain::targetAbi() const { return m_abi; @@ -76,14 +97,23 @@ bool AbstractMsvcToolChain::isValid() const return fi.isFile() && fi.isExecutable(); } +ToolChain::PredefinedMacrosRunner AbstractMsvcToolChain::createPredefinedMacrosRunner() const +{ + Utils::Environment env(m_lastEnvironment); + addToEnvironment(env); + + // This runner must be thread-safe! + return [this, env](const QStringList &cxxflags) { + QMutexLocker locker(m_predefinedMacrosMutex); + if (m_predefinedMacros.isEmpty()) + m_predefinedMacros = msvcPredefinedMacros(cxxflags, env); + return m_predefinedMacros; + }; +} + QByteArray AbstractMsvcToolChain::predefinedMacros(const QStringList &cxxflags) const { - if (m_predefinedMacros.isEmpty()) { - Utils::Environment env(m_lastEnvironment); - addToEnvironment(env); - m_predefinedMacros = msvcPredefinedMacros(cxxflags, env); - } - return m_predefinedMacros; + return createPredefinedMacrosRunner()(cxxflags); } ToolChain::CompilerFlags AbstractMsvcToolChain::compilerFlags(const QStringList &cxxflags) const @@ -160,17 +190,25 @@ WarningFlags AbstractMsvcToolChain::warningFlags(const QStringList &cflags) cons return flags; } -QList AbstractMsvcToolChain::systemHeaderPaths(const QStringList &cxxflags, const Utils::FileName &sysRoot) const +ToolChain::SystemHeaderPathsRunner AbstractMsvcToolChain::createSystemHeaderPathsRunner() const { - Q_UNUSED(cxxflags); - Q_UNUSED(sysRoot); - if (m_headerPaths.isEmpty()) { - Utils::Environment env(m_lastEnvironment); - addToEnvironment(env); - foreach (const QString &path, env.value(QLatin1String("INCLUDE")).split(QLatin1Char(';'))) - m_headerPaths.append(HeaderPath(path, HeaderPath::GlobalHeaderPath)); - } - return m_headerPaths; + Utils::Environment env(m_lastEnvironment); + addToEnvironment(env); + + return [this, env](const QStringList &, const QString &) { + QMutexLocker locker(m_headerPathsMutex); + if (m_headerPaths.isEmpty()) { + foreach (const QString &path, env.value(QLatin1String("INCLUDE")).split(QLatin1Char(';'))) + m_headerPaths.append(HeaderPath(path, HeaderPath::GlobalHeaderPath)); + } + return m_headerPaths; + }; +} + +QList AbstractMsvcToolChain::systemHeaderPaths(const QStringList &cxxflags, + const Utils::FileName &sysRoot) const +{ + return createSystemHeaderPathsRunner()(cxxflags, sysRoot.toString()); } void AbstractMsvcToolChain::addToEnvironment(Utils::Environment &env) const @@ -231,6 +269,7 @@ bool AbstractMsvcToolChain::canClone() const return true; } +// Function must be thread-safe! QByteArray AbstractMsvcToolChain::msvcPredefinedMacros(const QStringList cxxflags, const Utils::Environment& env) const { diff --git a/src/plugins/projectexplorer/abstractmsvctoolchain.h b/src/plugins/projectexplorer/abstractmsvctoolchain.h index 653caed65b9..93556320df5 100644 --- a/src/plugins/projectexplorer/abstractmsvctoolchain.h +++ b/src/plugins/projectexplorer/abstractmsvctoolchain.h @@ -29,6 +29,8 @@ #include "abi.h" #include "headerpath.h" +#include + #include #include @@ -41,14 +43,18 @@ public: explicit AbstractMsvcToolChain(Core::Id typeId, Core::Id l, Detection d, const Abi &abi, const QString& vcvarsBat); explicit AbstractMsvcToolChain(Core::Id typeId, Detection d); + AbstractMsvcToolChain(const AbstractMsvcToolChain &other); + ~AbstractMsvcToolChain(); Abi targetAbi() const override; bool isValid() const override; + PredefinedMacrosRunner createPredefinedMacrosRunner() const override; QByteArray predefinedMacros(const QStringList &cxxflags) const override; CompilerFlags compilerFlags(const QStringList &cxxflags) const override; WarningFlags warningFlags(const QStringList &cflags) const override; + SystemHeaderPathsRunner createSystemHeaderPathsRunner() const override; QList systemHeaderPaths(const QStringList &cxxflags, const Utils::FileName &sysRoot) const override; void addToEnvironment(Utils::Environment &env) const override; @@ -89,9 +95,11 @@ protected: Utils::FileName m_debuggerCommand; + mutable QMutex *m_predefinedMacrosMutex = nullptr; mutable QByteArray m_predefinedMacros; mutable Utils::Environment m_lastEnvironment; // Last checked 'incoming' environment. mutable Utils::Environment m_resultEnvironment; // Resulting environment for VC + mutable QMutex *m_headerPathsMutex = nullptr; mutable QList m_headerPaths; Abi m_abi; diff --git a/src/plugins/projectexplorer/customtoolchain.cpp b/src/plugins/projectexplorer/customtoolchain.cpp index 3e86571f8fd..aaa545dfd1d 100644 --- a/src/plugins/projectexplorer/customtoolchain.cpp +++ b/src/plugins/projectexplorer/customtoolchain.cpp @@ -115,33 +115,43 @@ bool CustomToolChain::isValid() const return true; } -QByteArray CustomToolChain::predefinedMacros(const QStringList &cxxflags) const +ToolChain::PredefinedMacrosRunner CustomToolChain::createPredefinedMacrosRunner() const { - QByteArray result; - QStringList macros = m_predefinedMacros; - foreach (const QString &cxxFlag, cxxflags) { - if (cxxFlag.startsWith(QLatin1String("-D"))) { - macros << cxxFlag.mid(2).trimmed(); - } else if (cxxFlag.startsWith(QLatin1String("-U"))) { - const QString &removedName = cxxFlag.mid(2).trimmed(); - for (int i = macros.size() - 1; i >= 0; --i) { - const QString &m = macros.at(i); - if (m.left(m.indexOf(QLatin1Char('='))) == removedName) - macros.removeAt(i); + const QStringList theMacros = m_predefinedMacros; + + // This runner must be thread-safe! + return [theMacros](const QStringList &cxxflags){ + QByteArray result; + QStringList macros = theMacros; + foreach (const QString &cxxFlag, cxxflags) { + if (cxxFlag.startsWith(QLatin1String("-D"))) { + macros << cxxFlag.mid(2).trimmed(); + } else if (cxxFlag.startsWith(QLatin1String("-U"))) { + const QString &removedName = cxxFlag.mid(2).trimmed(); + for (int i = macros.size() - 1; i >= 0; --i) { + const QString &m = macros.at(i); + if (m.left(m.indexOf(QLatin1Char('='))) == removedName) + macros.removeAt(i); + } } } - } - foreach (const QString &str, macros) { - QByteArray ba = str.toUtf8(); - int equals = ba.indexOf('='); - if (equals == -1) { - result += "#define " + ba.trimmed() + '\n'; - } else { - result += "#define " + ba.left(equals).trimmed() + ' ' - + ba.mid(equals + 1).trimmed() + '\n'; + foreach (const QString &str, macros) { + QByteArray ba = str.toUtf8(); + int equals = ba.indexOf('='); + if (equals == -1) { + result += "#define " + ba.trimmed() + '\n'; + } else { + result += "#define " + ba.left(equals).trimmed() + ' ' + + ba.mid(equals + 1).trimmed() + '\n'; + } } - } - return result; + return result; + }; +} + +QByteArray CustomToolChain::predefinedMacros(const QStringList &cxxflags) const +{ + return createPredefinedMacrosRunner()(cxxflags); } ToolChain::CompilerFlags CustomToolChain::compilerFlags(const QStringList &cxxflags) const @@ -171,15 +181,26 @@ void CustomToolChain::setPredefinedMacros(const QStringList &list) toolChainUpdated(); } -QList CustomToolChain::systemHeaderPaths(const QStringList &cxxFlags, const FileName &) const +ToolChain::SystemHeaderPathsRunner CustomToolChain::createSystemHeaderPathsRunner() const { - QList flagHeaderPaths; - foreach (const QString &cxxFlag, cxxFlags) { - if (cxxFlag.startsWith(QLatin1String("-I"))) - flagHeaderPaths << HeaderPath(cxxFlag.mid(2).trimmed(), HeaderPath::GlobalHeaderPath); - } + const QList systemHeaderPaths = m_systemHeaderPaths; - return m_systemHeaderPaths + flagHeaderPaths; + // This runner must be thread-safe! + return [systemHeaderPaths](const QStringList &cxxFlags, const QString &) { + QList flagHeaderPaths; + foreach (const QString &cxxFlag, cxxFlags) { + if (cxxFlag.startsWith(QLatin1String("-I"))) + flagHeaderPaths << HeaderPath(cxxFlag.mid(2).trimmed(), HeaderPath::GlobalHeaderPath); + } + + return systemHeaderPaths + flagHeaderPaths; + }; +} + +QList CustomToolChain::systemHeaderPaths(const QStringList &cxxFlags, + const FileName &fileName) const +{ + return createSystemHeaderPathsRunner()(cxxFlags, fileName.toString()); } void CustomToolChain::addToEnvironment(Environment &env) const diff --git a/src/plugins/projectexplorer/customtoolchain.h b/src/plugins/projectexplorer/customtoolchain.h index de57c637f4a..a1dfe319bde 100644 --- a/src/plugins/projectexplorer/customtoolchain.h +++ b/src/plugins/projectexplorer/customtoolchain.h @@ -77,12 +77,14 @@ public: bool isValid() const override; + PredefinedMacrosRunner createPredefinedMacrosRunner() const override; QByteArray predefinedMacros(const QStringList &cxxflags) const override; CompilerFlags compilerFlags(const QStringList &cxxflags) const override; WarningFlags warningFlags(const QStringList &cxxflags) const override; const QStringList &rawPredefinedMacros() const; void setPredefinedMacros(const QStringList &list); + SystemHeaderPathsRunner createSystemHeaderPathsRunner() const override; QList systemHeaderPaths(const QStringList &cxxFlags, const Utils::FileName &) const override; void addToEnvironment(Utils::Environment &env) const override; diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index fa79ae5ce1c..da6cbeff2bc 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -66,6 +66,105 @@ static const char targetAbiKeyC[] = "ProjectExplorer.GccToolChain.TargetAbi"; static const char originalTargetTripleKeyC[] = "ProjectExplorer.GccToolChain.OriginalTargetTriple"; static const char supportedAbisKeyC[] = "ProjectExplorer.GccToolChain.SupportedAbis"; +static const int CACHE_SIZE = 16; + +HeaderPathsCache::HeaderPathsCache(const HeaderPathsCache &other) +{ + QMutexLocker locker(&m_mutex); + m_cache = other.cache(); +} + +void HeaderPathsCache::insert(const QStringList &compilerCommand, + const QList &headerPaths) +{ + CacheItem runResults; + runResults.first = compilerCommand; + runResults.second = headerPaths; + + QMutexLocker locker(&m_mutex); + bool cacheHit = false; + check(compilerCommand, &cacheHit); + if (!cacheHit) { + m_cache.push_back(runResults); + if (m_cache.size() > CACHE_SIZE) + m_cache.pop_front(); + } +} + +QList HeaderPathsCache::check(const QStringList &compilerCommand, + bool *cacheHit) const +{ + QMutexLocker locker(&m_mutex); + for (Cache::iterator it = m_cache.begin(); it != m_cache.end(); ++it) { + if (it->first == compilerCommand) { + // Increase cached item priority + CacheItem pair = *it; + m_cache.erase(it); + m_cache.push_back(pair); + + *cacheHit = true; + return pair.second; + } + } + + *cacheHit = false; + return QList(); +} + +HeaderPathsCache::Cache HeaderPathsCache::cache() const +{ + QMutexLocker locker(&m_mutex); + return m_cache; +} + +MacroCache::MacroCache(const MacroCache &other) +{ + QMutexLocker locker(&m_mutex); + m_cache = other.cache(); +} + +void MacroCache::insert(const QStringList &compilerCommand, const QByteArray ¯os) +{ + if (macros.isNull()) + return; + + CacheItem runResults; + QByteArray data = macros; + runResults.first = compilerCommand; + if (macros.isNull()) + data = QByteArray(""); + runResults.second = data; + + QMutexLocker locker(&m_mutex); + if (check(compilerCommand).isNull()) { + m_cache.push_back(runResults); + if (m_cache.size() > CACHE_SIZE) + m_cache.pop_front(); + } +} + +QByteArray MacroCache::check(const QStringList &compilerCommand) const +{ + QMutexLocker locker(&m_mutex); + for (Cache::iterator it = m_cache.begin(); it != m_cache.end(); ++it) { + if (it->first == compilerCommand) { + // Increase cached item priority + CacheItem pair = *it; + m_cache.erase(it); + m_cache.push_back(pair); + + return pair.second; + } + } + return QByteArray(); +} + +MacroCache::Cache MacroCache::cache() const +{ + QMutexLocker locker(&m_mutex); + return m_cache; +} + static QByteArray runGcc(const FileName &gcc, const QStringList &arguments, const QStringList &env) { if (gcc.isEmpty() || !gcc.toFileInfo().isExecutable()) @@ -115,8 +214,6 @@ static QByteArray gccPredefinedMacros(const FileName &gcc, const QStringList &ar return predefinedMacros; } -const int GccToolChain::PREDEFINED_MACROS_CACHE_SIZE = 16; - QList GccToolChain::gccHeaderPaths(const FileName &gcc, const QStringList &arguments, const QStringList &env) { @@ -252,38 +349,6 @@ void GccToolChain::setOriginalTargetTriple(const QString &targetTriple) toolChainUpdated(); } -void GccToolChain::setMacroCache(const QStringList &allCxxflags, const QByteArray ¯os) const -{ - if (macros.isNull()) - return; - - CacheItem runResults; - QByteArray data = macros; - runResults.first = allCxxflags; - if (macros.isNull()) - data = QByteArray(""); - runResults.second = data; - - m_predefinedMacros.push_back(runResults); - if (m_predefinedMacros.size() > PREDEFINED_MACROS_CACHE_SIZE) - m_predefinedMacros.pop_front(); -} - -QByteArray GccToolChain::macroCache(const QStringList &allCxxflags) const -{ - for (GccCache::iterator it = m_predefinedMacros.begin(); it != m_predefinedMacros.end(); ++it) { - if (it->first == allCxxflags) { - // Increase cached item priority - CacheItem pair = *it; - m_predefinedMacros.erase(it); - m_predefinedMacros.push_back(pair); - - return pair.second; - } - } - return QByteArray(); -} - QString GccToolChain::defaultDisplayName() const { if (!m_targetAbi.isValid()) @@ -348,6 +413,64 @@ bool GccToolChain::isValid() const return fi.isExecutable(); } +ToolChain::PredefinedMacrosRunner GccToolChain::createPredefinedMacrosRunner() const +{ + // Using a clean environment breaks ccache/distcc/etc. + Environment env = Environment::systemEnvironment(); + addToEnvironment(env); + const Utils::FileName compilerCommand = m_compilerCommand; + const QStringList platformCodeGenFlags = m_platformCodeGenFlags; + OptionsReinterpreter reinterpretOptions = m_optionsReinterpreter; + QTC_CHECK(reinterpretOptions); + MacroCache *macroCache = &m_predefinedMacrosCache; + + // This runner must be thread-safe! + return [env, compilerCommand, platformCodeGenFlags, reinterpretOptions, macroCache] + (const QStringList &cxxflags) { + QStringList allCxxflags = platformCodeGenFlags + cxxflags; // add only cxxflags is empty? + QStringList arguments = gccPredefinedMacrosOptions(); + for (int iArg = 0; iArg < allCxxflags.length(); ++iArg) { + const QString &a = allCxxflags.at(iArg); + if (a.startsWith("--gcc-toolchain=")) { + arguments << a; + } else if (a == "-arch") { + if (++iArg < allCxxflags.length() && !arguments.contains(a)) + arguments << a << allCxxflags.at(iArg); + } else if (a == "--sysroot" || a == "-isysroot" || a == "-D" ||a == "-U") { + if (++iArg < allCxxflags.length()) + arguments << a << allCxxflags.at(iArg); + } else if (a == "-m128bit-long-double" || a == "-m32" || a == "-m3dnow" || a == "-m3dnowa" + || a == "-m64" || a == "-m96bit-long-double" || a == "-mabm" || a == "-maes" + || a.startsWith("-march=") || a == "-mavx" || a.startsWith("-masm=") + || a == "-mcx16" || a == "-mfma" || a == "-mfma4" || a == "-mlwp" + || a == "-mpclmul" || a == "-mpopcnt" || a == "-msse" || a == "-msse2" + || a == "-msse2avx" || a == "-msse3" || a == "-msse4" || a == "-msse4.1" + || a == "-msse4.2" || a == "-msse4a" || a == "-mssse3" + || a.startsWith("-mtune=") || a == "-mxop" + || a == "-Os" || a == "-O0" || a == "-O1" || a == "-O2" || a == "-O3" + || a == "-ffinite-math-only" || a == "-fshort-double" || a == "-fshort-wchar" + || a == "-fsignaling-nans" || a == "-fno-inline" || a == "-fno-exceptions" + || a == "-fstack-protector" || a == "-fstack-protector-all" + || a == "-fsanitize=address" || a == "-fno-rtti" + || a.startsWith("-std=") || a.startsWith("-stdlib=") || a.startsWith("-specs=") + || a == "-ansi" || a == "-undef" || a.startsWith("-D") || a.startsWith("-U") + || a == "-fopenmp" || a == "-Wno-deprecated" || a == "-fPIC" || a == "-fpic" + || a == "-fPIE" || a == "-fpie") + arguments << a; + } + + arguments = reinterpretOptions(arguments); + QByteArray macros = macroCache->check(arguments); + if (!macros.isNull()) + return macros; + + macros = gccPredefinedMacros(compilerCommand, arguments, env.toStringList()); + macroCache->insert(arguments, macros); + + return macros; + }; +} + /** * @brief Asks compiler for set of predefined macros * @param cxxflags - compiler flags collected from project settings @@ -359,50 +482,7 @@ bool GccToolChain::isValid() const */ QByteArray GccToolChain::predefinedMacros(const QStringList &cxxflags) const { - QStringList allCxxflags = m_platformCodeGenFlags + cxxflags; // add only cxxflags is empty? - QStringList arguments = gccPredefinedMacrosOptions(); - for (int iArg = 0; iArg < allCxxflags.length(); ++iArg) { - const QString &a = allCxxflags.at(iArg); - if (a.startsWith("--gcc-toolchain=")) { - arguments << a; - } else if (a == "-arch") { - if (++iArg < allCxxflags.length() && !arguments.contains(a)) - arguments << a << allCxxflags.at(iArg); - } else if (a == "--sysroot" || a == "-isysroot" || a == "-D" ||a == "-U") { - if (++iArg < allCxxflags.length()) - arguments << a << allCxxflags.at(iArg); - } else if (a == "-m128bit-long-double" || a == "-m32" || a == "-m3dnow" || a == "-m3dnowa" - || a == "-m64" || a == "-m96bit-long-double" || a == "-mabm" || a == "-maes" - || a.startsWith("-march=") || a == "-mavx" || a.startsWith("-masm=") - || a == "-mcx16" || a == "-mfma" || a == "-mfma4" || a == "-mlwp" - || a == "-mpclmul" || a == "-mpopcnt" || a == "-msse" || a == "-msse2" - || a == "-msse2avx" || a == "-msse3" || a == "-msse4" || a == "-msse4.1" - || a == "-msse4.2" || a == "-msse4a" || a == "-mssse3" - || a.startsWith("-mtune=") || a == "-mxop" - || a == "-Os" || a == "-O0" || a == "-O1" || a == "-O2" || a == "-O3" - || a == "-ffinite-math-only" || a == "-fshort-double" || a == "-fshort-wchar" - || a == "-fsignaling-nans" || a == "-fno-inline" || a == "-fno-exceptions" - || a == "-fstack-protector" || a == "-fstack-protector-all" - || a == "-fsanitize=address" || a == "-fno-rtti" - || a.startsWith("-std=") || a.startsWith("-stdlib=") || a.startsWith("-specs=") - || a == "-ansi" || a == "-undef" || a.startsWith("-D") || a.startsWith("-U") - || a == "-fopenmp" || a == "-Wno-deprecated" || a == "-fPIC" || a == "-fpic" - || a == "-fPIE" || a == "-fpie") - arguments << a; - } - - QByteArray macros = macroCache(arguments); - if (!macros.isNull()) - return macros; - - // Using a clean environment breaks ccache/distcc/etc. - Environment env = Environment::systemEnvironment(); - addToEnvironment(env); - macros = gccPredefinedMacros(m_compilerCommand, reinterpretOptions(arguments), - env.toStringList()); - - setMacroCache(arguments, macros); - return macros; + return createPredefinedMacrosRunner()(cxxflags); } /** @@ -516,29 +596,52 @@ WarningFlags GccToolChain::warningFlags(const QStringList &cflags) const return flags; } -QList GccToolChain::systemHeaderPaths(const QStringList &cxxflags, const FileName &sysRoot) const +ToolChain::SystemHeaderPathsRunner GccToolChain::createSystemHeaderPathsRunner() const { - if (m_headerPaths.isEmpty()) { - // Using a clean environment breaks ccache/distcc/etc. - Environment env = Environment::systemEnvironment(); - addToEnvironment(env); + // Using a clean environment breaks ccache/distcc/etc. + Environment env = Environment::systemEnvironment(); + addToEnvironment(env); + + const Utils::FileName compilerCommand = m_compilerCommand; + const QStringList platformCodeGenFlags = m_platformCodeGenFlags; + OptionsReinterpreter reinterpretOptions = m_optionsReinterpreter; + QTC_CHECK(reinterpretOptions); + HeaderPathsCache *headerCache = &m_headerPathsCache; + + // This runner must be thread-safe! + return [env, compilerCommand, platformCodeGenFlags, reinterpretOptions, headerCache] + (const QStringList &cxxflags, const QString &sysRoot) { // Prepare arguments QStringList arguments; if (!sysRoot.isEmpty()) - arguments.append(QString::fromLatin1("--sysroot=%1").arg(sysRoot.toString())); + arguments.append(QString::fromLatin1("--sysroot=%1").arg(sysRoot)); QStringList flags; - flags << m_platformCodeGenFlags << cxxflags; + flags << platformCodeGenFlags << cxxflags; foreach (const QString &a, flags) { if (a.startsWith("-stdlib=") || a.startsWith("--gcctoolchain=")) arguments << a; } arguments << "-xc++" << "-E" << "-v" << "-"; + arguments = reinterpretOptions(arguments); - m_headerPaths = gccHeaderPaths(m_compilerCommand, reinterpretOptions(arguments), env.toStringList()); - } - return m_headerPaths; + bool cacheHit = false; + QList paths = headerCache->check(arguments, &cacheHit); + if (cacheHit) + return paths; + + paths = gccHeaderPaths(compilerCommand, arguments, env.toStringList()); + headerCache->insert(arguments, paths); + + return paths; + }; +} + +QList GccToolChain::systemHeaderPaths(const QStringList &cxxflags, + const FileName &sysRoot) const +{ + return createSystemHeaderPathsRunner()(cxxflags, sysRoot.toString()); } void GccToolChain::addCommandPathToEnvironment(const FileName &command, Environment &env) @@ -735,6 +838,11 @@ void GccToolChain::updateSupportedAbis() const } } +void GccToolChain::setOptionsReinterpreter(const OptionsReinterpreter &optionsReinterpreter) +{ + m_optionsReinterpreter = optionsReinterpreter; +} + GccToolChain::DetectedAbisResult GccToolChain::detectSupportedAbis() const { Environment env = Environment::systemEnvironment(); @@ -892,7 +1000,7 @@ QList GccToolChainFactory::autoDetectToolChain(const FileName &comp return result; tc->setLanguage(language); - tc->setMacroCache(QStringList(), macros); + tc->m_predefinedMacrosCache.insert(QStringList(), macros); tc->setCompilerCommand(compilerPath); tc->setSupportedAbis(detectedAbis.supportedAbis); tc->setTargetAbi(abi); @@ -957,7 +1065,7 @@ void GccToolChainConfigWidget::applyImpl() tc->setDisplayName(displayName); // reset display name tc->setPlatformCodeGenFlags(splitString(m_platformCodeGenFlagsLineEdit->text())); tc->setPlatformLinkerFlags(splitString(m_platformLinkerFlagsLineEdit->text())); - tc->setMacroCache(tc->platformCodeGenFlags(), m_macros); + tc->m_predefinedMacrosCache.insert(tc->platformCodeGenFlags(), m_macros); } void GccToolChainConfigWidget::setFromToolchain() diff --git a/src/plugins/projectexplorer/gcctoolchain.h b/src/plugins/projectexplorer/gcctoolchain.h index 2d7bfc70cad..be2ef37b246 100644 --- a/src/plugins/projectexplorer/gcctoolchain.h +++ b/src/plugins/projectexplorer/gcctoolchain.h @@ -32,8 +32,11 @@ #include "headerpath.h" #include +#include #include +#include + namespace ProjectExplorer { namespace Internal { @@ -48,6 +51,42 @@ class LinuxIccToolChainFactory; // GccToolChain // -------------------------------------------------------------------------- +class PROJECTEXPLORER_EXPORT HeaderPathsCache +{ +public: + HeaderPathsCache() : m_mutex(QMutex::Recursive) {} + HeaderPathsCache(const HeaderPathsCache &other); + void insert(const QStringList &compilerCommand, const QList &headerPaths); + QList check(const QStringList &compilerCommand, bool *cacheHit) const; + +protected: + using CacheItem = QPair>; + using Cache = QList; + Cache cache() const; + +private: + mutable QMutex m_mutex; + mutable Cache m_cache; +}; + +class PROJECTEXPLORER_EXPORT MacroCache +{ +public: + MacroCache() : m_mutex(QMutex::Recursive) {} + MacroCache(const MacroCache &other); + void insert(const QStringList &compilerCommand, const QByteArray ¯os); + QByteArray check(const QStringList &compilerCommand) const; + +protected: + using CacheItem = QPair; + using Cache = QList; + Cache cache() const; + +private: + mutable QMutex m_mutex; + mutable Cache m_cache; +}; + class PROJECTEXPLORER_EXPORT GccToolChain : public ToolChain { public: @@ -61,12 +100,16 @@ public: bool isValid() const override; - QByteArray predefinedMacros(const QStringList &cxxflags) const override; CompilerFlags compilerFlags(const QStringList &cxxflags) const override; WarningFlags warningFlags(const QStringList &cflags) const override; + PredefinedMacrosRunner createPredefinedMacrosRunner() const override; + QByteArray predefinedMacros(const QStringList &cxxflags) const override; + + SystemHeaderPathsRunner createSystemHeaderPathsRunner() const override; QList systemHeaderPaths(const QStringList &cxxflags, const Utils::FileName &sysRoot) const override; + void addToEnvironment(Utils::Environment &env) const override; QString makeCommand(const Utils::Environment &environment) const override; Utils::FileNameList suggestedMkspecList() const override; @@ -104,16 +147,13 @@ public: }; protected: - typedef QList > GccCache; - GccToolChain(const GccToolChain &) = default; - typedef QPair CacheItem; - void setCompilerCommand(const Utils::FileName &path); void setSupportedAbis(const QList &m_abis); void setOriginalTargetTriple(const QString &targetTriple); - void setMacroCache(const QStringList &allCxxflags, const QByteArray ¯oCache) const; + + void setMacroCache(const QStringList &allCxxflags, const QByteArray ¯os) const; QByteArray macroCache(const QStringList &allCxxflags) const; virtual QString defaultDisplayName() const; @@ -124,12 +164,10 @@ protected: // Reinterpret options for compiler drivers inheriting from GccToolChain (e.g qcc) to apply -Wp option // that passes the initial options directly down to the gcc compiler - virtual QStringList reinterpretOptions(const QStringList &argument) const { return argument; } + using OptionsReinterpreter = std::function; + void setOptionsReinterpreter(const OptionsReinterpreter &optionsReinterpreter); static QList gccHeaderPaths(const Utils::FileName &gcc, const QStringList &args, const QStringList &env); - static const int PREDEFINED_MACROS_CACHE_SIZE; - mutable GccCache m_predefinedMacros; - class WarningFlagAdder { public: @@ -153,12 +191,17 @@ private: QStringList m_platformCodeGenFlags; QStringList m_platformLinkerFlags; + OptionsReinterpreter m_optionsReinterpreter = [](const QStringList &v) { return v; }; + Abi m_targetAbi; mutable QList m_supportedAbis; mutable QString m_originalTargetTriple; mutable QList m_headerPaths; mutable QString m_version; + mutable MacroCache m_predefinedMacrosCache; + mutable HeaderPathsCache m_headerPathsCache; + friend class Internal::GccToolChainConfigWidget; friend class Internal::GccToolChainFactory; friend class ToolChainFactory; diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 3d5d3410193..3306d896ac4 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -287,6 +287,7 @@ static QByteArray msvcCompilationFile() } // Run MSVC 'cl' compiler to obtain #defines. +// Function must be thread-safe! QByteArray MsvcToolChain::msvcPredefinedMacros(const QStringList cxxflags, const Utils::Environment &env) const { diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h index f77a1637fee..e7973800c3a 100644 --- a/src/plugins/projectexplorer/msvctoolchain.h +++ b/src/plugins/projectexplorer/msvctoolchain.h @@ -81,6 +81,7 @@ protected: explicit MsvcToolChain(Core::Id typeId); Utils::Environment readEnvironmentSetting(const Utils::Environment& env) const final; + // Function must be thread-safe! QByteArray msvcPredefinedMacros(const QStringList cxxflags, const Utils::Environment &env) const override; diff --git a/src/plugins/projectexplorer/toolchain.h b/src/plugins/projectexplorer/toolchain.h index 49004f37d09..abc6f115580 100644 --- a/src/plugins/projectexplorer/toolchain.h +++ b/src/plugins/projectexplorer/toolchain.h @@ -37,6 +37,8 @@ #include #include +#include + namespace Utils { class Environment; } namespace ProjectExplorer { @@ -99,8 +101,6 @@ public: virtual bool isValid() const = 0; - virtual QByteArray predefinedMacros(const QStringList &cxxflags) const = 0; - enum CompilerFlag { NoFlags = 0, StandardCxx11 = 0x1, @@ -118,8 +118,16 @@ public: Q_DECLARE_FLAGS(CompilerFlags, CompilerFlag) virtual CompilerFlags compilerFlags(const QStringList &cxxflags) const = 0; - virtual WarningFlags warningFlags(const QStringList &cflags) const = 0; + + // A PredefinedMacrosRunner is created in the ui thread and runs in another thread. + using PredefinedMacrosRunner = std::function; + virtual PredefinedMacrosRunner createPredefinedMacrosRunner() const = 0; + virtual QByteArray predefinedMacros(const QStringList &cxxflags) const = 0; + + // A SystemHeaderPathsRunner is created in the ui thread and runs in another thread. + using SystemHeaderPathsRunner = std::function(const QStringList &cxxflags, const QString &sysRoot)>; + virtual SystemHeaderPathsRunner createSystemHeaderPathsRunner() const = 0; virtual QList systemHeaderPaths(const QStringList &cxxflags, const Utils::FileName &sysRoot) const = 0; virtual void addToEnvironment(Utils::Environment &env) const = 0; diff --git a/src/plugins/projectexplorer/toolchainmanager.cpp b/src/plugins/projectexplorer/toolchainmanager.cpp index fd8c2ea2315..f1ae19fe411 100644 --- a/src/plugins/projectexplorer/toolchainmanager.cpp +++ b/src/plugins/projectexplorer/toolchainmanager.cpp @@ -491,9 +491,11 @@ public: QString typeDisplayName() const override { return QLatin1String("Test Tool Chain"); } Abi targetAbi() const override { return Abi::hostAbi(); } bool isValid() const override { return m_valid; } + PredefinedMacrosRunner createPredefinedMacrosRunner() const override { return PredefinedMacrosRunner(); } QByteArray predefinedMacros(const QStringList &cxxflags) const override { Q_UNUSED(cxxflags); return QByteArray(); } CompilerFlags compilerFlags(const QStringList &cxxflags) const override { Q_UNUSED(cxxflags); return NoFlags; } WarningFlags warningFlags(const QStringList &cflags) const override { Q_UNUSED(cflags); return WarningFlags::NoWarnings; } + SystemHeaderPathsRunner createSystemHeaderPathsRunner() const override { return SystemHeaderPathsRunner(); } QList systemHeaderPaths(const QStringList &cxxflags, const FileName &sysRoot) const override { Q_UNUSED(cxxflags); Q_UNUSED(sysRoot); return QList(); } void addToEnvironment(Environment &env) const override { Q_UNUSED(env); } diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp index 0ccd2d731e4..438c40f2336 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.cpp +++ b/src/plugins/qbsprojectmanager/qbsproject.cpp @@ -43,7 +43,7 @@ #include #include #include -#include +#include #include #include #include @@ -123,6 +123,7 @@ QbsProject::QbsProject(QbsManager *manager, const QString &fileName) : m_qbsUpdateFutureInterface(0), m_parsingScheduled(false), m_cancelStatus(CancelStatusNone), + m_cppCodeModelUpdater(new CppTools::CppProjectUpdater(this)), m_currentBc(0), m_extraCompilersPending(false) { @@ -143,11 +144,16 @@ QbsProject::QbsProject(QbsManager *manager, const QString &fileName) : connect(this, &Project::environmentChanged, this, &QbsProject::delayParsing); connect(&m_parsingDelay, &QTimer::timeout, this, &QbsProject::startParsing); + + connect(m_cppCodeModelUpdater, &CppTools::CppProjectUpdater::projectInfoUpdated, this, + [this](const CppTools::ProjectInfo &projectInfo){ + m_cppCodeModelProjectInfo = projectInfo; + }); } QbsProject::~QbsProject() { - m_codeModelFuture.cancel(); + delete m_cppCodeModelUpdater; delete m_qbsProjectParser; if (m_qbsUpdateFutureInterface) { m_qbsUpdateFutureInterface->reportCanceled(); @@ -895,20 +901,27 @@ void QbsProject::updateCppCodeModel() if (!m_projectData.isValid()) return; + const Kit *k = nullptr; + if (Target *target = activeTarget()) + k = target->kit(); + else + k = KitManager::defaultKit(); + QTC_ASSERT(k, return); + + ToolChain *cToolChain + = ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::C_LANGUAGE_ID); + ToolChain *cxxToolChain + = ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::CXX_LANGUAGE_ID); + QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(activeTarget()->kit()); - CppTools::CppModelManager *modelmanager = CppTools::CppModelManager::instance(); - CppTools::ProjectInfo pinfo(this); - CppTools::ProjectPartBuilder ppBuilder(pinfo); - + CppTools::ProjectPart::QtVersion qtVersionForPart = CppTools::ProjectPart::NoQt; if (qtVersion) { if (qtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0)) - ppBuilder.setQtVersion(CppTools::ProjectPart::Qt4); + qtVersionForPart = CppTools::ProjectPart::Qt4; else - ppBuilder.setQtVersion(CppTools::ProjectPart::Qt5); - } else { - ppBuilder.setQtVersion(CppTools::ProjectPart::NoQt); + qtVersionForPart = CppTools::ProjectPart::Qt5; } QList factories = @@ -918,6 +931,8 @@ void QbsProject::updateCppCodeModel() qDeleteAll(m_extraCompilers); m_extraCompilers.clear(); + + CppTools::RawProjectParts rpps; foreach (const qbs::ProductData &prd, m_projectData.allProducts()) { QString cPch; QString cxxPch; @@ -941,13 +956,16 @@ void QbsProject::updateCppCodeModel() } foreach (const qbs::GroupData &grp, prd.groups()) { + CppTools::RawProjectPart rpp; + // TODO: Set the Qt version only if this particular product depends on Qt. + rpp.setQtVersion(qtVersionForPart); const qbs::PropertyMap &props = grp.properties(); QStringList cFlags; QStringList cxxFlags; getExpandedCompilerFlags(cFlags, cxxFlags, props); - ppBuilder.setCxxFlags(cxxFlags); - ppBuilder.setCFlags(cFlags); + rpp.setFlagsForC({cToolChain, cFlags}); + rpp.setFlagsForCxx({cxxToolChain, cxxFlags}); QStringList list = props.getModulePropertiesAsStringList( QLatin1String(CONFIG_CPP_MODULE), @@ -962,7 +980,7 @@ void QbsProject::updateCppCodeModel() data.append(" 1"); // cpp.defines: [ "FOO" ] is considered to be "FOO=1" grpDefines += (QByteArray("#define ") + data + '\n'); } - ppBuilder.setDefines(grpDefines); + rpp.setDefines(grpDefines); list = props.getModulePropertiesAsStringList(QLatin1String(CONFIG_CPP_MODULE), QLatin1String(CONFIG_INCLUDEPATHS)); @@ -985,10 +1003,10 @@ void QbsProject::updateCppCodeModel() FileName::fromUserInput(p).toString(), CppTools::ProjectPartHeaderPath::FrameworkPath); - ppBuilder.setHeaderPaths(grpHeaderPaths); + rpp.setHeaderPaths(grpHeaderPaths); - ppBuilder.setDisplayName(grp.name()); - ppBuilder.setProjectFile(groupLocationToProjectFile(grp.location())); + rpp.setDisplayName(grp.name()); + rpp.setProjectFile(groupLocationToProjectFile(grp.location())); QHash filePathToSourceArtifact; bool hasCFiles = false; @@ -1051,33 +1069,26 @@ void QbsProject::updateCppCodeModel() << grp.name() << "in product" << prd.name(); qCWarning(qbsPmLog) << "Expect problems with code model"; } - ppBuilder.setPreCompiledHeaders(pchFiles); - - const QList languages = ppBuilder.createProjectPartsForFiles( - grp.allFilePaths(), - [filePathToSourceArtifact](const QString &filePath) { - return cppFileType(filePathToSourceArtifact.value(filePath)); + rpp.setPreCompiledHeaders(pchFiles); + rpp.setFiles(grp.allFilePaths(), [filePathToSourceArtifact](const QString &filePath) { + // Keep this lambda thread-safe! + return cppFileType(filePathToSourceArtifact.value(filePath)); }); - foreach (Id language, languages) - setProjectLanguage(language, true); + rpps.append(rpp); + } } CppTools::GeneratedCodeModelSupport::update(m_extraCompilers); - - // Update the code model - m_codeModelFuture.cancel(); - m_codeModelFuture = modelmanager->updateProjectInfo(pinfo); - m_codeModelProjectInfo = modelmanager->projectInfo(this); - QTC_CHECK(m_codeModelProjectInfo.isValid()); + m_cppCodeModelUpdater->update({this, cToolChain, cxxToolChain, k, rpps}); } void QbsProject::updateCppCompilerCallData() { OpTimer optimer("updateCppCompilerCallData"); CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance(); - QTC_ASSERT(m_codeModelProjectInfo == modelManager->projectInfo(this), return); + QTC_ASSERT(m_cppCodeModelProjectInfo == modelManager->projectInfo(this), return); CppTools::ProjectInfo::CompilerCallData data; foreach (const qbs::ProductData &product, m_projectData.allProducts()) { @@ -1117,7 +1128,7 @@ void QbsProject::updateCppCompilerCallData() } } - m_codeModelProjectInfo = modelManager->updateCompilerCallDataForProject(this, data); + m_cppCodeModelProjectInfo = modelManager->updateCompilerCallDataForProject(this, data); } void QbsProject::updateQmlJsCodeModel() diff --git a/src/plugins/qbsprojectmanager/qbsproject.h b/src/plugins/qbsprojectmanager/qbsproject.h index 079212075da..c28f403ff3b 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.h +++ b/src/plugins/qbsprojectmanager/qbsproject.h @@ -45,6 +45,7 @@ #include namespace Core { class IDocument; } +namespace CppTools { class CppProjectUpdater; } namespace ProjectExplorer { class BuildConfiguration; } namespace QbsProjectManager { @@ -164,8 +165,8 @@ private: CancelStatusCancelingAltoghether } m_cancelStatus; - QFuture m_codeModelFuture; - CppTools::ProjectInfo m_codeModelProjectInfo; + CppTools::CppProjectUpdater *m_cppCodeModelUpdater = nullptr; + CppTools::ProjectInfo m_cppCodeModelProjectInfo; QbsBuildConfiguration *m_currentBc; diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index 10fa5ce8144..3a6c2f25b0e 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -39,10 +39,10 @@ #include #include #include -#include +#include #include -#include #include +#include #include #include #include @@ -247,7 +247,8 @@ bool QmakeProjectFile::reload(QString *errorString, ReloadFlag flag, ChangeType QmakeProject::QmakeProject(QmakeManager *manager, const QString &fileName) : m_projectFiles(new QmakeProjectFiles), - m_qmakeVfs(new QMakeVfs) + m_qmakeVfs(new QMakeVfs), + m_cppCodeModelUpdater(new CppTools::CppProjectUpdater(this)) { setId(Constants::QMAKEPROJECT_ID); setProjectManager(manager); @@ -275,7 +276,8 @@ QmakeProject::~QmakeProject() { delete m_projectImporter; m_projectImporter = nullptr; - m_codeModelFuture.cancel(); + delete m_cppCodeModelUpdater; + m_cppCodeModelUpdater = nullptr; m_asyncUpdateState = ShuttingDown; // Make sure root node (and associated readers) are shut hown before proceeding @@ -352,6 +354,13 @@ void QmakeProject::updateCppCodeModel() k = KitManager::defaultKit(); QTC_ASSERT(k, return); + ToolChain *cToolChain + = ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::C_LANGUAGE_ID); + ToolChain *cxxToolChain + = ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::CXX_LANGUAGE_ID); + + m_cppCodeModelUpdater->cancel(); + QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(k); ProjectPart::QtVersion qtVersionForPart = ProjectPart::NoQt; if (qtVersion) { @@ -363,26 +372,26 @@ void QmakeProject::updateCppCodeModel() FindQmakeProFiles findQmakeProFiles; const QList proFiles = findQmakeProFiles(rootProjectNode()); - CppTools::ProjectInfo projectInfo(this); - CppTools::ProjectPartBuilder ppBuilder(projectInfo); QList generators; - + CppTools::RawProjectParts rpps; foreach (QmakeProFileNode *pro, proFiles) { warnOnToolChainMismatch(pro); - ppBuilder.setDisplayName(pro->displayName()); - ppBuilder.setProjectFile(pro->filePath().toString()); - ppBuilder.setCxxFlags(pro->variableValue(Variable::CppFlags)); // TODO: Handle QMAKE_CFLAGS - ppBuilder.setDefines(pro->cxxDefines()); - ppBuilder.setPreCompiledHeaders(pro->variableValue(Variable::PrecompiledHeader)); - ppBuilder.setSelectedForBuilding(pro->includedInExactParse()); + CppTools::RawProjectPart rpp; + rpp.setDisplayName(pro->displayName()); + rpp.setProjectFile(pro->filePath().toString()); + // TODO: Handle QMAKE_CFLAGS + rpp.setFlagsForCxx({cxxToolChain, pro->variableValue(Variable::CppFlags)}); + rpp.setDefines(pro->cxxDefines()); + rpp.setPreCompiledHeaders(pro->variableValue(Variable::PrecompiledHeader)); + rpp.setSelectedForBuilding(pro->includedInExactParse()); // Qt Version if (pro->variableValue(Variable::Config).contains(QLatin1String("qt"))) - ppBuilder.setQtVersion(qtVersionForPart); + rpp.setQtVersion(qtVersionForPart); else - ppBuilder.setQtVersion(ProjectPart::NoQt); + rpp.setQtVersion(ProjectPart::NoQt); // Header paths CppTools::ProjectPartHeaderPaths headerPaths; @@ -397,7 +406,7 @@ void QmakeProject::updateCppCodeModel() headerPaths += CppToolsHeaderPath(qtVersion->frameworkInstallPath(), CppToolsHeaderPath::FrameworkPath); } - ppBuilder.setHeaderPaths(headerPaths); + rpp.setHeaderPaths(headerPaths); // Files and generators QStringList fileList = pro->variableValue(Variable::Source); @@ -408,14 +417,13 @@ void QmakeProject::updateCppCodeModel() }); } generators.append(proGenerators); + rpp.setFiles(fileList); - const QList languages = ppBuilder.createProjectPartsForFiles(fileList); - foreach (const Core::Id &language, languages) - setProjectLanguage(language, true); + rpps.append(rpp); } CppTools::GeneratedCodeModelSupport::update(generators); - m_codeModelFuture = CppTools::CppModelManager::instance()->updateProjectInfo(projectInfo); + m_cppCodeModelUpdater->update({this, cToolChain, cxxToolChain, k, rpps}); } void QmakeProject::updateQmlJSCodeModel() @@ -523,7 +531,7 @@ void QmakeProject::scheduleAsyncUpdate(QmakeProFileNode *node, QmakeProFile::Asy m_partialEvaluate.append(node); // Cancel running code model update - m_codeModelFuture.cancel(); + m_cppCodeModelUpdater->cancel(); startAsyncTimer(delay); } else if (m_asyncUpdateState == AsyncUpdateInProgress) { @@ -561,7 +569,7 @@ void QmakeProject::scheduleAsyncUpdate(QmakeProFile::AsyncUpdateDelay delay) m_asyncUpdateState = AsyncFullUpdatePending; // Cancel running code model update - m_codeModelFuture.cancel(); + m_cppCodeModelUpdater->cancel(); startAsyncTimer(delay); } diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.h b/src/plugins/qmakeprojectmanager/qmakeproject.h index fb8ddfdbf68..eec07a2f9fd 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.h +++ b/src/plugins/qmakeprojectmanager/qmakeproject.h @@ -42,6 +42,7 @@ class QMakeGlobals; class QMakeVfs; QT_END_NAMESPACE +namespace CppTools { class CppProjectUpdater; } namespace ProjectExplorer { class DeploymentData; } namespace QtSupport { class ProFileReader; } @@ -205,7 +206,7 @@ private: bool m_cancelEvaluate = false; QList m_partialEvaluate; - QFuture m_codeModelFuture; + CppTools::CppProjectUpdater *m_cppCodeModelUpdater = nullptr; Internal::CentralizedFolderWatcher *m_centralizedFolderWatcher = nullptr; diff --git a/src/plugins/qnx/qnxtoolchain.cpp b/src/plugins/qnx/qnxtoolchain.cpp index f2b158167c1..65ce094fb0f 100644 --- a/src/plugins/qnx/qnxtoolchain.cpp +++ b/src/plugins/qnx/qnxtoolchain.cpp @@ -81,9 +81,29 @@ static void setQnxEnvironment(Environment &env, const QList &qn } } +// Qcc is a multi-compiler driver, and most of the gcc options can be accomplished by using the -Wp, and -Wc +// options to pass the options directly down to the compiler +static QStringList reinterpretOptions(const QStringList &args) +{ + QStringList arguments; + foreach (const QString &str, args) { + if (str.startsWith(QLatin1String("--sysroot="))) + continue; + QString arg = str; + if (arg == QLatin1String("-v") + || arg == QLatin1String("-dM")) + arg.prepend(QLatin1String("-Wp,")); + arguments << arg; + } + + return arguments; +} + QnxToolChain::QnxToolChain(ToolChain::Detection d) : GccToolChain(Constants::QNX_TOOLCHAIN_ID, d) -{ } +{ + setOptionsReinterpreter(&reinterpretOptions); +} QnxToolChain::QnxToolChain(Core::Id l, ToolChain::Detection d) : QnxToolChain(d) @@ -156,24 +176,6 @@ GccToolChain::DetectedAbisResult QnxToolChain::detectSupportedAbis() const return detectTargetAbis(FileName::fromString(m_sdpPath)); } -// Qcc is a multi-compiler driver, and most of the gcc options can be accomplished by using the -Wp, and -Wc -// options to pass the options directly down to the compiler -QStringList QnxToolChain::reinterpretOptions(const QStringList &args) const -{ - QStringList arguments; - foreach (const QString &str, args) { - if (str.startsWith(QLatin1String("--sysroot="))) - continue; - QString arg = str; - if (arg == QLatin1String("-v") - || arg == QLatin1String("-dM")) - arg.prepend(QLatin1String("-Wp,")); - arguments << arg; - } - - return arguments; -} - // -------------------------------------------------------------------------- // QnxToolChainFactory // -------------------------------------------------------------------------- diff --git a/src/plugins/qnx/qnxtoolchain.h b/src/plugins/qnx/qnxtoolchain.h index e20c9f66ead..93f2c15bc60 100644 --- a/src/plugins/qnx/qnxtoolchain.h +++ b/src/plugins/qnx/qnxtoolchain.h @@ -53,8 +53,6 @@ public: protected: virtual DetectedAbisResult detectSupportedAbis() const override; - QStringList reinterpretOptions(const QStringList &args) const override; - private: QString m_sdpPath; }; diff --git a/src/plugins/cpptools/projectpartbuilder.h b/tests/unit/mockup/projectexplorer/kitinformation.h similarity index 79% rename from src/plugins/cpptools/projectpartbuilder.h rename to tests/unit/mockup/projectexplorer/kitinformation.h index 22f5a47b5d4..fced31a50f2 100644 --- a/src/plugins/cpptools/projectpartbuilder.h +++ b/tests/unit/mockup/projectexplorer/kitinformation.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -25,18 +25,16 @@ #pragma once -#include "cpptools_global.h" +#include -#include "cppbaseprojectpartbuilder.h" +namespace ProjectExplorer { -namespace CppTools { +class Kit; -class ProjectInfo; - -class CPPTOOLS_EXPORT ProjectPartBuilder : public BaseProjectPartBuilder +class SysRootKitInformation { public: - ProjectPartBuilder(ProjectInfo &projectInfo); + static Utils::FileName sysRoot(const Kit *) { return Utils::FileName(); } }; -} // namespace CppTools +} // namespace ProjectExplorer diff --git a/tests/unit/mockup/projectexplorer/toolchain.h b/tests/unit/mockup/projectexplorer/toolchain.h new file mode 100644 index 00000000000..33bfa836dff --- /dev/null +++ b/tests/unit/mockup/projectexplorer/toolchain.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 +#include +#include + +#include + +namespace ProjectExplorer { + +class ToolChain +{ +public: + Core::Id typeId() const { return Core::Id(); } + + enum CompilerFlag { + NoFlags = 0, + StandardCxx11 = 0x1, + StandardC99 = 0x2, + StandardC11 = 0x4, + GnuExtensions = 0x8, + MicrosoftExtensions = 0x10, + BorlandExtensions = 0x20, + OpenMP = 0x40, + ObjectiveC = 0x80, + StandardCxx14 = 0x100, + StandardCxx17 = 0x200, + StandardCxx98 = 0x400, + }; + Q_DECLARE_FLAGS(CompilerFlags, CompilerFlag) + + Abi targetAbi() const { return Abi(); } + + using SystemHeaderPathsRunner = std::function(const QStringList &cxxflags, const QString &sysRoot)>; + virtual SystemHeaderPathsRunner createSystemHeaderPathsRunner() const { return SystemHeaderPathsRunner(); } + + using PredefinedMacrosRunner = std::function; + virtual PredefinedMacrosRunner createPredefinedMacrosRunner() const { return PredefinedMacrosRunner(); } + + virtual QString originalTargetTriple() const { return QString(); } +}; + +} // namespace ProjectExplorer diff --git a/tests/unit/unittest/cppbaseprojectpartbuilder-test.cpp b/tests/unit/unittest/cppbaseprojectpartbuilder-test.cpp deleted file mode 100644 index cb2108658d6..00000000000 --- a/tests/unit/unittest/cppbaseprojectpartbuilder-test.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "googletest.h" -#include "gtest-qt-printing.h" -#include "mimedatabase-utilities.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include - -using CppTools::BaseProjectPartBuilder; -using CppTools::ProjectFile; -using CppTools::ProjectFiles; -using CppTools::ProjectInfo; -using CppTools::ProjectInterface; -using CppTools::ProjectPart; -using CppTools::ToolChainInterface; -using CppTools::ToolChainInterfacePtr; - -using testing::Contains; -using testing::Eq; -using testing::UnorderedElementsAre; -using testing::PrintToString; - -namespace { - -MATCHER_P2(IsProjectPart, languageVersion, fileKind, - std::string(negation ? "isn't" : "is") - + " project part with language version " + PrintToString(languageVersion) - + " and file kind " + PrintToString(fileKind)) -{ - const ProjectPart::Ptr &projectPart = arg; - - return projectPart->languageVersion == languageVersion - && projectPart->files.at(0).kind == fileKind; -} - -class EditableToolChain : public CppTools::ToolChainInterface -{ -public: - void setCompilerFlags(ProjectExplorer::ToolChain::CompilerFlags compilerFlags) - { - m_compilerFlags = compilerFlags; - } - -private: - Core::Id type() const override { return Core::Id(); } - bool isMsvc2015Toolchain() const override { return false; } - unsigned wordWidth() const override { return 64; } - QString targetTriple() const override { return QString(); } - - QByteArray predefinedMacros() const override { return QByteArray(); } - QList systemHeaderPaths() const override - { return QList(); } - - ProjectExplorer::WarningFlags warningFlags() const override - { return ProjectExplorer::WarningFlags(); } - - ProjectExplorer::ToolChain::CompilerFlags compilerFlags() const override - { return m_compilerFlags; } - -private: - ProjectExplorer::ToolChain::CompilerFlags m_compilerFlags; -}; - -class EditableProject : public CppTools::ProjectInterface -{ -public: - void setToolChain(ToolChainInterface *toolChain) - { - m_toolChain = toolChain; - } - -private: - QString displayName() const override { return QString(); } - QString projectFilePath() const override { return QString(); } - - ToolChainInterfacePtr toolChain(Core::Id, - const QStringList &) const override - { return ToolChainInterfacePtr(m_toolChain); } - -private: - CppTools::ToolChainInterface *m_toolChain = nullptr; -}; - -class BaseProjectPartBuilder : public ::testing::Test -{ -protected: - void SetUp() override; - - QObject dummyProjectExplorerProject; - ProjectInfo projectInfo{static_cast(&dummyProjectExplorerProject)}; -}; - -TEST_F(BaseProjectPartBuilder, CreateNoPartsForEmptyFileList) -{ - ::BaseProjectPartBuilder builder(new EditableProject, projectInfo); - - builder.createProjectPartsForFiles(QStringList()); - - ASSERT_TRUE(projectInfo.projectParts().isEmpty()); -} - -TEST_F(BaseProjectPartBuilder, CreateSinglePart) -{ - ::BaseProjectPartBuilder builder(new EditableProject, projectInfo); - - builder.createProjectPartsForFiles(QStringList() << "foo.cpp" << "foo.h"); - - ASSERT_THAT(projectInfo.projectParts().size(), Eq(1)); -} - -TEST_F(BaseProjectPartBuilder, CreateMultipleParts) -{ - ::BaseProjectPartBuilder builder(new EditableProject, projectInfo); - - builder.createProjectPartsForFiles(QStringList() << "foo.cpp" << "foo.h" - << "bar.c" << "bar.h"); - - ASSERT_THAT(projectInfo.projectParts().size(), Eq(2)); -} - -TEST_F(BaseProjectPartBuilder, ProjectPartIndicatesObjectiveCExtensionsByDefault) -{ - ::BaseProjectPartBuilder builder(new EditableProject, projectInfo); - - builder.createProjectPartsForFiles(QStringList() << "foo.mm"); - - ASSERT_THAT(projectInfo.projectParts().size(), Eq(1)); - const ProjectPart &projectPart = *projectInfo.projectParts().at(0); - ASSERT_TRUE(projectPart.languageExtensions & ProjectPart::ObjectiveCExtensions); -} - -TEST_F(BaseProjectPartBuilder, ProjectPartHasLatestLanguageVersionByDefault) -{ - ::BaseProjectPartBuilder builder(new EditableProject, projectInfo); - - builder.createProjectPartsForFiles(QStringList() << "foo.cpp"); - - ASSERT_THAT(projectInfo.projectParts().size(), Eq(1)); - const ProjectPart &projectPart = *projectInfo.projectParts().at(0); - ASSERT_THAT(projectPart.languageVersion, Eq(ProjectPart::LatestCxxVersion)); -} - -TEST_F(BaseProjectPartBuilder, ToolChainSetsLanguageVersion) -{ - auto toolChain = new EditableToolChain; - toolChain->setCompilerFlags(ProjectExplorer::ToolChain::StandardCxx98); - auto project = new EditableProject; - project->setToolChain(toolChain); - ::BaseProjectPartBuilder builder(project, projectInfo); - - builder.createProjectPartsForFiles(QStringList() << "foo.cpp"); - - ASSERT_THAT(projectInfo.projectParts().size(), Eq(1)); - const ProjectPart &projectPart = *projectInfo.projectParts().at(0); - ASSERT_THAT(projectPart.languageVersion, Eq(ProjectPart::CXX98)); -} - -TEST_F(BaseProjectPartBuilder, ToolChainSetsLanguageExtensions) -{ - auto toolChain = new EditableToolChain; - toolChain->setCompilerFlags(ProjectExplorer::ToolChain::MicrosoftExtensions); - auto project = new EditableProject; - project->setToolChain(toolChain); - ::BaseProjectPartBuilder builder(project, projectInfo); - - builder.createProjectPartsForFiles(QStringList() << "foo.cpp"); - - ASSERT_THAT(projectInfo.projectParts().size(), Eq(1)); - const ProjectPart &projectPart = *projectInfo.projectParts().at(0); - ASSERT_TRUE(projectPart.languageExtensions & ProjectPart::MicrosoftExtensions); -} - -TEST_F(BaseProjectPartBuilder, ProjectFileKindsMatchProjectPartVersion) -{ - ::BaseProjectPartBuilder builder(new EditableProject, projectInfo); - - builder.createProjectPartsForFiles(QStringList() << "foo.h"); - - ASSERT_THAT(projectInfo.projectParts(), - UnorderedElementsAre(IsProjectPart(ProjectPart::LatestCVersion, ProjectFile::CHeader), - IsProjectPart(ProjectPart::LatestCVersion, ProjectFile::ObjCHeader), - IsProjectPart(ProjectPart::LatestCxxVersion, ProjectFile::CXXHeader), - IsProjectPart(ProjectPart::LatestCxxVersion, ProjectFile::ObjCXXHeader))); -} - -TEST_F(BaseProjectPartBuilder, ReportsCxxLanguage) -{ - ::BaseProjectPartBuilder builder(new EditableProject, projectInfo); - - const QList languages = builder.createProjectPartsForFiles(QStringList() << "foo.cpp"); - - ASSERT_THAT(languages, Eq(QList() << ProjectExplorer::Constants::CXX_LANGUAGE_ID)); -} - -TEST_F(BaseProjectPartBuilder, ReportsCLanguage) -{ - ::BaseProjectPartBuilder builder(new EditableProject, projectInfo); - - const QList languages = builder.createProjectPartsForFiles(QStringList() << "foo.c"); - - ASSERT_THAT(languages, Eq(QList() << ProjectExplorer::Constants::C_LANGUAGE_ID)); -} - -void BaseProjectPartBuilder::SetUp() -{ - ASSERT_TRUE(MimeDataBaseUtilities::addCppToolsMimeTypes()); -} - -} // anonymous namespace diff --git a/tests/unit/unittest/cppprojectinfogenerator-test.cpp b/tests/unit/unittest/cppprojectinfogenerator-test.cpp new file mode 100644 index 00000000000..9a991a75204 --- /dev/null +++ b/tests/unit/unittest/cppprojectinfogenerator-test.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "googletest.h" +#include "gtest-qt-printing.h" +#include "mimedatabase-utilities.h" + +#include +#include + +#include + +using CppTools::Internal::ProjectInfoGenerator; +using CppTools::ProjectFile; +using CppTools::ProjectInfo; +using CppTools::ProjectUpdateInfo; +using CppTools::ProjectPart; +using CppTools::RawProjectPart; + +using ProjectExplorer::ToolChain; + +using testing::Eq; +using testing::UnorderedElementsAre; +using testing::PrintToString; + +namespace { + +MATCHER_P2(IsProjectPart, languageVersion, fileKind, + std::string(negation ? "isn't" : "is") + + " project part with language version " + PrintToString(languageVersion) + + " and file kind " + PrintToString(fileKind)) +{ + const ProjectPart::Ptr &projectPart = arg; + + return projectPart->languageVersion == languageVersion + && projectPart->files.at(0).kind == fileKind; +} + +class ProjectInfoGenerator : public ::testing::Test +{ +protected: + void SetUp() override; + ProjectInfo generate(); + +protected: + RawProjectPart rawProjectPart; +}; + +TEST_F(ProjectInfoGenerator, CreateNoProjectPartsForEmptyFileList) +{ + const ProjectInfo projectInfo = generate(); + + ASSERT_TRUE(projectInfo.projectParts().isEmpty()); +} + +TEST_F(ProjectInfoGenerator, CreateSingleProjectPart) +{ + rawProjectPart.files = QStringList{ "foo.cpp", "foo.h"}; + + const ProjectInfo projectInfo = generate(); + + ASSERT_THAT(projectInfo.projectParts().size(), Eq(1)); +} + +TEST_F(ProjectInfoGenerator, CreateMultipleProjectParts) +{ + rawProjectPart.files = QStringList{ "foo.cpp", "foo.h", "bar.c", "bar.h" }; + + const ProjectInfo projectInfo = generate(); + + ASSERT_THAT(projectInfo.projectParts().size(), Eq(2)); +} + +TEST_F(ProjectInfoGenerator, ProjectPartIndicatesObjectiveCExtensionsByDefault) +{ + rawProjectPart.files = QStringList{ "foo.mm" }; + + const ProjectInfo projectInfo = generate(); + + ASSERT_THAT(projectInfo.projectParts().size(), Eq(1)); + const ProjectPart &projectPart = *projectInfo.projectParts().at(0); + ASSERT_TRUE(projectPart.languageExtensions & ProjectPart::ObjectiveCExtensions); +} + +TEST_F(ProjectInfoGenerator, ProjectPartHasLatestLanguageVersionByDefault) +{ + rawProjectPart.files = QStringList{ "foo.cpp" }; + + const ProjectInfo projectInfo = generate(); + + ASSERT_THAT(projectInfo.projectParts().size(), Eq(1)); + const ProjectPart &projectPart = *projectInfo.projectParts().at(0); + ASSERT_THAT(projectPart.languageVersion, Eq(ProjectPart::LatestCxxVersion)); +} + +TEST_F(ProjectInfoGenerator, UseCompilerFlagsForLanguageVersion) +{ + rawProjectPart.files = QStringList{ "foo.cpp" }; + rawProjectPart.flagsForCxx.compilerFlags = ToolChain::CompilerFlag::StandardCxx98; + + const ProjectInfo projectInfo = generate(); + + ASSERT_THAT(projectInfo.projectParts().size(), Eq(1)); + const ProjectPart &projectPart = *projectInfo.projectParts().at(0); + ASSERT_THAT(projectPart.languageVersion, Eq(ProjectPart::CXX98)); +} + +TEST_F(ProjectInfoGenerator, UseCompilerFlagsForLangaugeExtensions) +{ + rawProjectPart.files = QStringList{ "foo.cpp" }; + rawProjectPart.flagsForCxx.compilerFlags = ToolChain::CompilerFlag::MicrosoftExtensions; + + const ProjectInfo projectInfo = generate(); + + ASSERT_THAT(projectInfo.projectParts().size(), Eq(1)); + const ProjectPart &projectPart = *projectInfo.projectParts().at(0); + ASSERT_TRUE(projectPart.languageExtensions & ProjectPart::MicrosoftExtensions); +} + +TEST_F(ProjectInfoGenerator, ProjectFileKindsMatchProjectPartVersion) +{ + rawProjectPart.files = QStringList{ "foo.h" }; + + const ProjectInfo projectInfo = generate(); + + ASSERT_THAT(projectInfo.projectParts(), + UnorderedElementsAre(IsProjectPart(ProjectPart::LatestCVersion, ProjectFile::CHeader), + IsProjectPart(ProjectPart::LatestCVersion, ProjectFile::ObjCHeader), + IsProjectPart(ProjectPart::LatestCxxVersion, ProjectFile::CXXHeader), + IsProjectPart(ProjectPart::LatestCxxVersion, ProjectFile::ObjCXXHeader))); +} + +void ProjectInfoGenerator::SetUp() +{ + ASSERT_TRUE(MimeDataBaseUtilities::addCppToolsMimeTypes()); +} + +ProjectInfo ProjectInfoGenerator::generate() +{ + QFutureInterface fi; + ProjectUpdateInfo projectUpdateInfo; + projectUpdateInfo.rawProjectParts += rawProjectPart; + ::ProjectInfoGenerator generator(fi, projectUpdateInfo); + + return generator.generate(); +} + +} // anonymous namespace diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 390f54fe937..133f762c266 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -32,7 +32,7 @@ SOURCES += \ clientserverinprocess-test.cpp \ lineprefixer-test.cpp \ cppprojectfilecategorizer-test.cpp \ - cppbaseprojectpartbuilder-test.cpp \ + cppprojectinfogenerator-test.cpp \ cppprojectpartchooser-test.cpp \ processevents-utilities.cpp \ mimedatabase-utilities.cpp \