executeCommand in CppCheckExecutor or QCheckThread

This commit is contained in:
Daniel Marjamäki 2020-05-19 16:04:25 +02:00
parent baca1fbe04
commit 32e569704b
11 changed files with 160 additions and 110 deletions

View File

@ -496,7 +496,7 @@ $(libcppdir)/ctu.o: lib/ctu.cpp externals/tinyxml/tinyxml2.h lib/astutils.h lib/
$(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 $(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
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/errorlogger.o $(libcppdir)/errorlogger.cpp $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/errorlogger.o $(libcppdir)/errorlogger.cpp
$(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 $(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
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/exprengine.o $(libcppdir)/exprengine.cpp $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/exprengine.o $(libcppdir)/exprengine.cpp
$(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 $(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

View File

@ -209,7 +209,7 @@ int CppCheckExecutor::check(int argc, const char* const argv[])
CheckUnusedFunctions::clear(); CheckUnusedFunctions::clear();
CppCheck cppCheck(*this, true); CppCheck cppCheck(*this, true, executeCommand);
const Settings& settings = cppCheck.settings(); const Settings& settings = cppCheck.settings();
mSettings = &settings; mSettings = &settings;
@ -1162,3 +1162,36 @@ bool CppCheckExecutor::tryLoadLibrary(Library& destination, const char* basepath
} }
return true; return true;
} }
/**
* Execute a shell command and read the output from it. Returns true if command terminated successfully.
*/
bool CppCheckExecutor::executeCommand(std::string exe, std::vector<std::string> args, std::string redirect, std::string *output)
{
output->clear();
std::string joinedArgs;
for (const std::string arg: args) {
if (!joinedArgs.empty())
joinedArgs += " ";
joinedArgs += arg;
}
#ifdef _WIN32
// Extra quoutes are needed in windows if filename has space
if (exe.find(" ") != std::string::npos)
exe = "\"" + exe + "\"";
const std::string cmd = exe + " " + joinedArgs + " " + redirect;
std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(cmd.c_str(), "r"), _pclose);
#else
const std::string cmd = exe + " " + joinedArgs + " " + redirect;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
#endif
if (!pipe)
return false;
char buffer[1024];
while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr)
*output += buffer;
return true;
}

View File

@ -108,6 +108,11 @@ public:
*/ */
static bool tryLoadLibrary(Library& destination, const char* basepath, const char* filename); static bool tryLoadLibrary(Library& destination, const char* basepath, const char* filename);
/**
* Execute a shell command and read the output from it. Returns true if command terminated successfully.
*/
static bool executeCommand(std::string exe, std::vector<std::string> args, std::string redirect, std::string *output);
protected: protected:
/** /**

View File

@ -213,7 +213,7 @@ unsigned int ThreadExecutor::check()
close(pipes[0]); close(pipes[0]);
mWpipe = pipes[1]; mWpipe = pipes[1];
CppCheck fileChecker(*this, false); CppCheck fileChecker(*this, false, CppCheckExecutor::executeCommand);
fileChecker.settings() = mSettings; fileChecker.settings() = mSettings;
unsigned int resultOfCheck = 0; unsigned int resultOfCheck = 0;

View File

@ -32,10 +32,35 @@
#include "cppcheck.h" #include "cppcheck.h"
#include "common.h" #include "common.h"
static bool executeCommand(std::string exe, std::vector<std::string> args, std::string redirect, std::string *output)
{
output->clear();
QStringList args2;
for (std::string arg: args)
args2 << QString::fromStdString(arg);
QProcess process;
process.start(QString::fromStdString(exe), args2);
process.waitForFinished();
if (redirect == "2>&1")
*output = process.readAll().toStdString();
else
*output = process.readAllStandardOutput().toStdString();
if (redirect.compare(0,3,"2> ") == 0) {
std::ofstream fout(redirect.substr(3));
fout << process.readAllStandardError().toStdString();
}
return true;
}
CheckThread::CheckThread(ThreadResult &result) : CheckThread::CheckThread(ThreadResult &result) :
mState(Ready), mState(Ready),
mResult(result), mResult(result),
mCppcheck(result, true), mCppcheck(result, true, executeCommand),
mAnalyseWholeProgram(false) mAnalyseWholeProgram(false)
{ {
//ctor //ctor

View File

@ -536,7 +536,7 @@ void MainWindow::analyzeCode(const QString& code, const QString& filename)
mUI.mResults, SLOT(debugError(const ErrorItem &))); mUI.mResults, SLOT(debugError(const ErrorItem &)));
// Create CppCheck instance // Create CppCheck instance
CppCheck cppcheck(result, true); CppCheck cppcheck(result, true, nullptr);
cppcheck.settings() = getCppcheckSettings(); cppcheck.settings() = getCppcheckSettings();
// Check // Check

View File

@ -21,7 +21,7 @@ NewSuppressionDialog::NewSuppressionDialog(QWidget *parent) :
}; };
QErrorLogger errorLogger; QErrorLogger errorLogger;
CppCheck cppcheck(errorLogger,false); CppCheck cppcheck(errorLogger, false, nullptr);
cppcheck.getErrorMessages(); cppcheck.getErrorMessages();
errorLogger.errorIds.sort(); errorLogger.errorIds.sort();

View File

@ -164,73 +164,7 @@ static std::string cmdFileName(std::string f)
return f; return f;
} }
static bool executeShellCommand(std::string exe, const std::string &args, std::string *output) static std::vector<std::string> split(const std::string &str, const std::string &sep=" ")
{
output->clear();
#ifdef _WIN32
// Extra quoutes are needed in windows if filename has space
if (exe.find(" ") != std::string::npos)
exe = "\"" + exe + "\"";
const std::string cmd = exe + " " + args + " 2>&1";
std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(cmd.c_str(), "r"), _pclose);
#else
const std::string cmd = exe + " " + args + " 2>&1";
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
#endif
if (!pipe)
return false;
char buffer[1024];
while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr)
*output += buffer;
return true;
}
static std::string executeAddon(const AddonInfo &addonInfo,
const std::string &defaultPythonExe,
const std::string &dumpFile)
{
std::string pythonExe;
if (!addonInfo.python.empty())
pythonExe = cmdFileName(addonInfo.python);
else if (!defaultPythonExe.empty())
pythonExe = cmdFileName(defaultPythonExe);
else {
#ifdef _WIN32
const char *p[] = { "python3.exe", "python.exe" };
#else
const char *p[] = { "python3", "python" };
#endif
for (int i = 0; i < 2; ++i) {
std::string out;
if (executeShellCommand(p[i], "--version", &out) && out.compare(0, 7, "Python ") == 0 && std::isdigit(out[7])) {
pythonExe = p[i];
break;
}
}
if (pythonExe.empty())
throw InternalError(nullptr, "Failed to auto detect python");
}
const std::string args = cmdFileName(addonInfo.scriptFile) + " --cli" + addonInfo.args + " " + cmdFileName(dumpFile);
std::string result;
if (!executeShellCommand(pythonExe, args, &result))
throw InternalError(nullptr, "Failed to execute addon (command: '" + pythonExe + " " + args + "')");
// Validate output..
std::istringstream istr(result);
std::string line;
while (std::getline(istr, line)) {
if (line.compare(0,9,"Checking ", 0, 9) != 0 && !line.empty() && line[0] != '{')
throw InternalError(nullptr, "Failed to execute '" + pythonExe + " " + args + "'. " + result);
}
// Valid results
return result;
}
static std::vector<std::string> split(const std::string &str, const std::string &sep)
{ {
std::vector<std::string> ret; std::vector<std::string> ret;
for (std::string::size_type startPos = 0U; startPos < str.size();) { for (std::string::size_type startPos = 0U; startPos < str.size();) {
@ -252,26 +186,63 @@ static std::vector<std::string> split(const std::string &str, const std::string
return ret; return ret;
} }
static std::pair<bool,std::string> executeCommand(const std::string &cmd) static std::string executeAddon(const AddonInfo &addonInfo,
const std::string &defaultPythonExe,
const std::string &dumpFile,
std::function<bool(std::string,std::vector<std::string>,std::string,std::string*)> executeCommand)
{ {
const std::string redirect = "2>&1";
std::string pythonExe;
if (!addonInfo.python.empty())
pythonExe = cmdFileName(addonInfo.python);
else if (!defaultPythonExe.empty())
pythonExe = cmdFileName(defaultPythonExe);
else {
#ifdef _WIN32 #ifdef _WIN32
std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(cmd.c_str(), "r"), _pclose); const char *p[] = { "python3.exe", "python.exe" };
#else #else
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose); const char *p[] = { "python3", "python" };
#endif #endif
for (int i = 0; i < 2; ++i) {
std::string out;
if (executeCommand(p[i], split("--version"), redirect, &out) && out.compare(0, 7, "Python ") == 0 && std::isdigit(out[7])) {
pythonExe = p[i];
break;
}
}
if (pythonExe.empty())
throw InternalError(nullptr, "Failed to auto detect python");
}
if (!pipe) const std::string args = cmdFileName(addonInfo.scriptFile) + " --cli" + addonInfo.args + " " + cmdFileName(dumpFile);
return std::pair<bool, std::string>(false, "");
char buffer[1024];
std::string result; std::string result;
while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr) if (!executeCommand(pythonExe, split(args), redirect, &result))
result += buffer; throw InternalError(nullptr, "Failed to execute addon (command: '" + pythonExe + " " + args + "')");
return std::pair<bool, std::string>(true, result);
// Validate output..
std::istringstream istr(result);
std::string line;
while (std::getline(istr, line)) {
if (line.compare(0,9,"Checking ", 0, 9) != 0 && !line.empty() && line[0] != '{')
throw InternalError(nullptr, "Failed to execute '" + pythonExe + " " + args + "'. " + result);
}
// Valid results
return result;
} }
CppCheck::CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions) CppCheck::CppCheck(ErrorLogger &errorLogger,
: mErrorLogger(errorLogger), mExitCode(0), mSuppressInternalErrorFound(false), mUseGlobalSuppressions(useGlobalSuppressions), mTooManyConfigs(false), mSimplify(true) bool useGlobalSuppressions,
std::function<bool(std::string,std::vector<std::string>,std::string,std::string*)> executeCommand)
: mErrorLogger(errorLogger)
, mExitCode(0)
, mSuppressInternalErrorFound(false)
, mUseGlobalSuppressions(useGlobalSuppressions)
, mTooManyConfigs(false)
, mSimplify(true)
, mExecuteCommand(executeCommand)
{ {
} }
@ -349,14 +320,18 @@ unsigned int CppCheck::check(const std::string &path)
const std::string analyzerInfo = mSettings.buildDir.empty() ? std::string() : AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, path, ""); const std::string analyzerInfo = mSettings.buildDir.empty() ? std::string() : AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, path, "");
const std::string clangcmd = analyzerInfo + ".clang-cmd"; const std::string clangcmd = analyzerInfo + ".clang-cmd";
const std::string clangStderr = analyzerInfo + ".clang-stderr"; const std::string clangStderr = analyzerInfo + ".clang-stderr";
#ifdef _WIN32
const std::string cmd1 = "clang -v -fsyntax-only " + lang + " " + tempFile + " 2>&1"; const std::string exe = "clang.exe";
const std::pair<bool, std::string> &result1 = executeCommand(cmd1); #else
if (!result1.first || result1.second.find(" -cc1 ") == std::string::npos) { const std::string exe = "clang";
mErrorLogger.reportOut("Failed to execute '" + cmd1 + "':" + result1.second); #endif
const std::string args1 = "-v -fsyntax-only " + lang + " " + tempFile;
std::string output1;
if (!mExecuteCommand(exe, split(args1), "2>&1", &output1) || output1.find(" -cc1 ") == std::string::npos) {
mErrorLogger.reportOut("Failed to execute '" + exe + "':" + output1);
return 0; return 0;
} }
std::istringstream details(result1.second); std::istringstream details(output1);
std::string line; std::string line;
std::string flags(lang + " "); std::string flags(lang + " ");
while (std::getline(details, line)) { while (std::getline(details, line)) {
@ -374,15 +349,16 @@ unsigned int CppCheck::check(const std::string &path)
for (const std::string &i: mSettings.includePaths) for (const std::string &i: mSettings.includePaths)
flags += "-I" + i + " "; flags += "-I" + i + " ";
const std::string cmd = "clang -cc1 -ast-dump " + flags + path + (analyzerInfo.empty() ? std::string(" 2>&1") : (" 2> " + clangStderr)); const std::string args2 = "-cc1 -ast-dump " + flags + path;
const std::string redirect2 = analyzerInfo.empty() ? std::string("2>&1") : ("2> " + clangStderr);
if (!mSettings.buildDir.empty()) { if (!mSettings.buildDir.empty()) {
std::ofstream fout(clangcmd); std::ofstream fout(clangcmd);
fout << cmd << std::endl; fout << exe << " " << args2 << " " << redirect2 << std::endl;
} }
const std::pair<bool, std::string> &result2 = executeCommand(cmd); std::string output2;
if (!result2.first || result2.second.find("TranslationUnitDecl") == std::string::npos) { if (!mExecuteCommand(exe,split(args2),redirect2,&output2) || output2.find("TranslationUnitDecl") == std::string::npos) {
std::cerr << "Failed to execute '" + cmd + "'" << std::endl; std::cerr << "Failed to execute '" << exe << " " << args2 << " " << redirect2 << "'" << std::endl;
return 0; return 0;
} }
@ -395,7 +371,7 @@ unsigned int CppCheck::check(const std::string &path)
if (reportClangErrors(fin, reportError)) if (reportClangErrors(fin, reportError))
return 0; return 0;
} else { } else {
std::istringstream istr(result2.second); std::istringstream istr(output2);
auto reportError = [this](const ErrorLogger::ErrorMessage& errorMessage) { auto reportError = [this](const ErrorLogger::ErrorMessage& errorMessage) {
reportErr(errorMessage); reportErr(errorMessage);
}; };
@ -404,7 +380,7 @@ unsigned int CppCheck::check(const std::string &path)
} }
//std::cout << "Checking Clang ast dump:\n" << result2.second << std::endl; //std::cout << "Checking Clang ast dump:\n" << result2.second << std::endl;
std::istringstream ast(result2.second); std::istringstream ast(output2);
Tokenizer tokenizer(&mSettings, this); Tokenizer tokenizer(&mSettings, this);
tokenizer.list.appendFileIfNew(path); tokenizer.list.appendFileIfNew(path);
clangimport::parseClangAstDump(&tokenizer, ast); clangimport::parseClangAstDump(&tokenizer, ast);
@ -427,7 +403,7 @@ unsigned int CppCheck::check(const std::string &path, const std::string &content
unsigned int CppCheck::check(const ImportProject::FileSettings &fs) unsigned int CppCheck::check(const ImportProject::FileSettings &fs)
{ {
CppCheck temp(mErrorLogger, mUseGlobalSuppressions); CppCheck temp(mErrorLogger, mUseGlobalSuppressions, mExecuteCommand);
temp.mSettings = mSettings; temp.mSettings = mSettings;
if (!temp.mSettings.userDefines.empty()) if (!temp.mSettings.userDefines.empty())
temp.mSettings.userDefines += ';'; temp.mSettings.userDefines += ';';
@ -849,7 +825,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
continue; continue;
} }
const std::string results = const std::string results =
executeAddon(addonInfo, mSettings.addonPython, dumpFile); executeAddon(addonInfo, mSettings.addonPython, dumpFile, mExecuteCommand);
std::istringstream istr(results); std::istringstream istr(results);
std::string line; std::string line;
@ -1441,15 +1417,21 @@ void CppCheck::analyseClangTidy(const ImportProject::FileSettings &fileSettings)
pos += 3; pos += 3;
} }
const std::string cmd = "clang-tidy -quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename + "\" -- " + allIncludes + allDefines; #ifdef _WIN32
std::pair<bool, std::string> result = executeCommand(cmd); const char exe[] = "clang-tidy.exe";
if (!result.first) { #else
std::cerr << "Failed to execute '" + cmd + "'" << std::endl; const char exe[] = "clang-tidy";
#endif
const std::string args = "-quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename + "\" -- " + allIncludes + allDefines;
std::string output;
if (!mExecuteCommand(exe, split(args), "", &output)) {
std::cerr << "Failed to execute '" << exe << "'" << std::endl;
return; return;
} }
// parse output and create error messages // parse output and create error messages
std::istringstream istr(result.second); std::istringstream istr(output);
std::string line; std::string line;
if (!mSettings.buildDir.empty()) { if (!mSettings.buildDir.empty()) {

View File

@ -50,7 +50,9 @@ public:
/** /**
* @brief Constructor. * @brief Constructor.
*/ */
CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions); CppCheck(ErrorLogger &errorLogger,
bool useGlobalSuppressions,
std::function<bool(std::string,std::vector<std::string>,std::string,std::string*)> executeCommand);
/** /**
* @brief Destructor. * @brief Destructor.
@ -231,6 +233,9 @@ private:
std::list<Check::FileInfo*> mFileInfo; std::list<Check::FileInfo*> mFileInfo;
AnalyzerInformation mAnalyzerInformation; AnalyzerInformation mAnalyzerInformation;
/** Callback for executing a shell command (exe, args, output) */
std::function<bool(std::string,std::vector<std::string>,std::string,std::string*)> mExecuteCommand;
}; };
/// @} /// @}

