diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 0bc0052f576..dd13d1ef7f3 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -245,6 +245,9 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[]) if (mSettings.library.markupFile(fs.filename())) { fs.file.setLang(Standards::Language::C); } + std::string error = fs.updateFileSize(); + if (!error.empty()) + mLogger.printError(error); } // sort the markup last @@ -350,6 +353,9 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[]) if (mSettings.library.markupFile(f.path())) { f.setLang(Standards::Language::C); } + std::string error = f.updateSize(); + if (!error.empty()) + mLogger.printError(error); } // sort the markup last diff --git a/cli/filelister.cpp b/cli/filelister.cpp index 841f84ae89e..12b3c97a12f 100644 --- a/cli/filelister.cpp +++ b/cli/filelister.cpp @@ -232,11 +232,11 @@ static std::string addFiles2(std::list &files, } else { Standards::Language lang = Standards::Language::None; if (Path::acceptFile(new_path, extra, &lang) && !ignored.match(new_path)) { - if (stat(new_path.c_str(), &file_stat) == -1) { - const int err = errno; - return "could not stat file '" + new_path + "' (errno: " + std::to_string(err) + ")"; + files.emplace_back(new_path, lang); + std::string err = files.back().updateSize(); + if (!err.empty()) { + return err; } - files.emplace_back(new_path, lang, file_stat.st_size); } } } diff --git a/cli/processexecutor.cpp b/cli/processexecutor.cpp index 2ca13fa9e4e..5dd11c59a0b 100644 --- a/cli/processexecutor.cpp +++ b/cli/processexecutor.cpp @@ -239,13 +239,15 @@ unsigned int ProcessExecutor::check() unsigned int fileCount = 0; unsigned int result = 0; - const std::size_t totalfilesize = std::accumulate(mFiles.cbegin(), mFiles.cend(), std::size_t(0), [](std::size_t v, const FileWithDetails& p) { - return v + p.size(); + const std::size_t totalfilesize = std::accumulate(mFiles.cbegin(), mFiles.cend(), std::size_t(0), [](std::size_t v, const FileWithDetails& fwd) { + return v + fwd.size(); + }) + std::accumulate(mFileSettings.cbegin(), mFileSettings.cend(), std::size_t(0), [](std::size_t v, const FileSettings& fs) { + return v + fs.filesize(); }); std::list rpipes; std::map childFile; - std::map pipeFile; + std::map> pipeFile; std::size_t processedsize = 0; auto iFile = mFiles.cbegin(); auto iFileSettings = mFileSettings.cbegin(); @@ -304,11 +306,11 @@ unsigned int ProcessExecutor::check() rpipes.push_back(pipes[0]); if (iFileSettings != mFileSettings.end()) { childFile[pid] = iFileSettings->filename() + ' ' + iFileSettings->cfg; - pipeFile[pipes[0]] = iFileSettings->filename() + ' ' + iFileSettings->cfg; + pipeFile[pipes[0]] = { iFileSettings->filename() + ' ' + iFileSettings->cfg, iFileSettings->filesize() }; ++iFileSettings; } else { childFile[pid] = iFile->path(); - pipeFile[pipes[0]] = iFile->path(); + pipeFile[pipes[0]] = { iFile->path(), iFile->size() }; ++iFile; } } @@ -327,21 +329,16 @@ unsigned int ProcessExecutor::check() while (rp != rpipes.cend()) { if (FD_ISSET(*rp, &rfds)) { std::string name; - const auto p = utils::as_const(pipeFile).find(*rp); + std::size_t size = 0; + const std::map>::const_iterator p = pipeFile.find(*rp); if (p != pipeFile.cend()) { - name = p->second; + name = p->second.first; + size = p->second.second; } const bool readRes = handleRead(*rp, result, name); if (!readRes) { - std::size_t size = 0; if (p != pipeFile.cend()) { pipeFile.erase(p); - const auto fs = std::find_if(mFiles.cbegin(), mFiles.cend(), [&name](const FileWithDetails& entry) { - return entry.path() == name; - }); - if (fs != mFiles.end()) { - size = fs->size(); - } } fileCount++; diff --git a/cli/singleexecutor.cpp b/cli/singleexecutor.cpp index 216fe1fec48..59a098a97bf 100644 --- a/cli/singleexecutor.cpp +++ b/cli/singleexecutor.cpp @@ -44,6 +44,8 @@ unsigned int SingleExecutor::check() const std::size_t totalfilesize = std::accumulate(mFiles.cbegin(), mFiles.cend(), std::size_t(0), [](std::size_t v, const FileWithDetails& f) { return v + f.size(); + }) + std::accumulate(mFileSettings.cbegin(), mFileSettings.cend(), std::size_t(0), [](std::size_t v, const FileSettings& fs) { + return v + fs.filesize(); }); std::size_t processedsize = 0; @@ -62,9 +64,10 @@ unsigned int SingleExecutor::check() // check all files of the project for (const FileSettings &fs : mFileSettings) { result += mCppcheck.check(fs); + processedsize += fs.filesize(); ++c; if (!mSettings.quiet) - reportStatus(c, mFileSettings.size(), c, mFileSettings.size()); + reportStatus(c, mFileSettings.size(), processedsize, totalfilesize); if (mSettings.clangTidy) mCppcheck.analyseClangTidy(fs); } diff --git a/cli/threadexecutor.cpp b/cli/threadexecutor.cpp index a7758314cf7..3b66d295fa6 100644 --- a/cli/threadexecutor.cpp +++ b/cli/threadexecutor.cpp @@ -89,6 +89,8 @@ class ThreadData mTotalFiles = mFiles.size() + mFileSettings.size(); mTotalFileSize = std::accumulate(mFiles.cbegin(), mFiles.cend(), std::size_t(0), [](std::size_t v, const FileWithDetails& p) { return v + p.size(); + }) + std::accumulate(mFileSettings.cbegin(), mFileSettings.cend(), std::size_t(0), [](std::size_t v, const FileSettings& p) { + return v + p.filesize(); }); } @@ -104,7 +106,7 @@ class ThreadData if (mItNextFileSettings != mFileSettings.end()) { file = nullptr; fs = &(*mItNextFileSettings); - fileSize = 0; + fileSize = mItNextFileSettings->filesize(); ++mItNextFileSettings; return true; } diff --git a/lib/filesettings.h b/lib/filesettings.h index 5b3039cd4e7..799bbf6ecca 100644 --- a/lib/filesettings.h +++ b/lib/filesettings.h @@ -47,6 +47,16 @@ class FileWithDetails throw std::runtime_error("empty path specified"); } + FileWithDetails(std::string path, Standards::Language lang) + : mPath(std::move(path)) + , mPathSimplified(Path::simplifyPath(mPath)) + , mLang(lang) + , mSize(0) + { + if (mPath.empty()) + throw std::runtime_error("empty path specified"); + } + const std::string& path() const { return mPath; @@ -71,6 +81,16 @@ class FileWithDetails { return mLang; } + + std::string updateSize() + { + long long ssize = Path::fileSize(mPath); + if (ssize < 0) + return "could not stat file '" + mPath + "': (errno: " + std::to_string(errno) + ")"; + mSize = ssize; + return ""; + } + private: std::string mPath; std::string mPathSimplified; @@ -99,6 +119,16 @@ struct CPPCHECKLIB FileSettings { { return file.spath(); } + std::size_t filesize() const + { + return file.size(); + } + + std::string updateFileSize() + { + return file.updateSize(); + } + std::string defines; // TODO: handle differently std::string cppcheckDefines() const { diff --git a/lib/importproject.h b/lib/importproject.h index ef8c44f8d7b..71cff32e370 100644 --- a/lib/importproject.h +++ b/lib/importproject.h @@ -117,6 +117,7 @@ class CPPCHECKLIB WARN_UNUSED ImportProject { static void printError(const std::string &message); void setRelativePaths(const std::string &filename); + void setFileSizes(); std::string mPath; std::set mAllVSConfigs; diff --git a/lib/path.cpp b/lib/path.cpp index 3a1d517db36..bafc8922f58 100644 --- a/lib/path.cpp +++ b/lib/path.cpp @@ -439,3 +439,39 @@ std::string Path::join(const std::string& path1, const std::string& path2) { return path2; return ((path1.back() == '/') ? path1 : (path1 + "/")) + path2; } + +#ifdef _WIN32 + +# ifdef _WIN64 + +long long Path::fileSize(const std::string &filePath) { + struct _stati64 buf; + if (_stati64(filePath.c_str(), &buf) < 0) { + return -1; + } + return buf.st_size; +} + +# else + +long long Path::fileSize(const std::string &filePath) { + struct _stat buf; + if (_stat(filePath.c_str(), &buf) < 0) { + return -1; + } + return buf.st_size; +} + +# endif + +#else + +long long Path::fileSize(const std::string &filePath) { + struct stat buf; + if (stat(filePath.c_str(), &buf) < 0) { + return -1; + } + return buf.st_size; +} + +#endif diff --git a/lib/path.h b/lib/path.h index db22773ed65..175adcd8a3d 100644 --- a/lib/path.h +++ b/lib/path.h @@ -203,6 +203,13 @@ class CPPCHECKLIB Path { * join 2 paths with '/' separators */ static std::string join(const std::string& path1, const std::string& path2); + + /** + * @brief Get the size of a file + * @param filePath path to the file + * @return size of file, or -1 if the file cannot be accessed + */ + static long long fileSize(const std::string &filePath); }; /// @} diff --git a/test/cli/proj2_test.py b/test/cli/proj2_test.py index c9516d9ddbf..336908820b0 100644 --- a/test/cli/proj2_test.py +++ b/test/cli/proj2_test.py @@ -85,6 +85,46 @@ def test_absolute_path(tmp_path): assert stdout.find('Checking %s ...' % file1) >= 0 assert stdout.find('Checking %s ...' % file2) >= 0 +def test_progress_json(tmp_path): + proj_dir = tmp_path / 'proj2' + shutil.copytree(__proj_dir, proj_dir) + size1 = os.path.getsize(os.path.join(proj_dir, 'a', 'a.c')) + size2 = os.path.getsize(os.path.join(proj_dir, 'b', 'b.c')) + perc1 = 100 * size1 // (size1 + size2) + perc2 = 100 * size2 // (size1 + size2) + __create_compile_commands(proj_dir) + ret, stdout, _ = cppcheck(['--project=' + os.path.join(proj_dir, __COMPILE_COMMANDS_JSON)], cwd=tmp_path) + assert ret == 0, stdout + assert stdout.find('1/2 files checked %d%% done' % perc1) >= 0 or stdout.find('1/2 files checked %d%% done' % perc2) >= 0 + assert stdout.find('2/2 files checked 100% done') >= 0 + +def test_progress_cppcheck(tmp_path): + proj_dir = tmp_path / 'proj2' + shutil.copytree(__proj_dir, proj_dir) + size1 = os.path.getsize(os.path.join(proj_dir, 'a', 'a.c')) + size2 = os.path.getsize(os.path.join(proj_dir, 'b', 'b.c')) + perc1 = 100 * size1 // (size1 + size2) + perc2 = 100 * size2 // (size1 + size2) + __create_compile_commands(proj_dir) + ret, stdout, _ = cppcheck(['--project=proj2/proj2.cppcheck'], cwd=tmp_path) + assert ret == 0, stdout + assert stdout.find('1/2 files checked %d%% done' % perc1) >= 0 or stdout.find('1/2 files checked %d%% done' % perc2) >= 0 + assert stdout.find('2/2 files checked 100% done') >= 0 + +def test_progress_cli(tmp_path): + proj_dir = tmp_path / 'proj2' + shutil.copytree(__proj_dir, proj_dir) + path1 = os.path.join(proj_dir, 'a', 'a.c') + path2 = os.path.join(proj_dir, 'b', 'b.c') + size1 = os.path.getsize(path1) + size2 = os.path.getsize(path2) + perc1 = 100 * size1 // (size1 + size2) + perc2 = 100 * size2 // (size1 + size2) + ret, stdout, _ = cppcheck([path1, path2], cwd=tmp_path) + assert ret == 0, stdout + assert stdout.find('1/2 files checked %d%% done' % perc1) >= 0 or stdout.find('1/2 files checked %d%% done' % perc2) >= 0 + assert stdout.find('2/2 files checked 100% done') >= 0 + def test_gui_project_loads_compile_commands_1(tmp_path): proj_dir = tmp_path / 'proj2' shutil.copytree(__proj_dir, proj_dir)