| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 | /* 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"#include "cm_unordered_map.hxx"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(CM_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 (cmTargets::iterator l = targets.begin(); l != targets.end(); ++l) {      const char* incDirProp = l->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::vector<std::string>::const_iterator j = includes.begin();           j != includes.end(); ++j) {        std::string path = *j;        this->Makefile->ExpandVariablesInString(path);        if (uniqueIncludes.insert(path).second) {          orderedAndUniqueIncludes.push_back(path);        }      }    }    for (std::vector<std::string>::const_iterator it =           orderedAndUniqueIncludes.begin();         it != orderedAndUniqueIncludes.end(); ++it) {      this->AddSearchPath(*it);    }  }  /**   * 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, CM_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.c_str(), "#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;          } else {            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(), CM_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.c_str())) {            found = true;          }          for (std::vector<std::string>::iterator i =                 this->IncludeDirectories.begin();               i != this->IncludeDirectories.end(); ++i) {            std::string path = *i;            path = path + "/";            path = path + cxxFile;            if (cmSystemTools::FileExists(path.c_str())) {              found = true;            }          }          if (!found) {            cxxFile = root + ".cpp";            if (cmSystemTools::FileExists(cxxFile.c_str())) {              found = true;            }            for (std::vector<std::string>::iterator i =                   this->IncludeDirectories.begin();                 i != this->IncludeDirectories.end(); ++i) {              std::string path = *i;              path = path + "/";              path = path + cxxFile;              if (cmSystemTools::FileExists(path.c_str())) {                found = true;              }            }          }          if (!found) {            cxxFile = root + ".c";            if (cmSystemTools::FileExists(cxxFile.c_str())) {              found = true;            }            for (std::vector<std::string>::iterator i =                   this->IncludeDirectories.begin();                 i != this->IncludeDirectories.end(); ++i) {              std::string path = *i;              path = path + "/";              path = path + cxxFile;              if (cmSystemTools::FileExists(path.c_str())) {                found = true;              }            }          }          if (!found) {            cxxFile = root + ".txx";            if (cmSystemTools::FileExists(cxxFile.c_str())) {              found = true;            }            for (std::vector<std::string>::iterator i =                   this->IncludeDirectories.begin();                 i != this->IncludeDirectories.end(); ++i) {              std::string path = *i;              path = path + "/";              path = path + cxxFile;              if (cmSystemTools::FileExists(path.c_str())) {                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 != CM_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::vector<std::string>::const_iterator file =               cFile.GetDepends().begin();             file != cFile.GetDepends().end(); ++file) {          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::vector<std::string>::iterator t =                 this->IncludeDirectories.begin();               t != this->IncludeDirectories.end(); ++t) {            std::string incpath = *t;            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 = "";      }    }  }  /**   * 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::vector<std::string>::iterator i =           this->IncludeDirectories.begin();         i != this->IncludeDirectories.end(); ++i) {      std::string path = *i;      if (!path.empty() && path[path.size() - 1] != '/') {        path = path + "/";      }      path = path + fname;      if (cmSystemTools::FileExists(path.c_str(), 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.c_str(), 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;};// cmOutputRequiredFilesCommandbool 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::DependencySetType::const_iterator d =         info->DependencySet.begin();       d != info->DependencySet.end(); ++d) {    if (visited->find(*d) == visited->end()) {      if (info->FullPath != "") {        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);    }  }}
 |