Skip to content

Commit 32e5697

Browse files
committed
executeCommand in CppCheckExecutor or QCheckThread
1 parent baca1fb commit 32e5697

11 files changed

Lines changed: 145 additions & 95 deletions

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ $(libcppdir)/ctu.o: lib/ctu.cpp externals/tinyxml/tinyxml2.h lib/astutils.h lib/
496496
$(libcppdir)/errorlogger.o: lib/errorlogger.cpp externals/tinyxml/tinyxml2.h lib/analyzerinfo.h lib/check.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h
497497
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/errorlogger.o $(libcppdir)/errorlogger.cpp
498498

499-
$(libcppdir)/exprengine.o: lib/exprengine.cpp lib/astutils.h lib/config.h lib/errorlogger.h lib/exprengine.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h
499+
$(libcppdir)/exprengine.o: lib/exprengine.cpp externals/z3_version.h lib/astutils.h lib/config.h lib/errorlogger.h lib/exprengine.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h
500500
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/exprengine.o $(libcppdir)/exprengine.cpp
501501

502502
$(libcppdir)/forwardanalyzer.o: lib/forwardanalyzer.cpp lib/astutils.h lib/config.h lib/errorlogger.h lib/forwardanalyzer.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h

cli/cppcheckexecutor.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ int CppCheckExecutor::check(int argc, const char* const argv[])
209209

210210
CheckUnusedFunctions::clear();
211211

212-
CppCheck cppCheck(*this, true);
212+
CppCheck cppCheck(*this, true, executeCommand);
213213

214214
const Settings& settings = cppCheck.settings();
215215
mSettings = &settings;
@@ -1162,3 +1162,36 @@ bool CppCheckExecutor::tryLoadLibrary(Library& destination, const char* basepath
11621162
}
11631163
return true;
11641164
}
1165+
1166+
/**
1167+
* Execute a shell command and read the output from it. Returns true if command terminated successfully.
1168+
*/
1169+
bool CppCheckExecutor::executeCommand(std::string exe, std::vector<std::string> args, std::string redirect, std::string *output)
1170+
{
1171+
output->clear();
1172+
1173+
std::string joinedArgs;
1174+
for (const std::string arg: args) {
1175+
if (!joinedArgs.empty())
1176+
joinedArgs += " ";
1177+
joinedArgs += arg;
1178+
}
1179+
1180+
#ifdef _WIN32
1181+
// Extra quoutes are needed in windows if filename has space
1182+
if (exe.find(" ") != std::string::npos)
1183+
exe = "\"" + exe + "\"";
1184+
const std::string cmd = exe + " " + joinedArgs + " " + redirect;
1185+
std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(cmd.c_str(), "r"), _pclose);
1186+
#else
1187+
const std::string cmd = exe + " " + joinedArgs + " " + redirect;
1188+
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
1189+
#endif
1190+
if (!pipe)
1191+
return false;
1192+
char buffer[1024];
1193+
while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr)
1194+
*output += buffer;
1195+
return true;
1196+
}
1197+

