123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- /* 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()
- : DependDone(false)
- , SourceFile(nullptr)
- {
- }
- /**
- * 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;
- /**
- * If this object corresponds to a cmSourceFile instance, this points
- * to it.
- */
- const cmSourceFile* SourceFile;
- /**
- * 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); }
- /**
- * 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.c_str());
- 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.c_str());
- 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.c_str(), nullptr);
- }
- 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[incpath.size() - 1] != '/') {
- 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.c_str())) {
- 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[path.size() - 1] != '/') {
- 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[path.size() - 1] != '/') {
- 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);
- }
- }
- }
|