Cppcheck
cppcheckexecutor.cpp
Go to the documentation of this file.
00001 /*
00002  * Cppcheck - A tool for static C/C++ code analysis
00003  * Copyright (C) 2007-2013 Daniel Marjamäki and Cppcheck team.
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation, either version 3 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 #include "cppcheckexecutor.h"
00020 #include "cppcheck.h"
00021 #include "threadexecutor.h"
00022 #include "preprocessor.h"
00023 #include "errorlogger.h"
00024 #include <iostream>
00025 #include <sstream>
00026 #include <cstdlib> // EXIT_SUCCESS and EXIT_FAILURE
00027 #include <cstring>
00028 #include <algorithm>
00029 #include <climits>
00030 
00031 #include "cmdlineparser.h"
00032 #include "filelister.h"
00033 #include "path.h"
00034 #include "pathmatch.h"
00035 
00036 CppCheckExecutor::CppCheckExecutor()
00037     : _settings(0), time1(0), errorlist(false)
00038 {
00039 }
00040 
00041 CppCheckExecutor::~CppCheckExecutor()
00042 {
00043 }
00044 
00045 bool CppCheckExecutor::parseFromArgs(CppCheck *cppcheck, int argc, const char* const argv[])
00046 {
00047     Settings& settings = cppcheck->settings();
00048     CmdLineParser parser(&settings);
00049     bool success = parser.ParseFromArgs(argc, argv);
00050 
00051     if (success) {
00052         if (parser.GetShowVersion() && !parser.GetShowErrorMessages()) {
00053             const char * extraVersion = cppcheck->extraVersion();
00054             if (*extraVersion != 0)
00055                 std::cout << "Cppcheck " << cppcheck->version() << " ("
00056                           << extraVersion << ')' << std::endl;
00057             else
00058                 std::cout << "Cppcheck " << cppcheck->version() << std::endl;
00059         }
00060 
00061         if (parser.GetShowErrorMessages()) {
00062             errorlist = true;
00063             std::cout << ErrorLogger::ErrorMessage::getXMLHeader(settings._xml_version);
00064             cppcheck->getErrorMessages();
00065             std::cout << ErrorLogger::ErrorMessage::getXMLFooter(settings._xml_version) << std::endl;
00066         }
00067 
00068         if (parser.ExitAfterPrinting())
00069             std::exit(EXIT_SUCCESS);
00070     } else {
00071         std::exit(EXIT_FAILURE);
00072     }
00073 
00074     // Check that all include paths exist
00075     {
00076         std::list<std::string>::iterator iter;
00077         for (iter = settings._includePaths.begin();
00078              iter != settings._includePaths.end();
00079             ) {
00080             const std::string path(Path::toNativeSeparators(*iter));
00081             if (FileLister::isDirectory(path))
00082                 ++iter;
00083             else {
00084                 // If the include path is not found, warn user and remove the
00085                 // non-existing path from the list.
00086                 std::cout << "cppcheck: warning: Couldn't find path given by -I '" << path << '\'' << std::endl;
00087                 iter = settings._includePaths.erase(iter);
00088             }
00089         }
00090     }
00091 
00092     const std::vector<std::string>& pathnames = parser.GetPathNames();
00093 
00094     if (!pathnames.empty()) {
00095         // Execute recursiveAddFiles() to each given file parameter
00096         std::vector<std::string>::const_iterator iter;
00097         for (iter = pathnames.begin(); iter != pathnames.end(); ++iter)
00098             FileLister::recursiveAddFiles(_files, Path::toNativeSeparators(*iter));
00099     }
00100 
00101     if (!_files.empty()) {
00102         // Remove header files from the list of ignored files.
00103         // Also output a warning for the user.
00104         // TODO: Remove all unknown files? (use FileLister::acceptFile())
00105         bool warn = false;
00106         std::vector<std::string> ignored = parser.GetIgnoredPaths();
00107         for (std::vector<std::string>::iterator i = ignored.begin(); i != ignored.end();) {
00108             const std::string extension = Path::getFilenameExtension(*i);
00109             if (extension == ".h" || extension == ".hpp") {
00110                 i = ignored.erase(i);
00111                 warn = true;
00112             } else
00113                 ++i;
00114         }
00115         if (warn) {
00116             std::cout << "cppcheck: filename exclusion does not apply to header (.h and .hpp) files." << std::endl;
00117             std::cout << "cppcheck: Please use --suppress for ignoring results from the header files." << std::endl;
00118         }
00119 
00120 #if defined(_WIN32)
00121         // For Windows we want case-insensitive path matching
00122         const bool caseSensitive = false;
00123 #else
00124         const bool caseSensitive = true;
00125 #endif
00126         PathMatch matcher(parser.GetIgnoredPaths(), caseSensitive);
00127         for (std::map<std::string, std::size_t>::iterator i = _files.begin(); i != _files.end();) {
00128             if (matcher.Match(i->first))
00129                 _files.erase(i++);
00130             else
00131                 ++i;
00132         }
00133     } else {
00134         std::cout << "cppcheck: error: could not find or open any of the paths given." << std::endl;
00135         return false;
00136     }
00137 
00138     if (!_files.empty()) {
00139         return true;
00140     } else {
00141         std::cout << "cppcheck: error: no files to check - all paths ignored." << std::endl;
00142         return false;
00143     }
00144 }
00145 
00146 int CppCheckExecutor::check(int argc, const char* const argv[])
00147 {
00148     Preprocessor::missingIncludeFlag = false;
00149 
00150     CppCheck cppCheck(*this, true);
00151 
00152     Settings& settings = cppCheck.settings();
00153     _settings = &settings;
00154 
00155     if (!parseFromArgs(&cppCheck, argc, argv)) {
00156         return EXIT_FAILURE;
00157     }
00158 
00159     if (settings.reportProgress)
00160         time1 = std::time(0);
00161 
00162     if (settings._xml) {
00163         reportErr(ErrorLogger::ErrorMessage::getXMLHeader(settings._xml_version));
00164     }
00165 
00166     unsigned int returnValue = 0;
00167     if (settings._jobs == 1) {
00168         // Single process
00169 
00170         std::size_t totalfilesize = 0;
00171         for (std::map<std::string, std::size_t>::const_iterator i = _files.begin(); i != _files.end(); ++i) {
00172             totalfilesize += i->second;
00173         }
00174 
00175         std::size_t processedsize = 0;
00176         unsigned int c = 0;
00177         for (std::map<std::string, std::size_t>::const_iterator i = _files.begin(); i != _files.end(); ++i) {
00178             returnValue += cppCheck.check(i->first);
00179             processedsize += i->second;
00180             if (!settings._errorsOnly)
00181                 reportStatus(c + 1, _files.size(), processedsize, totalfilesize);
00182             c++;
00183         }
00184 
00185         cppCheck.checkFunctionUsage();
00186     } else if (!ThreadExecutor::isEnabled()) {
00187         std::cout << "No thread support yet implemented for this platform." << std::endl;
00188     } else {
00189         // Multiple processes
00190         ThreadExecutor executor(_files, settings, *this);
00191         returnValue = executor.check();
00192     }
00193 
00194     if (!settings.checkConfiguration) {
00195         if (!settings._errorsOnly)
00196             reportUnmatchedSuppressions(settings.nomsg.getUnmatchedGlobalSuppressions());
00197 
00198         cppCheck.tooManyConfigsError("",0U);
00199 
00200         if (settings.isEnabled("missingInclude") && Preprocessor::missingIncludeFlag) {
00201             const std::list<ErrorLogger::ErrorMessage::FileLocation> callStack;
00202             ErrorLogger::ErrorMessage msg(callStack,
00203                                           Severity::information,
00204                                           "Cppcheck cannot find all the include files (use --check-config for details)\n"
00205                                           "Cppcheck cannot find all the include files. Cppcheck can check the code without the "
00206                                           "include files found. But the results will probably be more accurate if all the include "
00207                                           "files are found. Please check your project's include directories and add all of them "
00208                                           "as include directories for Cppcheck. To see what files Cppcheck cannot find use "
00209                                           "--check-config.",
00210                                           "missingInclude",
00211                                           false);
00212             reportInfo(msg);
00213         }
00214     }
00215 
00216     if (settings._xml) {
00217         reportErr(ErrorLogger::ErrorMessage::getXMLFooter(settings._xml_version));
00218     }
00219 
00220     _settings = 0;
00221     if (returnValue)
00222         return settings._exitCode;
00223     else
00224         return 0;
00225 }
00226 
00227 void CppCheckExecutor::reportErr(const std::string &errmsg)
00228 {
00229     // Alert only about unique errors
00230     if (_errorList.find(errmsg) != _errorList.end())
00231         return;
00232 
00233     _errorList.insert(errmsg);
00234     std::cerr << errmsg << std::endl;
00235 }
00236 
00237 void CppCheckExecutor::reportOut(const std::string &outmsg)
00238 {
00239     std::cout << outmsg << std::endl;
00240 }
00241 
00242 void CppCheckExecutor::reportProgress(const std::string &filename, const char stage[], const std::size_t value)
00243 {
00244     (void)filename;
00245 
00246     if (!time1)
00247         return;
00248 
00249     // Report progress messages every 10 seconds
00250     const std::time_t time2 = std::time(NULL);
00251     if (time2 >= (time1 + 10)) {
00252         time1 = time2;
00253 
00254         // format a progress message
00255         std::ostringstream ostr;
00256         ostr << "progress: "
00257              << stage
00258              << ' ' << value << '%';
00259 
00260         // Report progress message
00261         reportOut(ostr.str());
00262     }
00263 }
00264 
00265 void CppCheckExecutor::reportInfo(const ErrorLogger::ErrorMessage &msg)
00266 {
00267     reportErr(msg);
00268 }
00269 
00270 void CppCheckExecutor::reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal)
00271 {
00272     if (filecount > 1) {
00273         std::ostringstream oss;
00274         oss << fileindex << '/' << filecount
00275             << " files checked " <<
00276             (sizetotal > 0 ? static_cast<long>(static_cast<long double>(sizedone) / sizetotal*100) : 0)
00277             << "% done";
00278         std::cout << oss.str() << std::endl;
00279     }
00280 }
00281 
00282 void CppCheckExecutor::reportErr(const ErrorLogger::ErrorMessage &msg)
00283 {
00284     if (errorlist) {
00285         reportOut(msg.toXML(false, _settings->_xml_version));
00286     } else if (_settings->_xml) {
00287         reportErr(msg.toXML(_settings->_verbose, _settings->_xml_version));
00288     } else {
00289         reportErr(msg.toString(_settings->_verbose, _settings->_outputFormat));
00290     }
00291 }