diff --git a/gui/checkthread.cpp b/gui/checkthread.cpp index 228d1bd35..f444241e7 100644 --- a/gui/checkthread.cpp +++ b/gui/checkthread.cpp @@ -37,14 +37,35 @@ CheckThread::~CheckThread() void CheckThread::Check(const Settings &settings) { + mFiles.clear(); mCppcheck.settings() = settings; start(); } +void CheckThread::AnalyseWholeProgram(const QStringList &files) +{ + mFiles = files; + start(); +} + void CheckThread::run() { mState = Running; + if (!mFiles.isEmpty()) { + qDebug() << "Whole program analysis"; + const std::string &buildDir = mCppcheck.settings().buildDir; + if (!buildDir.empty()) { + std::map files2; + for (QString file : mFiles) + files2[file.toStdString()] = 0; + mCppcheck.analyseWholeProgram(buildDir, files2); + } + mFiles.clear(); + emit Done(); + return; + } + QString file = mResult.GetNextFile(); while (!file.isEmpty() && mState == Running) { qDebug() << "Checking file" << file; diff --git a/gui/checkthread.h b/gui/checkthread.h index fcc397eae..903b1b8f0 100644 --- a/gui/checkthread.h +++ b/gui/checkthread.h @@ -46,6 +46,12 @@ public: */ void Check(const Settings &settings); + /** + * @brief Run whole program analysis + * @param files All files + */ + void AnalyseWholeProgram(const QStringList &files); + /** * @brief method that is run in a thread * @@ -91,7 +97,9 @@ protected: * */ CppCheck mCppcheck; + private: + QStringList mFiles; }; /// @} #endif // CHECKTHREAD_H diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 234ca6bb1..7335cd4be 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -438,6 +438,20 @@ void MainWindow::DoCheckFiles(const QStringList &files) if (mProject) qDebug() << "Checking project file" << mProject->GetProjectFile()->GetFilename(); + if (!checkSettings.buildDir.empty()) { + QString s = QString::fromStdString(checkSettings.buildDir); + if (!s.endsWith('/')) + s += '/'; + s += "files.txt"; + + std::ofstream fout(s.toStdString()); + if (fout.is_open()) { + foreach (QString f, fileNames) { + fout << f.toStdString() << '\n'; + } + } + } + mThread->SetCheckFiles(true); mThread->Check(checkSettings, true); } @@ -742,6 +756,12 @@ Settings MainWindow::GetCppcheckSettings() // Only check the given -D configuration if (!defines.isEmpty()) result.maxConfigs = 1; + + QString buildDir = pfile->GetBuildDir(); + if (!buildDir.isEmpty()) { + QString prjpath = QFileInfo(pfile->GetFilename()).absolutePath(); + result.buildDir = (prjpath + '/' + buildDir).toStdString(); + } } // Include directories (and files) are searched in listed order. @@ -759,6 +779,8 @@ Settings MainWindow::GetCppcheckSettings() result.addEnabled("portability"); result.addEnabled("information"); result.addEnabled("missingInclude"); + if (!result.buildDir.empty()) + result.addEnabled("unusedFunction"); result.debug = false; result.debugwarnings = mSettings->value(SETTINGS_SHOW_DEBUG_WARNINGS, false).toBool(); result.quiet = false; diff --git a/gui/project.cpp b/gui/project.cpp index 67060b557..14af3f02d 100644 --- a/gui/project.cpp +++ b/gui/project.cpp @@ -86,53 +86,40 @@ bool Project::Open() bool Project::Edit() { ProjectFileDialog dlg(mFilename, mParentWidget); - QString root = mPFile->GetRootPath(); - dlg.SetRootPath(root); - QStringList includes = mPFile->GetIncludeDirs(); - dlg.SetIncludepaths(includes); - QStringList defines = mPFile->GetDefines(); - dlg.SetDefines(defines); - QStringList paths = mPFile->GetCheckPaths(); - dlg.SetPaths(paths); - QString importProject = mPFile->GetImportProject(); - dlg.SetImportProject(importProject); - QStringList ignorepaths = mPFile->GetExcludedPaths(); - dlg.SetExcludedPaths(ignorepaths); - QStringList libraries = mPFile->GetLibraries(); - dlg.SetLibraries(libraries); - QStringList suppressions = mPFile->GetSuppressions(); - dlg.SetSuppressions(suppressions); + dlg.SetRootPath(mPFile->GetRootPath()); + dlg.SetBuildDir(mPFile->GetBuildDir()); + dlg.SetIncludepaths(mPFile->GetIncludeDirs()); + dlg.SetDefines(mPFile->GetDefines()); + dlg.SetPaths(mPFile->GetCheckPaths()); + dlg.SetImportProject(mPFile->GetImportProject()); + dlg.SetExcludedPaths(mPFile->GetExcludedPaths()); + dlg.SetLibraries(mPFile->GetLibraries()); + dlg.SetSuppressions(mPFile->GetSuppressions()); - int rv = dlg.exec(); - 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(); - mPFile->SetDefines(defines); - QStringList paths = dlg.GetPaths(); - mPFile->SetCheckPaths(paths); - QStringList excludedpaths = dlg.GetExcludedPaths(); - mPFile->SetExcludedPaths(excludedpaths); - QStringList libraries = dlg.GetLibraries(); - mPFile->SetLibraries(libraries); - QStringList suppressions = dlg.GetSuppressions(); - mPFile->SetSuppressions(suppressions); + if (dlg.exec() != QDialog::Accepted) + return false; - bool writeSuccess = mPFile->Write(); - if (!writeSuccess) { - QMessageBox msg(QMessageBox::Critical, - tr("Cppcheck"), - tr("Could not write the project file."), - QMessageBox::Ok, - mParentWidget); - msg.exec(); - } - return writeSuccess; + mPFile->SetRootPath(dlg.GetRootPath()); + mPFile->SetBuildDir(dlg.GetBuildDir()); + mPFile->SetImportProject(dlg.GetImportProject()); + mPFile->SetIncludes(dlg.GetIncludePaths()); + mPFile->SetDefines(dlg.GetDefines()); + mPFile->SetCheckPaths(dlg.GetPaths()); + mPFile->SetExcludedPaths(dlg.GetExcludedPaths()); + mPFile->SetLibraries(dlg.GetLibraries()); + mPFile->SetSuppressions(dlg.GetSuppressions()); + + if (!mPFile->Write()) { + QMessageBox msg(QMessageBox::Critical, + tr("Cppcheck"), + tr("Could not write the project file."), + QMessageBox::Ok, + mParentWidget); + msg.exec(); + return false; } - return false; + + return true; } void Project::Create() diff --git a/gui/projectfile.cpp b/gui/projectfile.cpp index e00a4a4c7..e21529540 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 BuildDirElementName[] = "builddir"; static const char ImportProjectElementName[] = "importproject"; static const char IncludeDirElementName[] = "includedir"; static const char DirElementName[] = "dir"; @@ -84,6 +85,10 @@ bool ProjectFile::Read(const QString &filename) if (insideProject && xmlReader.name() == RootPathName) ReadRootPath(xmlReader); + // Read root path from inside project element + if (insideProject && xmlReader.name() == BuildDirElementName) + ReadBuildDir(xmlReader); + // Find paths to check from inside project element if (insideProject && xmlReader.name() == PathsElementName) ReadCheckPaths(xmlReader); @@ -152,6 +157,31 @@ void ProjectFile::ReadRootPath(QXmlStreamReader &reader) mRootPath = name; } +void ProjectFile::ReadBuildDir(QXmlStreamReader &reader) +{ + mBuildDir.clear(); + do { + const QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::Characters: + mBuildDir = 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::ReadImportProject(QXmlStreamReader &reader) { mImportProject.clear(); @@ -426,6 +456,12 @@ bool ProjectFile::Write(const QString &filename) xmlWriter.writeEndElement(); } + if (!mBuildDir.isEmpty()) { + xmlWriter.writeStartElement(BuildDirElementName); + xmlWriter.writeCharacters(mBuildDir); + xmlWriter.writeEndElement(); + } + if (!mImportProject.isEmpty()) { xmlWriter.writeStartElement(ImportProjectElementName); xmlWriter.writeCharacters(mImportProject); diff --git a/gui/projectfile.h b/gui/projectfile.h index 0b9a162b2..44b5e8f2f 100644 --- a/gui/projectfile.h +++ b/gui/projectfile.h @@ -54,6 +54,10 @@ public: return mRootPath; } + QString GetBuildDir() const { + return mBuildDir; + } + QString GetImportProject() const { return mImportProject; } @@ -122,6 +126,11 @@ public: mRootPath = rootpath; } + + void SetBuildDir(const QString &buildDir) { + mBuildDir = buildDir; + } + void SetImportProject(const QString &importProject) { mImportProject = importProject; } @@ -193,6 +202,8 @@ protected: */ void ReadRootPath(QXmlStreamReader &reader); + void ReadBuildDir(QXmlStreamReader &reader); + /** * @brief Read importproject from XML. * @param reader XML stream reader. @@ -251,6 +262,10 @@ private: */ QString mRootPath; + /** Cppcheck build dir */ + QString mBuildDir; + + /** Visual studio project/solution , compile database */ QString mImportProject; /** diff --git a/gui/projectfile.ui b/gui/projectfile.ui index f41868332..145b938d7 100644 --- a/gui/projectfile.ui +++ b/gui/projectfile.ui @@ -6,8 +6,8 @@ 0 0 - 615 - 357 + 635 + 368 @@ -41,6 +41,27 @@ + + + + + + Cppcheck build dir (optional) + + + + + + + + + + ... + + + + + diff --git a/gui/projectfiledialog.cpp b/gui/projectfiledialog.cpp index 65f67ac87..8abb5bd5e 100644 --- a/gui/projectfiledialog.cpp +++ b/gui/projectfiledialog.cpp @@ -83,6 +83,7 @@ ProjectFileDialog::ProjectFileDialog(const QString &path, QWidget *parent) } connect(mUI.mButtons, SIGNAL(accepted()), this, SLOT(accept())); + connect(mUI.buildDirBrowse, SIGNAL(clicked()), this, SLOT(BrowseBuildDir())); connect(mUI.mBrowseCompileDatabase, SIGNAL(clicked()), this, SLOT(BrowseCompileDatabase())); connect(mUI.mBrowseVisualStudio, SIGNAL(clicked()), this, SLOT(BrowseVisualStudio())); connect(mUI.mBtnAddInclude, SIGNAL(clicked()), this, SLOT(AddIncludeDir())); @@ -119,6 +120,38 @@ void ProjectFileDialog::SaveSettings() const settings.setValue(SETTINGS_PROJECT_DIALOG_HEIGHT, size().height()); } + +QString ProjectFileDialog::getExistingDirectory(const QString &caption, bool trailingSlash) { + const QFileInfo inf(mFilePath); + const QString rootpath = inf.absolutePath(); + QString selectedDir = QFileDialog::getExistingDirectory(this, + caption, + rootpath); + + if (selectedDir.isEmpty()) + return QString(); + + // Check if the path is relative to project file's path and if so + // make it a relative path instead of absolute path. + const QDir dir(rootpath); + const QString relpath(dir.relativeFilePath(selectedDir)); + if (!relpath.startsWith(".")) + selectedDir = relpath; + + // Trailing slash.. + if (trailingSlash && !selectedDir.endsWith('/')) + selectedDir += '/'; + + return selectedDir; +} + +void ProjectFileDialog::BrowseBuildDir() +{ + const QString dir(getExistingDirectory(tr("Select Cppcheck build dir"), false)); + if (!dir.isEmpty()) + mUI.buildDirEdit->setText(dir); +} + void ProjectFileDialog::BrowseCompileDatabase() { const QFileInfo inf(mFilePath); @@ -180,6 +213,10 @@ QString ProjectFileDialog::GetRootPath() const return root; } +QString ProjectFileDialog::GetBuildDir() const { + return mUI.buildDirEdit->text(); +} + QString ProjectFileDialog::GetImportProject() const { return mUI.mEditCompileDatabase->text() + mUI.mEditVisualStudio->text(); @@ -253,10 +290,12 @@ QStringList ProjectFileDialog::GetSuppressions() const return suppressions; } -void ProjectFileDialog::SetRootPath(const QString &root) -{ - QString newroot = QDir::toNativeSeparators(root); - mUI.mEditProjectRoot->setText(newroot); +void ProjectFileDialog::SetRootPath(const QString &root) { + mUI.mEditProjectRoot->setText(QDir::toNativeSeparators(root)); +} + +void ProjectFileDialog::SetBuildDir(const QString &buildDir) { + mUI.buildDirEdit->setText(buildDir); } void ProjectFileDialog::SetImportProject(const QString &importProject) @@ -321,38 +360,16 @@ void ProjectFileDialog::SetSuppressions(const QStringList &suppressions) void ProjectFileDialog::AddIncludeDir() { - const QFileInfo inf(mFilePath); - const QString rootpath = inf.absolutePath(); - QString selectedDir = QFileDialog::getExistingDirectory(this, - tr("Select include directory"), - rootpath); - - if (!selectedDir.isEmpty()) { - // Check if the path is relative to project file's path and if so - // make it a relative path instead of absolute path. - const QDir dir(selectedDir); - QString absPath = dir.absolutePath(); - if (absPath.startsWith(rootpath)) { - // Remove also the slash from begin of new relative path - selectedDir = absPath.remove(0, rootpath.length() + 1); - } - if (!selectedDir.endsWith("/")) - selectedDir += '/'; - AddIncludeDir(selectedDir); - } + const QString dir = getExistingDirectory(tr("Select include directory"), true); + if (!dir.isEmpty()) + AddIncludeDir(dir); } void ProjectFileDialog::AddPath() { - QFileInfo inf(mFilePath); - const QString rootpath = inf.absolutePath(); - QString selectedDir = QFileDialog::getExistingDirectory(this, - tr("Select a directory to check"), - rootpath); - - if (!selectedDir.isEmpty()) { - AddPath(selectedDir); - } + QString dir = getExistingDirectory(tr("Select a directory to check"), false); + if (!dir.isEmpty()) + AddPath(dir); } void ProjectFileDialog::RemoveIncludeDir() @@ -383,18 +400,9 @@ void ProjectFileDialog::RemovePath() void ProjectFileDialog::AddExcludePath() { - QFileInfo inf(mFilePath); - const QString rootpath = inf.absolutePath(); - - QString selectedDir = QFileDialog::getExistingDirectory(this, - tr("Select directory to ignore"), - rootpath); - - if (!selectedDir.isEmpty()) { - if (!selectedDir.endsWith('/')) - selectedDir += '/'; - AddExcludePath(selectedDir); - } + QString dir = getExistingDirectory(tr("Select directory to ignore"), true); + if (!dir.isEmpty()) + AddExcludePath(dir); } void ProjectFileDialog::EditExcludePath() diff --git a/gui/projectfiledialog.h b/gui/projectfiledialog.h index c7e655b3f..08685d431 100644 --- a/gui/projectfiledialog.h +++ b/gui/projectfiledialog.h @@ -49,6 +49,9 @@ public: QString GetImportProject() const; + /** Get Cppcheck build dir */ + QString GetBuildDir() const; + /** * @brief Return include paths from the dialog control. * @return List of include paths. @@ -91,6 +94,9 @@ public: */ void SetRootPath(const QString &root); + /** Set build dir */ + void SetBuildDir(const QString &buildDir); + void SetImportProject(const QString &importProject); /** @@ -131,6 +137,11 @@ public: protected slots: + /** + * @brief Browse for build dir. + */ + void BrowseBuildDir(); + /** * @brief Browse for Visual Studio solution/project. */ @@ -247,6 +258,8 @@ private: /** @brief Library checkboxes */ QList mLibraryCheckboxes; + + QString getExistingDirectory(const QString &caption, bool trailingSlash); }; /// @} diff --git a/gui/threadhandler.cpp b/gui/threadhandler.cpp index 937be7407..8980f241e 100644 --- a/gui/threadhandler.cpp +++ b/gui/threadhandler.cpp @@ -29,7 +29,9 @@ ThreadHandler::ThreadHandler(QObject *parent) : QObject(parent), mScanDuration(0), - mRunningThreadCount(0) + mRunningThreadCount(0), + mAnalyseWholeProgram(false) + { SetThreadCount(1); } @@ -43,6 +45,7 @@ void ThreadHandler::ClearFiles() { mLastFiles.clear(); mResults.ClearFiles(); + mAnalyseWholeProgram = false; } void ThreadHandler::SetFiles(const QStringList &files) @@ -94,6 +97,8 @@ void ThreadHandler::Check(const Settings &settings, bool all) // Date and time when checking starts.. mCheckStartTime = QDateTime::currentDateTime(); + mAnalyseWholeProgram = true; + mTime.start(); } @@ -137,10 +142,17 @@ void ThreadHandler::RemoveThreads() } mThreads.clear(); + mAnalyseWholeProgram = false; } void ThreadHandler::ThreadDone() { + if (mRunningThreadCount == 1 && mAnalyseWholeProgram) { + mThreads[0]->AnalyseWholeProgram(mLastFiles); + mAnalyseWholeProgram = false; + return; + } + mRunningThreadCount--; if (mRunningThreadCount == 0) { emit Done(); @@ -158,6 +170,7 @@ void ThreadHandler::ThreadDone() void ThreadHandler::Stop() { mCheckStartTime = QDateTime(); + mAnalyseWholeProgram = false; for (int i = 0; i < mThreads.size(); i++) { mThreads[i]->stop(); } diff --git a/gui/threadhandler.h b/gui/threadhandler.h index 4a5020347..ce2f1baf8 100644 --- a/gui/threadhandler.h +++ b/gui/threadhandler.h @@ -234,6 +234,8 @@ protected: * */ int mRunningThreadCount; + + bool mAnalyseWholeProgram; private: /**