View File

@ -75,7 +75,7 @@ private:
void getErrorMessages() const { void getErrorMessages() const {
ErrorLogger2 errorLogger; ErrorLogger2 errorLogger;
CppCheck cppCheck(errorLogger, true); CppCheck cppCheck(errorLogger, true, nullptr);
cppCheck.getErrorMessages(); cppCheck.getErrorMessages();
ASSERT(!errorLogger.id.empty()); ASSERT(!errorLogger.id.empty());

View File

@ -179,7 +179,7 @@ private:
// Clear the error log // Clear the error log
errout.str(""); errout.str("");
CppCheck cppCheck(*this, true); CppCheck cppCheck(*this, true, nullptr);
Settings& settings = cppCheck.settings(); Settings& settings = cppCheck.settings();
settings.exitCode = 1; settings.exitCode = 1;
settings.inlineSuppressions = true; settings.inlineSuppressions = true;
@ -630,7 +630,7 @@ private:
void globalSuppressions() { // Testing that Cppcheck::useGlobalSuppressions works (#8515) void globalSuppressions() { // Testing that Cppcheck::useGlobalSuppressions works (#8515)
errout.str(""); errout.str("");
CppCheck cppCheck(*this, false); // <- do not "use global suppressions". pretend this is a thread that just checks a file. CppCheck cppCheck(*this, false, nullptr); // <- do not "use global suppressions". pretend this is a thread that just checks a file.
Settings& settings = cppCheck.settings(); Settings& settings = cppCheck.settings();
settings.nomsg.addSuppressionLine("uninitvar"); settings.nomsg.addSuppressionLine("uninitvar");
settings.exitCode = 1; settings.exitCode = 1;
@ -662,7 +662,7 @@ private:
// Clear the error log // Clear the error log
errout.str(""); errout.str("");
CppCheck cppCheck(*this, true); CppCheck cppCheck(*this, true, nullptr);
Settings& settings = cppCheck.settings(); Settings& settings = cppCheck.settings();
settings.addEnabled("style"); settings.addEnabled("style");
settings.inlineSuppressions = true; settings.inlineSuppressions = true;