cmDependsC.cxx 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /*=========================================================================
  2. Program: CMake - Cross-Platform Makefile Generator
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
  8. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
  9. This software is distributed WITHOUT ANY WARRANTY; without even
  10. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  11. PURPOSE. See the above copyright notices for more information.
  12. =========================================================================*/
  13. #include "cmDependsC.h"
  14. #include "cmSystemTools.h"
  15. //----------------------------------------------------------------------------
  16. cmDependsC::cmDependsC(const char* dir, const char* targetFile):
  17. cmDepends(dir, targetFile),
  18. m_SourceFile(),
  19. m_IncludePath(0),
  20. m_IncludeRegexLine(),
  21. m_IncludeRegexScan(),
  22. m_IncludeRegexComplain()
  23. {
  24. }
  25. //----------------------------------------------------------------------------
  26. cmDependsC::cmDependsC(const char* dir, const char* targetFile,
  27. const char* sourceFile,
  28. std::vector<std::string> const& includes,
  29. const char* scanRegex, const char* complainRegex):
  30. cmDepends(dir, targetFile),
  31. m_SourceFile(sourceFile),
  32. m_IncludePath(&includes),
  33. m_IncludeRegexLine("^[ \t]*#[ \t]*include[ \t]*[<\"]([^\">]+)[\">]"),
  34. m_IncludeRegexScan(scanRegex),
  35. m_IncludeRegexComplain(complainRegex)
  36. {
  37. }
  38. //----------------------------------------------------------------------------
  39. cmDependsC::~cmDependsC()
  40. {
  41. }
  42. //----------------------------------------------------------------------------
  43. bool cmDependsC::WriteDependencies(std::ostream& os)
  44. {
  45. // Make sure this is a scanning instance.
  46. if(m_SourceFile == "")
  47. {
  48. cmSystemTools::Error("Cannot scan dependencies without an source file.");
  49. return false;
  50. }
  51. if(!m_IncludePath)
  52. {
  53. cmSystemTools::Error("Cannot scan dependencies without an include path.");
  54. return false;
  55. }
  56. // Walk the dependency graph starting with the source file.
  57. bool first = true;
  58. m_Unscanned.push(m_SourceFile);
  59. m_Encountered.clear();
  60. m_Encountered.insert(m_SourceFile);
  61. std::set<cmStdString> dependencies;
  62. std::set<cmStdString> scanned;
  63. while(!m_Unscanned.empty())
  64. {
  65. // Get the next file to scan.
  66. std::string fname = m_Unscanned.front();
  67. m_Unscanned.pop();
  68. // If not a full path, find the file in the include path.
  69. std::string fullName;
  70. if(first || cmSystemTools::FileIsFullPath(fname.c_str()))
  71. {
  72. if(cmSystemTools::FileExists(fname.c_str()))
  73. {
  74. fullName = fname;
  75. }
  76. }
  77. else
  78. {
  79. for(std::vector<std::string>::const_iterator i = m_IncludePath->begin();
  80. i != m_IncludePath->end(); ++i)
  81. {
  82. std::string temp = *i;
  83. temp += "/";
  84. temp += fname;
  85. if(cmSystemTools::FileExists(temp.c_str()))
  86. {
  87. fullName = temp;
  88. break;
  89. }
  90. }
  91. }
  92. // Complain if the file cannot be found and matches the complain
  93. // regex.
  94. if(fullName.empty() && m_IncludeRegexComplain.find(fname.c_str()))
  95. {
  96. cmSystemTools::Error("Cannot find file \"", fname.c_str(), "\".");
  97. return false;
  98. }
  99. // Scan the file if it was found and has not been scanned already.
  100. if(!fullName.empty() && (scanned.find(fullName) == scanned.end()))
  101. {
  102. // Record scanned files.
  103. scanned.insert(fullName);
  104. // Try to scan the file. Just leave it out if we cannot find
  105. // it.
  106. std::ifstream fin(fullName.c_str());
  107. if(fin)
  108. {
  109. // Add this file as a dependency.
  110. dependencies.insert(fullName);
  111. // Scan this file for new dependencies.
  112. this->Scan(fin);
  113. }
  114. }
  115. first = false;
  116. }
  117. // Write the dependencies to the output stream.
  118. for(std::set<cmStdString>::iterator i=dependencies.begin();
  119. i != dependencies.end(); ++i)
  120. {
  121. os << m_TargetFile.c_str() << ": "
  122. << cmSystemTools::ConvertToOutputPath(i->c_str()).c_str()
  123. << std::endl;
  124. }
  125. os << std::endl;
  126. return true;
  127. }
  128. //----------------------------------------------------------------------------
  129. bool cmDependsC::CheckDependencies(std::istream& is)
  130. {
  131. // Parse dependencies from the stream. If any dependee is missing
  132. // or newer than the depender then dependencies should be
  133. // regenerated.
  134. bool okay = true;
  135. std::string line;
  136. std::string depender;
  137. std::string dependee;
  138. while(cmSystemTools::GetLineFromStream(is, line))
  139. {
  140. // Skip empty lines and comments.
  141. std::string::size_type pos = line.find_first_not_of(" \t\r\n");
  142. if(pos == std::string::npos || line[pos] == '#')
  143. {
  144. continue;
  145. }
  146. // Strip leading whitespace.
  147. if(pos > 0)
  148. {
  149. line = line.substr(pos);
  150. }
  151. // Skip lines too short to have a dependency.
  152. if(line.size() < 3)
  153. {
  154. continue;
  155. }
  156. // Find the colon on the line. Skip the first two characters to
  157. // avoid finding the colon in a drive letter on Windows. Ignore
  158. // the line if a colon cannot be found.
  159. if((pos = line.find(':', 2)) == std::string::npos)
  160. {
  161. continue;
  162. }
  163. // Split the line into depender and dependee.
  164. depender = line.substr(0, pos);
  165. dependee = line.substr(pos+1);
  166. // Strip whitespace from the dependee.
  167. if((pos = dependee.find_first_not_of(" \t\r\n")) != std::string::npos &&
  168. pos > 0)
  169. {
  170. dependee = dependee.substr(pos);
  171. }
  172. if((pos = dependee.find_last_not_of(" \t\r\n")) != std::string::npos)
  173. {
  174. dependee = dependee.substr(0, pos+1);
  175. }
  176. // Convert dependee to a full path.
  177. if(!cmSystemTools::FileIsFullPath(dependee.c_str()))
  178. {
  179. dependee = cmSystemTools::CollapseFullPath(dependee.c_str(),
  180. m_Directory.c_str());
  181. }
  182. // Strip whitespace from the depender.
  183. if((pos = depender.find_last_not_of(" \t\r\n")) != std::string::npos)
  184. {
  185. depender = depender.substr(0, pos+1);
  186. }
  187. // Convert depender to a full path.
  188. if(!cmSystemTools::FileIsFullPath(depender.c_str()))
  189. {
  190. depender = cmSystemTools::CollapseFullPath(depender.c_str(),
  191. m_Directory.c_str());
  192. }
  193. // Dependencies must be regenerated if the dependee does not exist
  194. // or if the depender exists and is older than the dependee.
  195. int result = 0;
  196. if(!cmSystemTools::FileExists(dependee.c_str()) ||
  197. (cmSystemTools::FileExists(depender.c_str()) &&
  198. (!cmSystemTools::FileTimeCompare(depender.c_str(), dependee.c_str(),
  199. &result) || result < 0)))
  200. {
  201. // Dependencies must be regenerated.
  202. okay = false;
  203. // Remove the depender to be sure it is rebuilt.
  204. cmSystemTools::RemoveFile(depender.c_str());
  205. }
  206. }
  207. return okay;
  208. }
  209. //----------------------------------------------------------------------------
  210. void cmDependsC::Scan(std::istream& is)
  211. {
  212. // Read one line at a time.
  213. std::string line;
  214. while(cmSystemTools::GetLineFromStream(is, line))
  215. {
  216. // Match include directives.
  217. if(m_IncludeRegexLine.find(line.c_str()))
  218. {
  219. // Get the file being included.
  220. std::string includeFile = m_IncludeRegexLine.match(1);
  221. // Queue the file if it has not yet been encountered and it
  222. // matches the regular expression for recursive scanning.
  223. if(m_Encountered.find(includeFile) == m_Encountered.end() &&
  224. m_IncludeRegexScan.find(includeFile.c_str()))
  225. {
  226. m_Encountered.insert(includeFile);
  227. m_Unscanned.push(includeFile);
  228. }
  229. }
  230. }
  231. }