diff options
Diffstat (limited to 'Source/cmOutputRequiredFilesCommand.cxx')
-rw-r--r-- | Source/cmOutputRequiredFilesCommand.cxx | 522 |
1 files changed, 522 insertions, 0 deletions
diff --git a/Source/cmOutputRequiredFilesCommand.cxx b/Source/cmOutputRequiredFilesCommand.cxx new file mode 100644 index 0000000..cb9433f --- /dev/null +++ b/Source/cmOutputRequiredFilesCommand.cxx @@ -0,0 +1,522 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmOutputRequiredFilesCommand.h" + +#include "cmsys/FStream.hxx" +#include "cmsys/RegularExpression.hxx" +#include <map> +#include <utility> + +#include "cmAlgorithms.h" +#include "cmGeneratorExpression.h" +#include "cmMakefile.h" +#include "cmSourceFile.h" +#include "cmSystemTools.h" +#include "cmTarget.h" + +class cmExecutionStatus; + +/** \class cmDependInformation + * \brief Store dependency information for a single source file. + * + * This structure stores the depend information for a single source file. + */ +class cmDependInformation +{ +public: + /** + * Construct with dependency generation marked not done; instance + * not placed in cmMakefile's list. + */ + cmDependInformation() = default; + + /** + * The set of files on which this one depends. + */ + typedef std::set<cmDependInformation*> DependencySetType; + DependencySetType DependencySet; + + /** + * This flag indicates whether dependency checking has been + * performed for this file. + */ + bool DependDone = false; + + /** + * If this object corresponds to a cmSourceFile instance, this points + * to it. + */ + const cmSourceFile* SourceFile = nullptr; + + /** + * Full path to this file. + */ + std::string FullPath; + + /** + * Full path not including file name. + */ + std::string PathOnly; + + /** + * Name used to #include this file. + */ + std::string IncludeName; + + /** + * This method adds the dependencies of another file to this one. + */ + void AddDependencies(cmDependInformation* info) + { + if (this != info) { + this->DependencySet.insert(info); + } + } +}; + +class cmLBDepend +{ +public: + /** + * Construct the object with verbose turned off. + */ + cmLBDepend() + { + this->Verbose = false; + this->IncludeFileRegularExpression.compile("^.*$"); + this->ComplainFileRegularExpression.compile("^$"); + } + + /** + * Destructor. + */ + ~cmLBDepend() { cmDeleteAll(this->DependInformationMap); } + + cmLBDepend(const cmLBDepend&) = delete; + cmLBDepend& operator=(const cmLBDepend&) = delete; + + /** + * Set the makefile that is used as a source of classes. + */ + void SetMakefile(cmMakefile* makefile) + { + this->Makefile = makefile; + + // Now extract the include file regular expression from the makefile. + this->IncludeFileRegularExpression.compile( + this->Makefile->GetIncludeRegularExpression()); + this->ComplainFileRegularExpression.compile( + this->Makefile->GetComplainRegularExpression()); + + // Now extract any include paths from the targets + std::set<std::string> uniqueIncludes; + std::vector<std::string> orderedAndUniqueIncludes; + cmTargets& targets = this->Makefile->GetTargets(); + for (auto const& target : targets) { + const char* incDirProp = + target.second.GetProperty("INCLUDE_DIRECTORIES"); + if (!incDirProp) { + continue; + } + + std::string incDirs = cmGeneratorExpression::Preprocess( + incDirProp, cmGeneratorExpression::StripAllGeneratorExpressions); + + std::vector<std::string> includes; + cmSystemTools::ExpandListArgument(incDirs, includes); + + for (std::string& path : includes) { + this->Makefile->ExpandVariablesInString(path); + if (uniqueIncludes.insert(path).second) { + orderedAndUniqueIncludes.push_back(path); + } + } + } + + for (std::string const& inc : orderedAndUniqueIncludes) { + this->AddSearchPath(inc); + } + } + + /** + * Add a directory to the search path for include files. + */ + void AddSearchPath(const std::string& path) + { + this->IncludeDirectories.push_back(path); + } + + /** + * Generate dependencies for the file given. Returns a pointer to + * the cmDependInformation object for the file. + */ + const cmDependInformation* FindDependencies(const char* file) + { + cmDependInformation* info = this->GetDependInformation(file, nullptr); + this->GenerateDependInformation(info); + return info; + } + +protected: + /** + * Compute the depend information for this class. + */ + + void DependWalk(cmDependInformation* info) + { + cmsys::ifstream fin(info->FullPath.c_str()); + if (!fin) { + cmSystemTools::Error("error can not open " + info->FullPath); + return; + } + + std::string line; + while (cmSystemTools::GetLineFromStream(fin, line)) { + if (cmHasLiteralPrefix(line, "#include")) { + // if it is an include line then create a string class + size_t qstart = line.find('\"', 8); + size_t qend; + // if a quote is not found look for a < + if (qstart == std::string::npos) { + qstart = line.find('<', 8); + // if a < is not found then move on + if (qstart == std::string::npos) { + cmSystemTools::Error("unknown include directive " + line); + continue; + } + qend = line.find('>', qstart + 1); + } else { + qend = line.find('\"', qstart + 1); + } + // extract the file being included + std::string includeFile = line.substr(qstart + 1, qend - qstart - 1); + // see if the include matches the regular expression + if (!this->IncludeFileRegularExpression.find(includeFile)) { + if (this->Verbose) { + std::string message = "Skipping "; + message += includeFile; + message += " for file "; + message += info->FullPath; + cmSystemTools::Error(message); + } + continue; + } + + // Add this file and all its dependencies. + this->AddDependency(info, includeFile.c_str()); + /// add the cxx file if it exists + std::string cxxFile = includeFile; + std::string::size_type pos = cxxFile.rfind('.'); + if (pos != std::string::npos) { + std::string root = cxxFile.substr(0, pos); + cxxFile = root + ".cxx"; + bool found = false; + // try jumping to .cxx .cpp and .c in order + if (cmSystemTools::FileExists(cxxFile)) { + found = true; + } + for (std::string path : this->IncludeDirectories) { + path = path + "/"; + path = path + cxxFile; + if (cmSystemTools::FileExists(path)) { + found = true; + } + } + if (!found) { + cxxFile = root + ".cpp"; + if (cmSystemTools::FileExists(cxxFile)) { + found = true; + } + for (std::string path : this->IncludeDirectories) { + path = path + "/"; + path = path + cxxFile; + if (cmSystemTools::FileExists(path)) { + found = true; + } + } + } + if (!found) { + cxxFile = root + ".c"; + if (cmSystemTools::FileExists(cxxFile)) { + found = true; + } + for (std::string path : this->IncludeDirectories) { + path = path + "/"; + path = path + cxxFile; + if (cmSystemTools::FileExists(path)) { + found = true; + } + } + } + if (!found) { + cxxFile = root + ".txx"; + if (cmSystemTools::FileExists(cxxFile)) { + found = true; + } + for (std::string path : this->IncludeDirectories) { + path = path + "/"; + path = path + cxxFile; + if (cmSystemTools::FileExists(path)) { + found = true; + } + } + } + if (found) { + this->AddDependency(info, cxxFile.c_str()); + } + } + } + } + } + + /** + * Add a dependency. Possibly walk it for more dependencies. + */ + void AddDependency(cmDependInformation* info, const char* file) + { + cmDependInformation* dependInfo = + this->GetDependInformation(file, info->PathOnly.c_str()); + this->GenerateDependInformation(dependInfo); + info->AddDependencies(dependInfo); + } + + /** + * Fill in the given object with dependency information. If the + * information is already complete, nothing is done. + */ + void GenerateDependInformation(cmDependInformation* info) + { + // If dependencies are already done, stop now. + if (info->DependDone) { + return; + } + // Make sure we don't visit the same file more than once. + info->DependDone = true; + + const char* path = info->FullPath.c_str(); + if (!path) { + cmSystemTools::Error( + "Attempt to find dependencies for file without path!"); + return; + } + + bool found = false; + + // If the file exists, use it to find dependency information. + if (cmSystemTools::FileExists(path, true)) { + // Use the real file to find its dependencies. + this->DependWalk(info); + found = true; + } + + // See if the cmSourceFile for it has any files specified as + // dependency hints. + if (info->SourceFile != nullptr) { + + // Get the cmSourceFile corresponding to this. + const cmSourceFile& cFile = *(info->SourceFile); + // See if there are any hints for finding dependencies for the missing + // file. + if (!cFile.GetDepends().empty()) { + // Dependency hints have been given. Use them to begin the + // recursion. + for (std::string const& file : cFile.GetDepends()) { + this->AddDependency(info, file.c_str()); + } + + // Found dependency information. We are done. + found = true; + } + } + + if (!found) { + // Try to find the file amongst the sources + cmSourceFile* srcFile = this->Makefile->GetSource( + cmSystemTools::GetFilenameWithoutExtension(path)); + if (srcFile) { + if (srcFile->GetFullPath() == path) { + found = true; + } else { + // try to guess which include path to use + for (std::string incpath : this->IncludeDirectories) { + if (!incpath.empty() && incpath.back() != '/') { + incpath = incpath + "/"; + } + incpath = incpath + path; + if (srcFile->GetFullPath() == incpath) { + // set the path to the guessed path + info->FullPath = incpath; + found = true; + } + } + } + } + } + + if (!found) { + // Couldn't find any dependency information. + if (this->ComplainFileRegularExpression.find(info->IncludeName)) { + cmSystemTools::Error("error cannot find dependencies for ", path); + } else { + // Destroy the name of the file so that it won't be output as a + // dependency. + info->FullPath.clear(); + } + } + } + + /** + * Get an instance of cmDependInformation corresponding to the given file + * name. + */ + cmDependInformation* GetDependInformation(const char* file, + const char* extraPath) + { + // Get the full path for the file so that lookup is unambiguous. + std::string fullPath = this->FullPath(file, extraPath); + + // Try to find the file's instance of cmDependInformation. + DependInformationMapType::const_iterator result = + this->DependInformationMap.find(fullPath); + if (result != this->DependInformationMap.end()) { + // Found an instance, return it. + return result->second; + } + // Didn't find an instance. Create a new one and save it. + cmDependInformation* info = new cmDependInformation; + info->FullPath = fullPath; + info->PathOnly = cmSystemTools::GetFilenamePath(fullPath); + info->IncludeName = file; + this->DependInformationMap[fullPath] = info; + return info; + } + + /** + * Find the full path name for the given file name. + * This uses the include directories. + * TODO: Cache path conversions to reduce FileExists calls. + */ + std::string FullPath(const char* fname, const char* extraPath) + { + DirectoryToFileToPathMapType::iterator m; + if (extraPath) { + m = this->DirectoryToFileToPathMap.find(extraPath); + } else { + m = this->DirectoryToFileToPathMap.find(""); + } + + if (m != this->DirectoryToFileToPathMap.end()) { + FileToPathMapType& map = m->second; + FileToPathMapType::iterator p = map.find(fname); + if (p != map.end()) { + return p->second; + } + } + + if (cmSystemTools::FileExists(fname, true)) { + std::string fp = cmSystemTools::CollapseFullPath(fname); + this->DirectoryToFileToPathMap[extraPath ? extraPath : ""][fname] = fp; + return fp; + } + + for (std::string path : this->IncludeDirectories) { + if (!path.empty() && path.back() != '/') { + path = path + "/"; + } + path = path + fname; + if (cmSystemTools::FileExists(path, true) && + !cmSystemTools::FileIsDirectory(path)) { + std::string fp = cmSystemTools::CollapseFullPath(path); + this->DirectoryToFileToPathMap[extraPath ? extraPath : ""][fname] = fp; + return fp; + } + } + + if (extraPath) { + std::string path = extraPath; + if (!path.empty() && path.back() != '/') { + path = path + "/"; + } + path = path + fname; + if (cmSystemTools::FileExists(path, true) && + !cmSystemTools::FileIsDirectory(path)) { + std::string fp = cmSystemTools::CollapseFullPath(path); + this->DirectoryToFileToPathMap[extraPath][fname] = fp; + return fp; + } + } + + // Couldn't find the file. + return std::string(fname); + } + + cmMakefile* Makefile; + bool Verbose; + cmsys::RegularExpression IncludeFileRegularExpression; + cmsys::RegularExpression ComplainFileRegularExpression; + std::vector<std::string> IncludeDirectories; + typedef std::map<std::string, std::string> FileToPathMapType; + typedef std::map<std::string, FileToPathMapType> + DirectoryToFileToPathMapType; + typedef std::map<std::string, cmDependInformation*> DependInformationMapType; + DependInformationMapType DependInformationMap; + DirectoryToFileToPathMapType DirectoryToFileToPathMap; +}; + +// cmOutputRequiredFilesCommand +bool cmOutputRequiredFilesCommand::InitialPass( + std::vector<std::string> const& args, cmExecutionStatus&) +{ + if (args.size() != 2) { + this->SetError("called with incorrect number of arguments"); + return false; + } + + // store the arg for final pass + this->File = args[0]; + this->OutputFile = args[1]; + + // compute the list of files + cmLBDepend md; + md.SetMakefile(this->Makefile); + md.AddSearchPath(this->Makefile->GetCurrentSourceDirectory()); + // find the depends for a file + const cmDependInformation* info = md.FindDependencies(this->File.c_str()); + if (info) { + // write them out + FILE* fout = cmsys::SystemTools::Fopen(this->OutputFile, "w"); + if (!fout) { + std::string err = "Can not open output file: "; + err += this->OutputFile; + this->SetError(err); + return false; + } + std::set<cmDependInformation const*> visited; + this->ListDependencies(info, fout, &visited); + fclose(fout); + } + + return true; +} + +void cmOutputRequiredFilesCommand::ListDependencies( + cmDependInformation const* info, FILE* fout, + std::set<cmDependInformation const*>* visited) +{ + // add info to the visited set + visited->insert(info); + // now recurse with info's dependencies + for (cmDependInformation* d : info->DependencySet) { + if (visited->find(d) == visited->end()) { + if (!info->FullPath.empty()) { + std::string tmp = d->FullPath; + std::string::size_type pos = tmp.rfind('.'); + if (pos != std::string::npos && (tmp.substr(pos) != ".h")) { + tmp = tmp.substr(0, pos); + fprintf(fout, "%s\n", d->FullPath.c_str()); + } + } + this->ListDependencies(d, fout, visited); + } + } +} |