diff --git a/gui/checkthread.cpp b/gui/checkthread.cpp index c7426c2dc..228d1bd35 100644 --- a/gui/checkthread.cpp +++ b/gui/checkthread.cpp @@ -44,9 +44,8 @@ void CheckThread::Check(const Settings &settings) void CheckThread::run() { mState = Running; - QString file; - file = mResult.GetNextFile(); + QString file = mResult.GetNextFile(); while (!file.isEmpty() && mState == Running) { qDebug() << "Checking file" << file; mCppcheck.check(file.toStdString()); @@ -55,6 +54,18 @@ void CheckThread::run() if (mState == Running) file = mResult.GetNextFile(); } + + ImportProject::FileSettings fileSettings = mResult.GetNextFileSettings(); + while (!fileSettings.filename.empty() && mState == Running) { + file = QString::fromStdString(fileSettings.filename); + qDebug() << "Checking file" << file; + mCppcheck.check(fileSettings); + emit FileChecked(file); + + if (mState == Running) + fileSettings = mResult.GetNextFileSettings(); + } + if (mState == Running) mState = Ready; else diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index c6da6dbfa..e30731b7a 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -353,6 +353,43 @@ void MainWindow::SaveSettings() const mUI.mResults->SaveSettings(mSettings); } +void MainWindow::DoCheckProject(ImportProject p) +{ + ClearResults(); + + mIsLogfileLoaded = false; + if (mProject) { + std::vector v; + foreach (const QString &s, mProject->GetProjectFile()->GetExcludedPaths()) { + v.push_back(s.toStdString()); + } + p.ignorePaths(v); + } else { + EnableProjectActions(false); + } + + mUI.mResults->Clear(true); + mThread->ClearFiles(); + + mUI.mResults->CheckingStarted(p.fileSettings.size()); + + mThread->SetProject(p); + QDir inf(mCurrentDirectory); + const QString checkPath = inf.canonicalPath(); + SetPath(SETTINGS_LAST_CHECK_PATH, checkPath); + + CheckLockDownUI(); // lock UI while checking + + mUI.mResults->SetCheckDirectory(checkPath); + Settings checkSettings = GetCppcheckSettings(); + + if (mProject) + qDebug() << "Checking project file" << mProject->GetProjectFile()->GetFilename(); + + //mThread->SetCheckProject(true); + mThread->Check(checkSettings, true); +} + void MainWindow::DoCheckFiles(const QStringList &files) { if (files.isEmpty()) { @@ -695,6 +732,8 @@ Settings MainWindow::GetCppcheckSettings() result.quiet = false; result.verbose = true; result.force = mSettings->value(SETTINGS_CHECK_FORCE, 1).toBool(); + if (mProject && !mProject->GetProjectFile()->GetImportProject().isEmpty()) + result.force = false; result.xml = false; result.jobs = mSettings->value(SETTINGS_CHECK_THREADS, 1).toInt(); result.inlineSuppressions = mSettings->value(SETTINGS_INLINE_SUPPRESSIONS, false).toBool(); @@ -1230,6 +1269,14 @@ void MainWindow::CheckProject(Project *project) else mCurrentDirectory = rootpath; + if (!project->GetProjectFile()->GetImportProject().isEmpty()) { + ImportProject p; + QString prjfile = inf.canonicalPath() + '/' + project->GetProjectFile()->GetImportProject(); + p.import(prjfile.toStdString()); + DoCheckProject(p); + return; + } + QStringList paths = project->GetProjectFile()->GetCheckPaths(); // If paths not given then check the root path (which may be the project diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 58ec0c85d..4783f6bf9 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -371,6 +371,13 @@ private: */ QStringList SelectFilesToCheck(QFileDialog::FileMode mode); + /** + * @brief Check project + * + * @param p imported project + */ + void DoCheckProject(ImportProject p); + /** * @brief Check all files specified in parameter files * diff --git a/gui/project.cpp b/gui/project.cpp index 0d1b35037..8cb2e963d 100644 --- a/gui/project.cpp +++ b/gui/project.cpp @@ -104,6 +104,7 @@ bool Project::Edit() if (rv == QDialog::Accepted) { QString root = dlg.GetRootPath(); mPFile->SetRootPath(root); + mPFile->SetImportProject(dlg.GetImportProject()); QStringList includes = dlg.GetIncludePaths(); mPFile->SetIncludes(includes); QStringList defines = dlg.GetDefines(); diff --git a/gui/projectfile.cpp b/gui/projectfile.cpp index 38f89b7a7..bcbdbfa5e 100644 --- a/gui/projectfile.cpp +++ b/gui/projectfile.cpp @@ -27,6 +27,7 @@ static const char ProjectElementName[] = "project"; static const char ProjectVersionAttrib[] = "version"; static const char ProjectFileVersion[] = "1"; +static const char ImportProjectElementName[] = "importproject"; static const char IncludeDirElementName[] = "includedir"; static const char DirElementName[] = "dir"; static const char DirNameAttrib[] = "name"; @@ -87,6 +88,9 @@ bool ProjectFile::Read(const QString &filename) if (insideProject && xmlReader.name() == PathsElementName) ReadCheckPaths(xmlReader); + if (insideProject && xmlReader.name() == ImportProjectElementName) + ReadImportProject(xmlReader); + // Find include directory from inside project element if (insideProject && xmlReader.name() == IncludeDirElementName) ReadIncludeDirs(xmlReader); @@ -198,6 +202,31 @@ void ProjectFile::ReadRootPath(QXmlStreamReader &reader) mRootPath = name; } +void ProjectFile::ReadImportProject(QXmlStreamReader &reader) +{ + mImportProject.clear(); + do { + const QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::Characters: + mImportProject = reader.text().toString(); + case QXmlStreamReader::EndElement: + return; + // Not handled + case QXmlStreamReader::StartElement: + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (1); +} + void ProjectFile::ReadIncludeDirs(QXmlStreamReader &reader) { QXmlStreamReader::TokenType type; @@ -447,6 +476,12 @@ bool ProjectFile::Write(const QString &filename) xmlWriter.writeEndElement(); } + if (!mImportProject.isEmpty()) { + xmlWriter.writeStartElement(ImportProjectElementName); + xmlWriter.writeCharacters(mImportProject); + xmlWriter.writeEndElement(); + } + if (!mIncludeDirs.isEmpty()) { xmlWriter.writeStartElement(IncludeDirElementName); foreach (QString incdir, mIncludeDirs) { diff --git a/gui/projectfile.h b/gui/projectfile.h index 5f82cd966..fe1f09401 100644 --- a/gui/projectfile.h +++ b/gui/projectfile.h @@ -54,6 +54,10 @@ public: return mRootPath; } + QString GetImportProject() const { + return mImportProject; + } + /** * @brief Get list of include directories. * @return list of directories. @@ -106,6 +110,10 @@ public: mRootPath = rootpath; } + void SetImportProject(const QString &importProject) { + mImportProject = importProject; + } + /** * @brief Set list of includes. * @param includes List of defines. @@ -173,6 +181,12 @@ protected: */ void ReadRootPath(QXmlStreamReader &reader); + /** + * @brief Read importproject from XML. + * @param reader XML stream reader. + */ + void ReadImportProject(QXmlStreamReader &reader); + /** * @brief Read list of include directories from XML. * @param reader XML stream reader. @@ -220,6 +234,8 @@ private: */ QString mRootPath; + QString mImportProject; + /** * @brief List of include directories used to search include files. */ diff --git a/gui/projectfile.ui b/gui/projectfile.ui index 01a367ec1..f41868332 100644 --- a/gui/projectfile.ui +++ b/gui/projectfile.ui @@ -6,8 +6,8 @@ 0 0 - 467 - 329 + 615 + 357 @@ -23,28 +23,7 @@ Project - - - - - - - Defines: - - - mEditDefines - - - - - - - Defines must be separated by a semicolon ';' - - - - - + @@ -62,6 +41,19 @@ + + + + Qt::Vertical + + + + 20 + 97 + + + + @@ -83,6 +75,120 @@ + + + + Qt::Vertical + + + + 20 + 96 + + + + + + + + + Visual Studio + + + + + + Visual Studio + +Cppcheck can import visual studio solutions and projects. + +Files to check, include paths, configurations, defines, platform settings are imported. + +Library settings are not imported. + + + + + + + + + + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 149 + + + + + + + + + CMake + + + + + + Compile database + +Cppcheck can import files to analyse, include paths, defines from the compile database. + +Platform settings are not provided in compile database and must be configured. + +Library settings are not provided in compile database, be careful about this configuration also. + + + + + + + + + + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 149 + + + + + + + + + Other + + @@ -152,22 +258,53 @@ - - - - - Includes - - - - - Include directories: - - + + + + + Defines: + + + mEditDefines + + + + + + + Defines must be separated by a semicolon ';' + + + + + + + + + + Include Paths: + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + @@ -229,6 +366,19 @@ + + + + Qt::Vertical + + + + 20 + 0 + + + + @@ -354,19 +504,7 @@ mButtons - tabWidget - mEditDefines mEditProjectRoot - mListPaths - mBtnAddPath - mBtnEditPath - mBtnRemovePath - mListIncludeDirs - mBtnAddInclude - mBtnEditInclude - mBtnRemoveInclude - mBtnIncludeUp - mBtnIncludeDown mListExcludedPaths mBtnAddIgnorePath mBtnEditIgnorePath @@ -381,8 +519,8 @@ accept() - 266 - 319 + 270 + 352 157 @@ -397,8 +535,8 @@ reject() - 334 - 319 + 338 + 352 286 diff --git a/gui/projectfiledialog.cpp b/gui/projectfiledialog.cpp index a9ae139a4..65f67ac87 100644 --- a/gui/projectfiledialog.cpp +++ b/gui/projectfiledialog.cpp @@ -83,6 +83,8 @@ ProjectFileDialog::ProjectFileDialog(const QString &path, QWidget *parent) } connect(mUI.mButtons, SIGNAL(accepted()), this, SLOT(accept())); + connect(mUI.mBrowseCompileDatabase, SIGNAL(clicked()), this, SLOT(BrowseCompileDatabase())); + connect(mUI.mBrowseVisualStudio, SIGNAL(clicked()), this, SLOT(BrowseVisualStudio())); connect(mUI.mBtnAddInclude, SIGNAL(clicked()), this, SLOT(AddIncludeDir())); connect(mUI.mBtnAddPath, SIGNAL(clicked()), this, SLOT(AddPath())); connect(mUI.mBtnEditInclude, SIGNAL(clicked()), this, SLOT(EditIncludeDir())); @@ -117,6 +119,26 @@ void ProjectFileDialog::SaveSettings() const settings.setValue(SETTINGS_PROJECT_DIALOG_HEIGHT, size().height()); } +void ProjectFileDialog::BrowseCompileDatabase() +{ + const QFileInfo inf(mFilePath); + const QDir &dir = inf.absoluteDir(); + QString fileName = QFileDialog::getOpenFileName(this, tr("Compile Database"), + dir.canonicalPath(), + tr("Compile database (compile_database.json)")); + mUI.mEditCompileDatabase->setText(dir.relativeFilePath(fileName)); +} + +void ProjectFileDialog::BrowseVisualStudio() +{ + const QFileInfo inf(mFilePath); + const QDir &dir = inf.absoluteDir(); + QString fileName = QFileDialog::getOpenFileName(this, tr("Visual Studio"), + dir.canonicalPath(), + tr("Visual Studio Solution/Project (*.sln *.vcxproj)")); + mUI.mEditVisualStudio->setText(dir.relativeFilePath(fileName)); +} + void ProjectFileDialog::AddIncludeDir(const QString &dir) { if (dir.isNull() || dir.isEmpty()) @@ -158,6 +180,11 @@ QString ProjectFileDialog::GetRootPath() const return root; } +QString ProjectFileDialog::GetImportProject() const +{ + return mUI.mEditCompileDatabase->text() + mUI.mEditVisualStudio->text(); +} + QStringList ProjectFileDialog::GetIncludePaths() const { const int count = mUI.mListIncludeDirs->count(); @@ -232,6 +259,16 @@ void ProjectFileDialog::SetRootPath(const QString &root) mUI.mEditProjectRoot->setText(newroot); } +void ProjectFileDialog::SetImportProject(const QString &importProject) +{ + mUI.mEditCompileDatabase->setText(""); + mUI.mEditVisualStudio->setText(""); + if (importProject.endsWith("compile_database.json", Qt::CaseInsensitive)) + mUI.mEditCompileDatabase->setText(importProject); + else if (importProject.endsWith(".sln",Qt::CaseInsensitive) || importProject.endsWith(".vcxproj",Qt::CaseInsensitive)) + mUI.mEditVisualStudio->setText(importProject); +} + void ProjectFileDialog::SetIncludepaths(const QStringList &includes) { foreach (QString dir, includes) { diff --git a/gui/projectfiledialog.h b/gui/projectfiledialog.h index 5a13b9d89..c7e655b3f 100644 --- a/gui/projectfiledialog.h +++ b/gui/projectfiledialog.h @@ -47,6 +47,8 @@ public: */ QString GetRootPath() const; + QString GetImportProject() const; + /** * @brief Return include paths from the dialog control. * @return List of include paths. @@ -89,6 +91,8 @@ public: */ void SetRootPath(const QString &root); + void SetImportProject(const QString &importProject); + /** * @brief Set include paths to dialog control. * @param includes List of include paths to set to dialog control. @@ -126,6 +130,17 @@ public: void SetSuppressions(const QStringList &suppressions); protected slots: + + /** + * @brief Browse for Visual Studio solution/project. + */ + void BrowseVisualStudio(); + + /** + * @brief Browse for Compile Database. + */ + void BrowseCompileDatabase(); + /** * @brief Browse for include directory. * Allow user to add new include directory to the list. diff --git a/gui/threadhandler.cpp b/gui/threadhandler.cpp index efba99e7a..937be7407 100644 --- a/gui/threadhandler.cpp +++ b/gui/threadhandler.cpp @@ -51,6 +51,12 @@ void ThreadHandler::SetFiles(const QStringList &files) mLastFiles = files; } +void ThreadHandler::SetProject(const ImportProject &prj) +{ + mResults.SetProject(prj); + mLastFiles.clear(); +} + void ThreadHandler::SetCheckFiles(bool all) { if (mRunningThreadCount == 0) { diff --git a/gui/threadhandler.h b/gui/threadhandler.h index c1be3c8f5..4a5020347 100644 --- a/gui/threadhandler.h +++ b/gui/threadhandler.h @@ -25,6 +25,7 @@ #include #include #include "threadresult.h" +#include "importproject.h" class ResultsView; class CheckThread; @@ -83,6 +84,13 @@ public: */ void SetFiles(const QStringList &files); + /** + * @brief Set project to check + * + * @param prj project to check + */ + void SetProject(const ImportProject &prj); + /** * @brief Start the threads to check the files * diff --git a/gui/threadresult.cpp b/gui/threadresult.cpp index 233710449..bc9a0449f 100644 --- a/gui/threadresult.cpp +++ b/gui/threadresult.cpp @@ -98,6 +98,17 @@ QString ThreadResult::GetNextFile() return mFiles.takeFirst(); } +ImportProject::FileSettings ThreadResult::GetNextFileSettings() +{ + QMutexLocker locker(&mutex); + if (mFileSettings.empty()) { + return ImportProject::FileSettings(); + } + const ImportProject::FileSettings fs = mFileSettings.front(); + mFileSettings.pop_front(); + return fs; +} + void ThreadResult::SetFiles(const QStringList &files) { QMutexLocker locker(&mutex); @@ -115,10 +126,29 @@ void ThreadResult::SetFiles(const QStringList &files) mMaxProgress = sizeOfFiles; } +void ThreadResult::SetProject(const ImportProject &prj) +{ + QMutexLocker locker(&mutex); + mFiles.clear(); + mFileSettings = prj.fileSettings; + mProgress = 0; + mFilesChecked = 0; + mTotalFiles = prj.fileSettings.size(); + + // Determine the total size of all of the files to check, so that we can + // show an accurate progress estimate + quint64 sizeOfFiles = 0; + foreach (const ImportProject::FileSettings& fs, prj.fileSettings) { + sizeOfFiles += QFile(QString::fromStdString(fs.filename)).size(); + } + mMaxProgress = sizeOfFiles; +} + void ThreadResult::ClearFiles() { QMutexLocker locker(&mutex); mFiles.clear(); + mFileSettings.clear(); mFilesChecked = 0; mTotalFiles = 0; } @@ -126,5 +156,5 @@ void ThreadResult::ClearFiles() int ThreadResult::GetFileCount() const { QMutexLocker locker(&mutex); - return mFiles.size(); + return mFiles.size() + mFileSettings.size(); } diff --git a/gui/threadresult.h b/gui/threadresult.h index 782258cce..fcd921d68 100644 --- a/gui/threadresult.h +++ b/gui/threadresult.h @@ -24,6 +24,7 @@ #include #include #include "errorlogger.h" +#include "importproject.h" class ErrorItem; @@ -46,12 +47,16 @@ public: */ QString GetNextFile(); + ImportProject::FileSettings GetNextFileSettings(); + /** * @brief Set list of files to check * @param files List of files to check */ void SetFiles(const QStringList &files); + void SetProject(const ImportProject &prj); + /** * @brief Clear files to check * @@ -120,6 +125,8 @@ protected: */ QStringList mFiles; + std::list mFileSettings; + /** * @brief Max progress *