#include "project/IncludeValidation.h" #include "project/IncludeDirective.h" #include "utility/file/FilePath.h" #include "utility/text/TextAccess.h" #include "utility/utility.h" #include "utility/utilityString.h" std::vector IncludeValidation::getUnresolvedIncludeDirectives( const std::vector& sourceFilePaths, const std::vector& indexedPaths, const std::vector& headerSearchDirectories, size_t quantileCount, std::function progress ) { struct IncludeDirectiveComparator { bool operator()(const IncludeDirective& a, const IncludeDirective& b) { return a.getIncludedFile() < b.getIncludedFile(); } }; std::set processedFilePaths; std::set unresolvedIncludeDirectives; quantileCount = std::max(1, std::min(quantileCount, sourceFilePaths.size())); std::vector> quantiles; for (size_t i = 0; i < quantileCount; i++) { quantiles.push_back(std::vector()); } for (size_t i = 0; i < sourceFilePaths.size(); i++) { quantiles[i%quantileCount].push_back(sourceFilePaths[i]); } for (size_t i = 0; i < quantiles.size(); i++) { const std::vector& quantile = quantiles[i]; progress(float(i) / quantiles.size()); std::set unprocessedFilePaths(quantile.begin(), quantile.end()); while (!unprocessedFilePaths.empty()) { std::transform( unprocessedFilePaths.begin(), unprocessedFilePaths.end(), std::inserter(processedFilePaths, processedFilePaths.begin()), [](const FilePath& p){ return p.str(); } ); std::set tempUnprocessedFilePaths; for (const FilePath& filePath: unprocessedFilePaths) { for (const IncludeDirective& includeDirective: getIncludeDirectives(filePath)) { const FilePath resolvedIncludePath = resolveIncludeDirective(includeDirective, headerSearchDirectories).canonical(); if (resolvedIncludePath.empty()) { unresolvedIncludeDirectives.insert(includeDirective); } else if (processedFilePaths.find(resolvedIncludePath.str()) == processedFilePaths.end()) { for (const FilePath& indexedPath: indexedPaths) { if (indexedPath.contains(resolvedIncludePath)) { tempUnprocessedFilePaths.insert(resolvedIncludePath); break; } } } } } unprocessedFilePaths = tempUnprocessedFilePaths; } } std::vector ret; for (const IncludeDirective& directive: unresolvedIncludeDirectives) { ret.push_back(directive); } progress(1.0f); return ret; } std::vector IncludeValidation::getIncludeDirectives(const FilePath& filePath) { std::vector includeDirectives; if (filePath.exists()) { std::shared_ptr textAccess = TextAccess::createFromFile(filePath); const std::vector lines = textAccess->getAllLines(); for (size_t i = 0; i < lines.size(); i++) { const std::string lineTrimmedToHash = utility::trim(lines[i]); if (utility::isPrefix("#", lineTrimmedToHash)) { const std::string lineTrimmedToInclude = utility::trim(lineTrimmedToHash.substr(1)); if (utility::isPrefix("include", lineTrimmedToInclude)) { std::string includeString = utility::substrBetween(lineTrimmedToInclude, "<", ">"); bool usesBrackets = true; if (includeString.empty()) { includeString = utility::substrBetween(lineTrimmedToInclude, "\"", "\""); usesBrackets = false; } if (!includeString.empty()) { // lines are 1 based includeDirectives.push_back(IncludeDirective(FilePath(includeString), filePath, i + 1, usesBrackets)); } } } } } return includeDirectives; } FilePath IncludeValidation::resolveIncludeDirective(const IncludeDirective& includeDirective, const std::vector& headerSearchDirectories) { { // check for an absolute include path if (includeDirective.getIncludedFile().exists()) { return includeDirective.getIncludedFile(); } } { // check for an include path relative to the including path FilePath resolvedIncludePath = includeDirective.getIncludingFile().parentDirectory().concat(includeDirective.getIncludedFile()); if (resolvedIncludePath.exists()) { return resolvedIncludePath; } } { // check for an include path relative to the header search directories for (const FilePath& headerSearchDirectory: headerSearchDirectories) { FilePath resolvedIncludePath = headerSearchDirectory.concat(includeDirective.getIncludedFile()); if (resolvedIncludePath.exists()) { return resolvedIncludePath; } } } return FilePath(); }