cli/cppcheckexecutor.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ class CppCheckExecutor : public ErrorLogger {
108108
*/
109109
static bool tryLoadLibrary(Library& destination, const char* basepath, const char* filename);
110110

111+
/**
112+
* Execute a shell command and read the output from it. Returns true if command terminated successfully.
113+
*/
114+
static bool executeCommand(std::string exe, std::vector<std::string> args, std::string redirect, std::string *output);
115+
111116
protected:
112117

113118
/**

cli/threadexecutor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ unsigned int ThreadExecutor::check()
213213
close(pipes[0]);
214214
mWpipe = pipes[1];
215215

216-
CppCheck fileChecker(*this, false);
216+
CppCheck fileChecker(*this, false, CppCheckExecutor::executeCommand);
217217
fileChecker.settings() = mSettings;
218218
unsigned int resultOfCheck = 0;
219219

gui/checkthread.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,35 @@
3232
#include "cppcheck.h"
3333
#include "common.h"
3434

35+
static bool executeCommand(std::string exe, std::vector<std::string> args, std::string redirect, std::string *output)
36+
{
37+
output->clear();
38+
39+
QStringList args2;
40+
for (std::string arg: args)
41+
args2 << QString::fromStdString(arg);
42+
43+
QProcess process;
44+
process.start(QString::fromStdString(exe), args2);
45+
process.waitForFinished();
46+
47+
if (redirect == "2>&1")
48+
*output = process.readAll().toStdString();
49+
else
50+
*output = process.readAllStandardOutput().toStdString();
51+
52+
if (redirect.compare(0,3,"2> ") == 0) {
53+
std::ofstream fout(redirect.substr(3));
54+
fout << process.readAllStandardError().toStdString();
55+
}
56+
return true;
57+
}
58+
59+
3560
CheckThread::CheckThread(ThreadResult &result) :
3661
mState(Ready),
3762
mResult(result),
38-
mCppcheck(result, true),
63+
mCppcheck(result, true, executeCommand),
3964
mAnalyseWholeProgram(false)
4065
{
4166
//ctor

gui/mainwindow.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ void MainWindow::analyzeCode(const QString& code, const QString& filename)
536536
mUI.mResults, SLOT(debugError(const ErrorItem &)));
537537

538538
// Create CppCheck instance
539-
CppCheck cppcheck(result, true);
539+
CppCheck cppcheck(result, true, nullptr);
540540
cppcheck.settings() = getCppcheckSettings();
541541

542542
// Check

gui/newsuppressiondialog.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ NewSuppressionDialog::NewSuppressionDialog(QWidget *parent) :
2121
};
2222

2323
QErrorLogger errorLogger;
24-
CppCheck cppcheck(errorLogger,false);
24+
CppCheck cppcheck(errorLogger, false, nullptr);
2525
cppcheck.getErrorMessages();
2626
errorLogger.errorIds.sort();
2727

lib/cppcheck.cpp

Lines changed: 66 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -164,32 +164,35 @@ static std::string cmdFileName(std::string f)
164164
return f;
165165
}
166166

167-
static bool executeShellCommand(std::string exe, const std::string &args, std::string *output)
167+
static std::vector<std::string> split(const std::string &str, const std::string &sep=" ")
168168
{
169-
output->clear();
170-
171-
#ifdef _WIN32
172-
// Extra quoutes are needed in windows if filename has space
173-
if (exe.find(" ") != std::string::npos)
174-
exe = "\"" + exe + "\"";
175-
const std::string cmd = exe + " " + args + " 2>&1";
176-
std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(cmd.c_str(), "r"), _pclose);
177-
#else
178-
const std::string cmd = exe + " " + args + " 2>&1";
179-
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
180-
#endif
181-
if (!pipe)
182-
return false;
183-
char buffer[1024];
184-
while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr)
185-
*output += buffer;
186-
return true;
169+
std::vector<std::string> ret;
170+
for (std::string::size_type startPos = 0U; startPos < str.size();) {
171+
std::string::size_type endPos;
172+
if (str[startPos] == '\"') {
173+
endPos = str.find("\"", startPos + 1);
174+
if (endPos < str.size())
175+
endPos++;
176+
} else {
177+
endPos = str.find(sep, startPos + 1);
178+
}
179+
if (endPos == std::string::npos) {
180+
ret.push_back(str.substr(startPos));
181+
break;
182+
}
183+
ret.push_back(str.substr(startPos, endPos - startPos));
184+
startPos = str.find_first_not_of(sep, endPos);
185+
}
186+
return ret;
187187
}
188188

189189
static std::string executeAddon(const AddonInfo &addonInfo,
190190
const std::string &defaultPythonExe,
191-
const std::string &dumpFile)
191+
const std::string &dumpFile,
192+
std::function<bool(std::string,std::vector<std::string>,std::string,std::string*)> executeCommand)
192193
{
194+
const std::string redirect = "2>&1";
195+
193196
std::string pythonExe;
194197

195198
if (!addonInfo.python.empty())
@@ -204,7 +207,7 @@ static std::string executeAddon(const AddonInfo &addonInfo,
204207
#endif
205208
for (int i = 0; i < 2; ++i) {
206209
std::string out;
207-
if (executeShellCommand(p[i], "--version", &out) && out.compare(0, 7, "Python ") == 0 && std::isdigit(out[7])) {
210+
if (executeCommand(p[i], split("--version"), redirect, &out) && out.compare(0, 7, "Python ") == 0 && std::isdigit(out[7])) {
208211
pythonExe = p[i];
209212
break;
210213
}
@@ -215,7 +218,7 @@ static std::string executeAddon(const AddonInfo &addonInfo,
215218

216219
const std::string args = cmdFileName(addonInfo.scriptFile) + " --cli" + addonInfo.args + " " + cmdFileName(dumpFile);
217220
std::string result;
218-
if (!executeShellCommand(pythonExe, args, &result))
221+
if (!executeCommand(pythonExe, split(args), redirect, &result))
219222
throw InternalError(nullptr, "Failed to execute addon (command: '" + pythonExe + " " + args + "')");
220223

221224
// Validate output..
@@ -230,48 +233,16 @@ static std::string executeAddon(const AddonInfo &addonInfo,
230233
return result;
231234
}
232235

233-
static std::vector<std::string> split(const std::string &str, const std::string &sep)
234-
{
235-
std::vector<std::string> ret;
236-
for (std::string::size_type startPos = 0U; startPos < str.size();) {
237-
std::string::size_type endPos;
238-
if (str[startPos] == '\"') {
239-
endPos = str.find("\"", startPos + 1);
240-
if (endPos < str.size())
241-
endPos++;
242-
} else {
243-
endPos = str.find(sep, startPos + 1);
244-
}
245-
if (endPos == std::string::npos) {
246-
ret.push_back(str.substr(startPos));
247-
break;
248-
}
249-
ret.push_back(str.substr(startPos, endPos - startPos));
250-
startPos = str.find_first_not_of(sep, endPos);
251-
}
252-
return ret;
253-
}
254-
255-
static std::pair<bool,std::string> executeCommand(const std::string &cmd)
256-
{
257-
#ifdef _WIN32
258-
std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(cmd.c_str(), "r"), _pclose);
259-
#else
260-
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
261-
#endif
262-
263-
if (!pipe)
264-
return std::pair<bool, std::string>(false, "");
265-
266-
char buffer[1024];
267-
std::string result;
268-
while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr)
269-
result += buffer;
270-
return std::pair<bool, std::string>(true, result);
271-
}
272-
273-
CppCheck::CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions)
274-
: mErrorLogger(errorLogger), mExitCode(0), mSuppressInternalErrorFound(false), mUseGlobalSuppressions(useGlobalSuppressions), mTooManyConfigs(false), mSimplify(true)
236+
CppCheck::CppCheck(ErrorLogger &errorLogger,
237+
bool useGlobalSuppressions,
238+
std::function<bool(std::string,std::vector<std::string>,std::string,std::string*)> executeCommand)
239+
: mErrorLogger(errorLogger)
240+
, mExitCode(0)
241+
, mSuppressInternalErrorFound(false)
242+
, mUseGlobalSuppressions(useGlobalSuppressions)
243+
, mTooManyConfigs(false)
244+
, mSimplify(true)
245+
, mExecuteCommand(executeCommand)
275246
{
276247
}
277248

@@ -349,14 +320,18 @@ unsigned int CppCheck::check(const std::string &path)
349320
const std::string analyzerInfo = mSettings.buildDir.empty() ? std::string() : AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, path, "");
350321
const std::string clangcmd = analyzerInfo + ".clang-cmd";
351322
const std::string clangStderr = analyzerInfo + ".clang-stderr";
352-
353-
const std::string cmd1 = "clang -v -fsyntax-only " + lang + " " + tempFile + " 2>&1";
354-
const std::pair<bool, std::string> &result1 = executeCommand(cmd1);
355-
if (!result1.first || result1.second.find(" -cc1 ") == std::string::npos) {
356-
mErrorLogger.reportOut("Failed to execute '" + cmd1 + "':" + result1.second);
323+
#ifdef _WIN32
324+
const std::string exe = "clang.exe";
325+
#else
326+
const std::string exe = "clang";
327+
#endif
328+
const std::string args1 = "-v -fsyntax-only " + lang + " " + tempFile;
329+
std::string output1;
330+
if (!mExecuteCommand(exe, split(args1), "2>&1", &output1) || output1.find(" -cc1 ") == std::string::npos) {
331+
mErrorLogger.reportOut("Failed to execute '" + exe + "':" + output1);
357332
return 0;
358333
}
359-
std::istringstream details(result1.second);
334+
std::istringstream details(output1);
360335
std::string line;
361336
std::string flags(lang + " ");
362337
while (std::getline(details, line)) {
@@ -374,15 +349,16 @@ unsigned int CppCheck::check(const std::string &path)
374349
for (const std::string &i: mSettings.includePaths)
375350
flags += "-I" + i + " ";
376351

377-
const std::string cmd = "clang -cc1 -ast-dump " + flags + path + (analyzerInfo.empty() ? std::string(" 2>&1") : (" 2> " + clangStderr));
352+
const std::string args2 = "-cc1 -ast-dump " + flags + path;
353+
const std::string redirect2 = analyzerInfo.empty() ? std::string("2>&1") : ("2> " + clangStderr);
378354
if (!mSettings.buildDir.empty()) {
379355
std::ofstream fout(clangcmd);
380-
fout << cmd << std::endl;
356+
fout << exe << " " << args2 << " " << redirect2 << std::endl;
381357
}
382358

383-
const std::pair<bool, std::string> &result2 = executeCommand(cmd);
384-
if (!result2.first || result2.second.find("TranslationUnitDecl") == std::string::npos) {
385-
std::cerr << "Failed to execute '" + cmd + "'" << std::endl;
359+
std::string output2;
360+
if (!mExecuteCommand(exe,split(args2),redirect2,&output2) || output2.find("TranslationUnitDecl") == std::string::npos) {
361+
std::cerr << "Failed to execute '" << exe << " " << args2 << " " << redirect2 << "'" << std::endl;
386362
return 0;
387363
}
388364

@@ -395,7 +371,7 @@ unsigned int CppCheck::check(const std::string &path)
395371
if (reportClangErrors(fin, reportError))
396372
return 0;
397373
} else {
398-
std::istringstream istr(result2.second);
374+
std::istringstream istr(output2);
399375
auto reportError = [this](const ErrorLogger::ErrorMessage& errorMessage) {
400376
reportErr(errorMessage);
401377
};
@@ -404,7 +380,7 @@ unsigned int CppCheck::check(const std::string &path)
404380
}
405381

406382
//std::cout << "Checking Clang ast dump:\n" << result2.second << std::endl;
407-
std::istringstream ast(result2.second);
383+
std::istringstream ast(output2);
408384
Tokenizer tokenizer(&mSettings, this);
409385
tokenizer.list.appendFileIfNew(path);
410386
clangimport::parseClangAstDump(&tokenizer, ast);
@@ -427,7 +403,7 @@ unsigned int CppCheck::check(const std::string &path, const std::string &content
427403

428404
unsigned int CppCheck::check(const ImportProject::FileSettings &fs)
429405
{
430-
CppCheck temp(mErrorLogger, mUseGlobalSuppressions);
406+
CppCheck temp(mErrorLogger, mUseGlobalSuppressions, mExecuteCommand);
431407
temp.mSettings = mSettings;
432408
if (!temp.mSettings.userDefines.empty())
433409
temp.mSettings.userDefines += ';';
@@ -849,7 +825,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
849825
continue;
850826
}
851827
const std::string results =
852-
executeAddon(addonInfo, mSettings.addonPython, dumpFile);
828+
executeAddon(addonInfo, mSettings.addonPython, dumpFile, mExecuteCommand);
853829
std::istringstream istr(results);
854830
std::string line;
855831

@@ -1441,15 +1417,21 @@ void CppCheck::analyseClangTidy(const ImportProject::FileSettings &fileSettings)
14411417
pos += 3;
14421418
}
14431419

1444-
const std::string cmd = "clang-tidy -quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename + "\" -- " + allIncludes + allDefines;
1445-
std::pair<bool, std::string> result = executeCommand(cmd);
1446-
if (!result.first) {
1447-
std::cerr << "Failed to execute '" + cmd + "'" << std::endl;
1420+
#ifdef _WIN32
1421+
const char exe[] = "clang-tidy.exe";
1422+
#else
1423+
const char exe[] = "clang-tidy";
1424+
#endif
1425+
1426+
const std::string args = "-quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename + "\" -- " + allIncludes + allDefines;
1427+
std::string output;
1428+
if (!mExecuteCommand(exe, split(args), "", &output)) {
1429+
std::cerr << "Failed to execute '" << exe << "'" << std::endl;
14481430
return;
14491431
}
14501432

14511433
// parse output and create error messages
1452-
std::istringstream istr(result.second);
1434+
std::istringstream istr(output);
14531435
std::string line;
14541436

14551437
if (!mSettings.buildDir.empty()) {

lib/cppcheck.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ class CPPCHECKLIB CppCheck : ErrorLogger {
5050
/**
5151
* @brief Constructor.
5252
*/
53-
CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions);
53+
CppCheck(ErrorLogger &errorLogger,
54+
bool useGlobalSuppressions,
55+
std::function<bool(std::string,std::vector<std::string>,std::string,std::string*)> executeCommand);
5456

5557
/**
5658
* @brief Destructor.
@@ -231,6 +233,9 @@ class CPPCHECKLIB CppCheck : ErrorLogger {
231233
std::list<Check::FileInfo*> mFileInfo;
232234

233235
AnalyzerInformation mAnalyzerInformation;
236+
237+
/** Callback for executing a shell command (exe, args, output) */
238+
std::function<bool(std::string,std::vector<std::string>,std::string,std::string*)> mExecuteCommand;
234239
};
235240

236241
/// @}

0 commit comments

Comments
 (0)