/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2023 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "compliancereportdialog.h" #include "ui_compliancereportdialog.h" #include "errortypes.h" #include "filelist.h" #include "filesettings.h" #include "importproject.h" #include "projectfile.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void addHeaders(const QString& file1, QSet &allFiles) { if (allFiles.contains(file1)) return; QFile file(file1); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return; allFiles << file1; const QRegularExpression re("^#include[ ]*\"([^\">]+)\".*"); QTextStream in(&file); QString line = in.readLine(); while (!in.atEnd()) { if (line.startsWith("#include")) { const QRegularExpressionMatch match = re.match(line); if (match.hasMatch()) { QString hfile = match.captured(1); if (file1.contains("/")) hfile = file1.mid(0,file1.lastIndexOf("/") + 1) + hfile; addHeaders(hfile, allFiles); } } line = in.readLine(); } } static std::vector toStdStringList(const QStringList& from) { std::vector ret; std::transform(from.cbegin(), from.cend(), std::back_inserter(ret), [](const QString& e) { return e.toStdString(); }); return ret; } ComplianceReportDialog::ComplianceReportDialog(ProjectFile* projectFile, QString resultsFile) : QDialog(nullptr), mUI(new Ui::ComplianceReportDialog), mProjectFile(projectFile), mResultsFile(std::move(resultsFile)) { mUI->setupUi(this); mUI->mEditProjectName->setText(projectFile->getProjectName()); connect(mUI->buttonBox, &QDialogButtonBox::clicked, this, &ComplianceReportDialog::buttonClicked); mUI->mCodingStandard->clear(); if (projectFile->getCodingStandards().contains("misra-c-2023")) mUI->mCodingStandard->addItem("Misra C 2023"); else if (projectFile->getAddons().contains("misra")) mUI->mCodingStandard->addItem("Misra C 2012"); if (projectFile->getCodingStandards().contains("misra-c++-2008")) mUI->mCodingStandard->addItem("Misra C++ 2008"); if (projectFile->getCodingStandards().contains("cert-c-2016")) mUI->mCodingStandard->addItem("Cert C"); if (projectFile->getCodingStandards().contains("cert-c++-2016")) mUI->mCodingStandard->addItem("Cert C++"); mUI->mCodingStandard->addItems(projectFile->getCodingStandards()); } ComplianceReportDialog::~ComplianceReportDialog() { delete mUI; } void ComplianceReportDialog::buttonClicked(QAbstractButton* button) { switch (mUI->buttonBox->standardButton(button)) { case QDialogButtonBox::StandardButton::Save: save(); break; case QDialogButtonBox::StandardButton::Close: close(); break; default: break; }; } void ComplianceReportDialog::save() { const QString std(mUI->mCodingStandard->currentText().toLower().replace(" ", "-")); const QString outFile = QFileDialog::getSaveFileName(this, tr("Compliance report"), QDir::homePath() + "/" + std + "-compliance-report.html", tr("HTML files (*.html)")); if (outFile.isEmpty()) return; close(); const QString& projectName = mUI->mEditProjectName->text(); const QString& projectVersion = mUI->mEditProjectVersion->text(); const bool files = mUI->mCheckFiles->isChecked(); if (projectName != mProjectFile->getProjectName()) { mProjectFile->setProjectName(projectName); mProjectFile->write(); } QTemporaryFile tempFiles; if (files && tempFiles.open()) { QTextStream out(&tempFiles); FileList fileList; fileList.addPathList(mProjectFile->getCheckPaths()); if (!mProjectFile->getImportProject().isEmpty()) { QFileInfo inf(mProjectFile->getFilename()); QString prjfile; if (QFileInfo(mProjectFile->getImportProject()).isAbsolute()) prjfile = mProjectFile->getImportProject(); else prjfile = inf.canonicalPath() + '/' + mProjectFile->getImportProject(); ImportProject p; try { p.import(prjfile.toStdString()); } catch (InternalError &e) { QMessageBox msg(QMessageBox::Critical, tr("Save compliance report"), tr("Failed to import '%1' (%2), can not show files in compliance report").arg(prjfile, QString::fromStdString(e.errorMessage)), QMessageBox::Ok, this); msg.exec(); return; } p.ignorePaths(toStdStringList(mProjectFile->getExcludedPaths())); QDir dir(inf.absoluteDir()); for (const FileSettings& fs: p.fileSettings) fileList.addFile(dir.relativeFilePath(QString::fromStdString(fs.filename))); } QSet allFiles; for (const QString &sourcefile: fileList.getFileList()) addHeaders(sourcefile, allFiles); for (const QString& fileName: allFiles) { QFile f(fileName); if (f.open(QFile::ReadOnly)) { QCryptographicHash hash(QCryptographicHash::Algorithm::Md5); if (hash.addData(&f)) { for (auto b: hash.result()) out << QString::number((unsigned char)b,16); out << " " << fileName << "\n"; } } } tempFiles.close(); } QStringList suppressions; for (const auto& suppression: mProjectFile->getSuppressions()) { if (!suppression.errorId.empty()) suppressions.append(QString::fromStdString(suppression.errorId)); } QStringList args{"--project-name=" + projectName, "--project-version=" + projectVersion, "--output-file=" + outFile}; if (!suppressions.isEmpty()) args << "--suppressions=" + suppressions.join(","); args << ("--" + std); if (files) args << "--files=" + tempFiles.fileName(); args << mResultsFile; const QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalPath(); QProcess process; #ifdef Q_OS_WIN process.start(appPath + "/compliance-report.exe", args); #else process.start(appPath + "/compliance-report", args); #endif process.waitForFinished(); const QString output = process.readAll(); if (!output.isEmpty()) { QMessageBox msg(QMessageBox::Critical, tr("Save compliance report"), output, QMessageBox::Ok, this); msg.exec(); } }