diff --git a/gui/projectfile.cpp b/gui/projectfile.cpp index aad1a3d79..f3af8c074 100644 --- a/gui/projectfile.cpp +++ b/gui/projectfile.cpp @@ -65,6 +65,7 @@ void ProjectFile::clear() mMaxCtuDepth = 10; mCheckUnknownFunctionReturn.clear(); mSafeChecks.clear(); + mVsConfigurations.clear(); } bool ProjectFile::read(const QString &filename) @@ -174,6 +175,9 @@ bool ProjectFile::read(const QString &filename) if (xmlReader.name() == CppcheckXml::MaxCtuDepthElementName) mMaxCtuDepth = readInt(xmlReader, mMaxCtuDepth); + // VSConfiguration + if (xmlReader.name() == CppcheckXml::VSConfigurationElementName) + readVsConfigurations(xmlReader); break; case QXmlStreamReader::EndElement: @@ -466,6 +470,44 @@ void ProjectFile::readExcludes(QXmlStreamReader &reader) } while (!allRead); } +void ProjectFile::readVsConfigurations(QXmlStreamReader &reader) +{ + QXmlStreamReader::TokenType type; + do { + type = reader.readNext(); + switch (type) { + case QXmlStreamReader::StartElement: + // Read library-elements + if (reader.name().toString() == CppcheckXml::VSConfigurationName) { + QString config; + type = reader.readNext(); + if (type == QXmlStreamReader::Characters) { + config = reader.text().toString(); + } + mVsConfigurations << config; + } + break; + + case QXmlStreamReader::EndElement: + if (reader.name().toString() != CppcheckXml::VSConfigurationName) + return; + break; + + // Not handled + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (true); +} + void ProjectFile::readPlatform(QXmlStreamReader &reader) { do { @@ -619,6 +661,11 @@ void ProjectFile::setAddons(const QStringList &addons) mAddons = addons; } +void ProjectFile::setVSConfigurations(const QStringList &vsConfigs) +{ + mVsConfigurations = vsConfigs; +} + bool ProjectFile::write(const QString &filename) { if (!filename.isEmpty()) @@ -694,6 +741,13 @@ bool ProjectFile::write(const QString &filename) xmlWriter.writeEndElement(); } + if (!mVsConfigurations.isEmpty()) { + writeStringList(xmlWriter, + mVsConfigurations, + CppcheckXml::VSConfigurationElementName, + CppcheckXml::VSConfigurationName); + } + writeStringList(xmlWriter, mUndefines, CppcheckXml::UndefinesElementName, diff --git a/gui/projectfile.h b/gui/projectfile.h index 63f2be3fb..298480d91 100644 --- a/gui/projectfile.h +++ b/gui/projectfile.h @@ -124,6 +124,14 @@ public: return ProjectFile::fromNativeSeparators(mExcludedPaths); } + /** + * @brief Get list of paths to exclude from the check. + * @return list of paths. + */ + QStringList getVsConfigurations() const { + return mVsConfigurations; + } + /** * @brief Get list libraries. * @return list of libraries. @@ -272,6 +280,11 @@ public: */ void setAddons(const QStringList &addons); + /** @brief Set list of Visual Studio configurations to be checked + * @param vsConfigs List of configurations + */ + void setVSConfigurations(const QStringList &vsConfigs); + /** * @brief Set tags. * @param tags tag list @@ -394,6 +407,12 @@ protected: */ void readExcludes(QXmlStreamReader &reader); + /** + * @brief Read lists of Visual Studio configurations + * @param reader XML stream reader. + */ + void readVsConfigurations(QXmlStreamReader &reader); + /** * @brief Read platform text. * @param reader XML stream reader. @@ -458,6 +477,9 @@ private: */ bool mAnalyzeAllVsConfigs; + /** Check only a selected VS configuration */ + QStringList mVsConfigurations; + /** Check code in headers */ bool mCheckHeaders; diff --git a/gui/projectfiledialog.cpp b/gui/projectfiledialog.cpp index c9b51b1d4..19ab3c173 100644 --- a/gui/projectfiledialog.cpp +++ b/gui/projectfiledialog.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "common.h" #include "newsuppressiondialog.h" #include "projectfiledialog.h" @@ -33,6 +34,7 @@ #include "projectfile.h" #include "library.h" #include "platforms.h" +#include "importproject.h" /** Return paths from QListWidget */ static QStringList getPaths(const QListWidget *list) @@ -58,6 +60,16 @@ static const cppcheck::Platform::PlatformType builtinPlatforms[] = { static const int numberOfBuiltinPlatforms = sizeof(builtinPlatforms) / sizeof(builtinPlatforms[0]); +QStringList ProjectFileDialog::getProjectConfigs(const QString &fileName) { + QStringList ret; + ImportProject importer; + Settings projSettings; + importer.import(fileName.toStdString(), &projSettings); + for (const std::string &cfg : importer.getVSConfigs()) + ret << QString::fromStdString(cfg); + return ret; +} + ProjectFileDialog::ProjectFileDialog(ProjectFile *projectFile, QWidget *parent) : QDialog(parent) , mProjectFile(projectFile) @@ -194,7 +206,7 @@ ProjectFileDialog::ProjectFileDialog(ProjectFile *projectFile, QWidget *parent) connect(mUI.mBtnRemoveSuppression, &QPushButton::clicked, this, &ProjectFileDialog::removeSuppression); connect(mUI.mListSuppressions, &QListWidget::doubleClicked, this, &ProjectFileDialog::editSuppression); connect(mUI.mBtnBrowseMisraFile, &QPushButton::clicked, this, &ProjectFileDialog::browseMisraFile); - + connect(mUI.mChkAllVsConfigs, &QCheckBox::clicked, this, &ProjectFileDialog::checkAllVSConfigs); loadFromProjectFile(projectFile); } @@ -227,6 +239,13 @@ static void updateAddonCheckBox(QCheckBox *cb, const ProjectFile *projectFile, c } } +void ProjectFileDialog::checkAllVSConfigs() +{ + if(mUI.mChkAllVsConfigs->isChecked()) + mUI.mListVsConfigs->selectAll(); + mUI.mListVsConfigs->setEnabled(!mUI.mChkAllVsConfigs->isChecked()); +} + void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile) { setRootPath(projectFile->getRootPath()); @@ -313,6 +332,16 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile) } mUI.mEditTags->setText(projectFile->getTags().join(';')); updatePathsAndDefines(); + if(mUI.mEditImportProject->text().endsWith(".sln") || mUI.mEditImportProject->text().endsWith(".vcxproj")) { + setVsConfigurations(getProjectConfigs(mUI.mEditImportProject->text())); + foreach(const QString &cfg, projectFile->getVsConfigurations()) { + QList items = mUI.mListVsConfigs->findItems(cfg, Qt::MatchFlag::MatchExactly); + items[0]->setSelected(true); + } + } + else + mUI.mListVsConfigs->setEnabled(false); + } void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const @@ -370,6 +399,7 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const projectFile->setClangAnalyzer(mUI.mToolClangAnalyzer->isChecked()); projectFile->setClangTidy(mUI.mToolClangTidy->isChecked()); projectFile->setTags(mUI.mEditTags->text().split(";", QString::SkipEmptyParts)); + projectFile->setVSConfigurations(getVsConfigurations()); } void ProjectFileDialog::ok() @@ -429,6 +459,7 @@ void ProjectFileDialog::updatePathsAndDefines() mUI.mBtnIncludeUp->setEnabled(!importProject); mUI.mBtnIncludeDown->setEnabled(!importProject); mUI.mChkAllVsConfigs->setEnabled(fileName.endsWith(".sln") || fileName.endsWith(".vcxproj")); + mUI.mListVsConfigs->setEnabled(fileName.endsWith(".sln") || fileName.endsWith(".vcxproj")); } void ProjectFileDialog::clearImportProject() @@ -451,9 +482,26 @@ void ProjectFileDialog::browseImportProject() if (!fileName.isEmpty()) { mUI.mEditImportProject->setText(dir.relativeFilePath(fileName)); updatePathsAndDefines(); + setVsConfigurations(getProjectConfigs(fileName)); + mUI.mListVsConfigs->selectAll(); } } +QStringList ProjectFileDialog::getVsConfigurations() const +{ + QStringList configs; + foreach(QListWidgetItem *item, mUI.mListVsConfigs->selectedItems()) + configs << item->text(); + + return configs; +} + +void ProjectFileDialog::setVsConfigurations(const QStringList &configs) +{ + mUI.mListVsConfigs->clear(); + mUI.mListVsConfigs->addItems(configs); +} + QString ProjectFileDialog::getImportProject() const { return mUI.mEditImportProject->text(); diff --git a/gui/projectfiledialog.h b/gui/projectfiledialog.h index 7af7c5303..40a4a20d2 100644 --- a/gui/projectfiledialog.h +++ b/gui/projectfiledialog.h @@ -58,6 +58,9 @@ private: */ QString getRootPath() const; + QStringList getVsConfigurations() const; + void setVsConfigurations(const QStringList &configs); + QString getImportProject() const; /** Get Cppcheck build dir */ @@ -256,6 +259,11 @@ protected slots: */ void browseMisraFile(); + /** + * @brief Check for all VS configurations + */ + void checkAllVSConfigs(); + protected: /** @@ -295,6 +303,8 @@ protected: int getSuppressionIndex(const QString &shortText) const; private: + QStringList getProjectConfigs(const QString &fileName); + Ui::ProjectFile mUI; /** diff --git a/gui/projectfiledialog.ui b/gui/projectfiledialog.ui index d6fd16625..1e748d27c 100644 --- a/gui/projectfiledialog.ui +++ b/gui/projectfiledialog.ui @@ -7,7 +7,7 @@ 0 0 888 - 585 + 573 @@ -64,14 +64,45 @@ - - - <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> - - - Analyze all Visual Studio configurations - - + + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + + + Analyze all Visual Studio configurations + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Selected VS Configurations + + + + + + + QAbstractItemView::MultiSelection + + + + diff --git a/lib/importproject.cpp b/lib/importproject.cpp index 9596138ca..ca50e8f71 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -621,8 +621,10 @@ void ImportProject::importVcxproj(const std::string &filename, std::mapFirstChildElement(); cfg; cfg = cfg->NextSiblingElement()) { if (std::strcmp(cfg->Name(), "ProjectConfiguration") == 0) { const ProjectConfiguration p(cfg); - if (p.platform != ProjectConfiguration::Unknown) + if (p.platform != ProjectConfiguration::Unknown) { projectConfigurationList.emplace_back(cfg); + mAllVSConfigs.insert(p.configuration); + } } } } else { @@ -654,6 +656,18 @@ void ImportProject::importVcxproj(const std::string &filename, std::map(istr), eos); } + bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *settings) { tinyxml2::XMLDocument doc; @@ -1018,6 +1033,8 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti guiProject.libraries = readXmlStringList(node, "", CppcheckXml::LibraryElementName, nullptr); else if (strcmp(node->Name(), CppcheckXml::SuppressionsElementName) == 0) suppressions = readXmlStringList(node, "", CppcheckXml::SuppressionElementName, nullptr); + else if (strcmp(node->Name(), CppcheckXml::VSConfigurationElementName) == 0) + guiProject.checkVsConfigs = readXmlStringList(node, "", CppcheckXml::VSConfigurationName, nullptr); else if (strcmp(node->Name(), CppcheckXml::PlatformElementName) == 0) guiProject.platform = node->GetText(); else if (strcmp(node->Name(), CppcheckXml::AnalyzeAllVsConfigsElementName) == 0) @@ -1064,6 +1081,7 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti settings->userUndefs = temp.userUndefs; settings->addons = temp.addons; settings->clangTidy = temp.clangTidy; + for (const std::string &p : paths) guiProject.pathNames.push_back(p); for (const std::string &supp : suppressions) @@ -1103,3 +1121,8 @@ void ImportProject::selectOneVsConfig(Settings::PlatformType platform) } } } + +std::list ImportProject::getVSConfigs() +{ + return std::list (mAllVSConfigs.begin(), mAllVSConfigs.end()); +} \ No newline at end of file diff --git a/lib/importproject.h b/lib/importproject.h index 0a561979b..8603c4650 100644 --- a/lib/importproject.h +++ b/lib/importproject.h @@ -84,12 +84,15 @@ public: void selectOneVsConfig(cppcheck::Platform::PlatformType platform); + std::list getVSConfigs(); + // Cppcheck GUI output struct { std::string analyzeAllVsConfigs; std::vector pathNames; std::list libraries; std::list excludedPaths; + std::list checkVsConfigs; std::string projectFile; std::string platform; } guiProject; @@ -108,6 +111,7 @@ private: void importBcb6Prj(const std::string &projectFilename); std::string mPath; + std::set mAllVSConfigs; }; @@ -154,6 +158,8 @@ namespace CppcheckXml { const char CheckUnknownFunctionReturn[] = "check-unknown-function-return-values"; const char ClangTidy[] = "clang-tidy"; const char Name[] = "name"; + const char VSConfigurationElementName[] = "vs-configurations"; + const char VSConfigurationName[] = "config"; } /// @} diff --git a/lib/settings.h b/lib/settings.h index 6321c2ab2..4da9a2d3e 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -153,6 +153,9 @@ public: for finding include files inside source files. (-I) */ std::list includePaths; + /** @brief List of selected Visual Studio configurations that should be checks */ + std::list checkVsConfigs; + /** @brief Inconclusive checks */ bool inconclusive;