X Tutup
Skip to content

Commit e1b313b

Browse files
committed
GUI: Generate compliance report
1 parent 7c7ccdf commit e1b313b

File tree

10 files changed

+363
-0
lines changed

10 files changed

+363
-0
lines changed

gui/compliancereportdialog.cpp

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
#include "compliancereportdialog.h"
2+
#include "ui_compliancereportdialog.h"
3+
4+
#include "filelist.h"
5+
#include "projectfile.h"
6+
7+
#include <QCryptographicHash>
8+
#include <QFile>
9+
#include <QFileDialog>
10+
#include <QFileInfo>
11+
#include <QMessageBox>
12+
#include <QProcess>
13+
#include <QRegularExpression>
14+
#include <QSet>
15+
#include <QTemporaryFile>
16+
#include <QTextStream>
17+
18+
static void addHeaders(const QString& file1, QSet<QString> &allFiles) {
19+
if (allFiles.contains(file1))
20+
return;
21+
QFile file(file1);
22+
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
23+
return;
24+
allFiles << file1;
25+
const QRegularExpression re("^#include[ ]*\"([^\">]+)\".*");
26+
QTextStream in(&file);
27+
QString line = in.readLine();
28+
while (!in.atEnd()) {
29+
if (line.startsWith("#include")) {
30+
const QRegularExpressionMatch match = re.match(line);
31+
if (match.hasMatch()) {
32+
QString hfile = match.captured(1);
33+
if (file1.contains("/"))
34+
hfile = file1.mid(0,file1.lastIndexOf("/") + 1) + hfile;
35+
addHeaders(hfile, allFiles);
36+
}
37+
}
38+
line = in.readLine();
39+
}
40+
}
41+
42+
static std::vector<std::string> toStdStringList(const QStringList& from) {
43+
std::vector<std::string> ret;
44+
std::transform(from.cbegin(), from.cend(), std::back_inserter(ret), [](const QString& e) {
45+
return e.toStdString();
46+
});
47+
return ret;
48+
}
49+
50+
ComplianceReportDialog::ComplianceReportDialog(ProjectFile* projectFile, QString resultsFile) :
51+
QDialog(nullptr),
52+
mUI(new Ui::ComplianceReportDialog),
53+
mProjectFile(projectFile),
54+
mResultsFile(std::move(resultsFile))
55+
{
56+
mUI->setupUi(this);
57+
mUI->mEditProjectName->setText(projectFile->getProjectName());
58+
connect(mUI->buttonBox, &QDialogButtonBox::clicked, this, &ComplianceReportDialog::buttonClicked);
59+
}
60+
61+
ComplianceReportDialog::~ComplianceReportDialog()
62+
{
63+
delete mUI;
64+
}
65+
66+
void ComplianceReportDialog::buttonClicked(QAbstractButton* button)
67+
{
68+
switch (mUI->buttonBox->standardButton(button)) {
69+
case QDialogButtonBox::StandardButton::Save:
70+
save();
71+
break;
72+
case QDialogButtonBox::StandardButton::Close:
73+
close();
74+
break;
75+
default:
76+
break;
77+
};
78+
}
79+
80+
void ComplianceReportDialog::save()
81+
{
82+
const QString outFile = QFileDialog::getSaveFileName(this,
83+
tr("Compliance report"),
84+
QDir::homePath() + "/misra-c-2012-compliance-report.html",
85+
tr("HTML files (*.html)"));
86+
if (outFile.isEmpty())
87+
return;
88+
89+
close();
90+
91+
const QString& projectName = mUI->mEditProjectName->text();
92+
const QString& projectVersion = mUI->mEditProjectVersion->text();
93+
const bool files = mUI->mCheckFiles->isChecked();
94+
95+
if (projectName != mProjectFile->getProjectName()) {
96+
mProjectFile->setProjectName(projectName);
97+
mProjectFile->write();
98+
}
99+
100+
QTemporaryFile tempFiles;
101+
if (files && tempFiles.open()) {
102+
QTextStream out(&tempFiles);
103+
FileList fileList;
104+
fileList.addPathList(mProjectFile->getCheckPaths());
105+
if (!mProjectFile->getImportProject().isEmpty()) {
106+
QFileInfo inf(mProjectFile->getFilename());
107+
108+
QString prjfile;
109+
if (QFileInfo(mProjectFile->getImportProject()).isAbsolute())
110+
prjfile = mProjectFile->getImportProject();
111+
else
112+
prjfile = inf.canonicalPath() + '/' + mProjectFile->getImportProject();
113+
114+
ImportProject p;
115+
try {
116+
p.import(prjfile.toStdString());
117+
} catch (InternalError &e) {
118+
QMessageBox msg(QMessageBox::Critical,
119+
tr("Save compliance report"),
120+
tr("Failed to import '%1', can not show files in compliance report").arg(prjfile),
121+
QMessageBox::Ok,
122+
this);
123+
msg.exec();
124+
return;
125+
}
126+
127+
p.ignorePaths(toStdStringList(mProjectFile->getExcludedPaths()));
128+
129+
QDir dir(inf.absoluteDir());
130+
for (const ImportProject::FileSettings& fs: p.fileSettings)
131+
fileList.addFile(dir.relativeFilePath(QString::fromStdString(fs.filename)));
132+
}
133+
134+
QSet<QString> allFiles;
135+
for (const QString &sourcefile: fileList.getFileList())
136+
addHeaders(sourcefile, allFiles);
137+
for (const QString& fileName: allFiles) {
138+
QFile f(fileName);
139+
if (f.open(QFile::ReadOnly)) {
140+
QCryptographicHash hash(QCryptographicHash::Algorithm::Md5);
141+
if (hash.addData(&f)) {
142+
for (auto b: hash.result())
143+
out << QString::number((unsigned char)b,16);
144+
out << " " << fileName << "\n";
145+
}
146+
}
147+
}
148+
tempFiles.close();
149+
}
150+
151+
QStringList args{"--compliant=misra-c2012-1.1",
152+
"--compliant=misra-c2012-1.2",
153+
"--project-name=" + projectName,
154+
"--project-version=" + projectVersion,
155+
"--output-file=" + outFile};
156+
if (files)
157+
args << "--files=" + tempFiles.fileName();
158+
args << mResultsFile;
159+
160+
const QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalPath();
161+
162+
QProcess process;
163+
#ifdef Q_OS_WIN
164+
process.start(appPath + "/compliance-report.exe", args);
165+
#else
166+
process.start(appPath + "/compliance-report", args);
167+
#endif
168+
process.waitForFinished();
169+
}

gui/compliancereportdialog.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#ifndef COMPLIANCEREPORTDIALOG_H
2+
#define COMPLIANCEREPORTDIALOG_H
3+
4+
#include <QDialog>
5+
6+
namespace Ui {
7+
class ComplianceReportDialog;
8+
}
9+
10+
class ProjectFile;
11+
class QAbstractButton;
12+
13+
class ComplianceReportDialog final : public QDialog
14+
{
15+
Q_OBJECT
16+
17+
public:
18+
explicit ComplianceReportDialog(ProjectFile* projectFile, QString resultsFile);
19+
~ComplianceReportDialog() final;
20+
21+
private slots:
22+
void buttonClicked(QAbstractButton* button);
23+
24+
private:
25+
void save();
26+
27+
Ui::ComplianceReportDialog *mUI;
28+
ProjectFile* mProjectFile;
29+
QString mResultsFile;
30+
};
31+
32+
#endif // COMPLIANCEREPORTDIALOG_H

gui/compliancereportdialog.ui

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>ComplianceReportDialog</class>
4+
<widget class="QDialog" name="ComplianceReportDialog">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>403</width>
10+
<height>199</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>Dialog</string>
15+
</property>
16+
<layout class="QVBoxLayout" name="verticalLayout">
17+
<item>
18+
<layout class="QGridLayout" name="gridLayout">
19+
<item row="0" column="0">
20+
<widget class="QLabel" name="label">
21+
<property name="text">
22+
<string>Project name</string>
23+
</property>
24+
</widget>
25+
</item>
26+
<item row="0" column="1">
27+
<widget class="QLineEdit" name="mEditProjectName"/>
28+
</item>
29+
<item row="1" column="0">
30+
<widget class="QLabel" name="label_2">
31+
<property name="text">
32+
<string>Project version</string>
33+
</property>
34+
</widget>
35+
</item>
36+
<item row="1" column="1">
37+
<widget class="QLineEdit" name="mEditProjectVersion"/>
38+
</item>
39+
</layout>
40+
</item>
41+
<item>
42+
<widget class="QCheckBox" name="mCheckFiles">
43+
<property name="text">
44+
<string>List of files with md5 checksums</string>
45+
</property>
46+
<property name="checked">
47+
<bool>true</bool>
48+
</property>
49+
</widget>
50+
</item>
51+
<item>
52+
<spacer name="verticalSpacer">
53+
<property name="orientation">
54+
<enum>Qt::Vertical</enum>
55+
</property>
56+
<property name="sizeHint" stdset="0">
57+
<size>
58+
<width>20</width>
59+
<height>40</height>
60+
</size>
61+
</property>
62+
</spacer>
63+
</item>
64+
<item>
65+
<widget class="QDialogButtonBox" name="buttonBox">
66+
<property name="orientation">
67+
<enum>Qt::Horizontal</enum>
68+
</property>
69+
<property name="standardButtons">
70+
<set>QDialogButtonBox::Close|QDialogButtonBox::Save</set>
71+
</property>
72+
</widget>
73+
</item>
74+
</layout>
75+
</widget>
76+
<resources/>
77+
<connections/>
78+
</ui>

gui/gui.pro

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ win32 {
6363
RESOURCES = gui.qrc
6464
FORMS = about.ui \
6565
applicationdialog.ui \
66+
compliancereportdialog.ui \
6667
fileview.ui \
6768
helpdialog.ui \
6869
mainwindow.ui \
@@ -122,6 +123,7 @@ HEADERS += aboutdialog.h \
122123
codeeditstyledialog.h \
123124
codeeditor.h \
124125
common.h \
126+
compliancereportdialog.h \
125127
csvreport.h \
126128
erroritem.h \
127129
filelist.h \
@@ -162,6 +164,7 @@ SOURCES += aboutdialog.cpp \
162164
codeeditstyledialog.cpp \
163165
codeeditor.cpp \
164166
common.cpp \
167+
compliancereportdialog.cpp \
165168
csvreport.cpp \
166169
erroritem.cpp \
167170
filelist.cpp \

gui/mainwindow.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "cppcheck.h"
2626
#include "errortypes.h"
2727
#include "filelist.h"
28+
#include "compliancereportdialog.h"
2829
#include "fileviewdialog.h"
2930
#include "helpdialog.h"
3031
#include "importproject.h"
@@ -68,6 +69,7 @@
6869
#include <QTimer>
6970
#include <QtNetwork/QNetworkAccessManager>
7071
#include <QtNetwork/QNetworkReply>
72+
#include <QTemporaryFile>
7173

7274
static const QString OnlineHelpURL("https://cppcheck.sourceforge.io/manual.html");
7375
static const QString compile_commands_json("compile_commands.json");
@@ -154,6 +156,7 @@ MainWindow::MainWindow(TranslationHandler* th, QSettings* settings) :
154156

155157
connect(mUI->mActionStop, &QAction::triggered, this, &MainWindow::stopAnalysis);
156158
connect(mUI->mActionSave, &QAction::triggered, this, &MainWindow::save);
159+
connect(mUI->mActionComplianceReport, &QAction::triggered, this, &MainWindow::complianceReport);
157160

158161
// About menu
159162
connect(mUI->mActionAbout, &QAction::triggered, this, &MainWindow::about);
@@ -191,6 +194,8 @@ MainWindow::MainWindow(TranslationHandler* th, QSettings* settings) :
191194
else
192195
formatAndSetTitle();
193196

197+
mUI->mActionComplianceReport->setVisible(isCppcheckPremium());
198+
194199
enableCheckButtons(true);
195200

196201
mUI->mActionPrint->setShortcut(QKeySequence::Print);
@@ -1290,6 +1295,10 @@ void MainWindow::enableCheckButtons(bool enable)
12901295
}
12911296

12921297
mUI->mActionAnalyzeDirectory->setEnabled(enable);
1298+
1299+
if (isCppcheckPremium()) {
1300+
mUI->mActionComplianceReport->setEnabled(enable && mProjectFile && mProjectFile->getAddons().contains("misra"));
1301+
}
12931302
}
12941303

12951304
void MainWindow::enableResultsButtons()
@@ -1459,6 +1468,20 @@ void MainWindow::save()
14591468
}
14601469
}
14611470

1471+
void MainWindow::complianceReport()
1472+
{
1473+
if (isCppcheckPremium() && mProjectFile && mProjectFile->getAddons().contains("misra")) {
1474+
QTemporaryFile tempResults;
1475+
tempResults.open();
1476+
tempResults.close();
1477+
1478+
mUI->mResults->save(tempResults.fileName(), Report::XMLV2);
1479+
1480+
ComplianceReportDialog dlg(mProjectFile, tempResults.fileName());
1481+
dlg.exec();
1482+
}
1483+
}
1484+
14621485
void MainWindow::resultsAdded()
14631486
{}
14641487

gui/mainwindow.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ public slots:
168168
/** @brief Slot to save results */
169169
void save();
170170

171+
/** @brief Slot to generate compliance report */
172+
void complianceReport();
173+
171174
/** @brief Slot to create new project file */
172175
void newProjectFile();
173176

0 commit comments

Comments
 (0)
X Tutup