cmListFileCache.cxx 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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 "cmListFileCache.h"
  14. #include "cmSystemTools.h"
  15. #include <cmsys/RegularExpression.hxx>
  16. cmListFileCache* cmListFileCache::Instance = 0;
  17. cmListFileCache* cmListFileCache::GetInstance()
  18. {
  19. if(!cmListFileCache::Instance)
  20. {
  21. cmListFileCache::Instance = new cmListFileCache;
  22. }
  23. return cmListFileCache::Instance;
  24. }
  25. void cmListFileCache::ClearCache()
  26. {
  27. delete cmListFileCache::Instance;
  28. cmListFileCache::Instance = 0;
  29. }
  30. cmListFile* cmListFileCache::GetFileCache(const char* path,
  31. bool requireProjectCommand)
  32. {
  33. ListFileMap::iterator sl = m_ListFileCache.find(path);
  34. if (sl == m_ListFileCache.end())
  35. {
  36. // if not already in the map, then parse and store the
  37. // file
  38. if(!this->CacheFile(path, requireProjectCommand))
  39. {
  40. return 0;
  41. }
  42. sl = m_ListFileCache.find(path);
  43. if (sl == m_ListFileCache.end())
  44. {
  45. cmSystemTools::Error("Fatal error, in cmListFileCache CacheFile failed",
  46. path);
  47. return 0;
  48. }
  49. }
  50. cmListFile& ret = sl->second;
  51. if(cmSystemTools::ModifiedTime(path) > ret.m_ModifiedTime )
  52. {
  53. if(!this->CacheFile(path, requireProjectCommand))
  54. {
  55. return 0;
  56. }
  57. else
  58. {
  59. sl = m_ListFileCache.find(path);
  60. return &sl->second;
  61. }
  62. }
  63. return &ret;
  64. }
  65. bool cmListFileCache::CacheFile(const char* path, bool requireProjectCommand)
  66. {
  67. if(!cmSystemTools::FileExists(path))
  68. {
  69. return false;
  70. }
  71. std::ifstream fin(path);
  72. if(!fin)
  73. {
  74. cmSystemTools::Error("cmListFileCache: error can not open file ", path);
  75. return false;
  76. }
  77. long line=0;
  78. cmListFile inFile;
  79. inFile.m_ModifiedTime = cmSystemTools::ModifiedTime(path);
  80. bool parseError;
  81. while ( fin )
  82. {
  83. cmListFileFunction inFunction;
  84. if(cmListFileCache::ParseFunction(fin, inFunction, path, parseError,
  85. line))
  86. {
  87. inFunction.m_FilePath = path;
  88. inFile.m_Functions.push_back(inFunction);
  89. }
  90. if (parseError)
  91. {
  92. inFile.m_ModifiedTime = 0;
  93. }
  94. }
  95. if(requireProjectCommand)
  96. {
  97. bool hasProject = false;
  98. // search for a project command
  99. for(std::vector<cmListFileFunction>::iterator i
  100. = inFile.m_Functions.begin();
  101. i != inFile.m_Functions.end(); ++i)
  102. {
  103. if(i->m_Name == "PROJECT")
  104. {
  105. hasProject = true;
  106. break;
  107. }
  108. }
  109. // if no project command is found, add one
  110. if(!hasProject)
  111. {
  112. cmListFileFunction project;
  113. project.m_Name = "PROJECT";
  114. cmListFileArgument prj("Project", false);
  115. project.m_Arguments.push_back(prj);
  116. inFile.m_Functions.insert(inFile.m_Functions.begin(),project);
  117. }
  118. }
  119. m_ListFileCache[path] = inFile;
  120. return true;
  121. }
  122. void cmListFileCache::FlushCache(const char* path)
  123. {
  124. ListFileMap::iterator it = m_ListFileCache.find(path);
  125. if ( it != m_ListFileCache.end() )
  126. {
  127. m_ListFileCache.erase(it);
  128. return;
  129. }
  130. }
  131. inline void RemoveComments(std::string& line)
  132. {
  133. std::string::size_type pos = line.find("#");
  134. if (pos != std::string::npos )
  135. {
  136. line.erase(pos);
  137. }
  138. }
  139. bool cmListFileCache::ParseFunction(std::ifstream& fin,
  140. cmListFileFunction& function,
  141. const char* filename,
  142. bool& parseError,
  143. long& line)
  144. {
  145. parseError = false;
  146. std::string& name = function.m_Name;
  147. std::vector<cmListFileArgument>& arguments = function.m_Arguments;
  148. name = "";
  149. arguments = std::vector<cmListFileArgument>();
  150. std::string inbuffer;
  151. if(!fin)
  152. {
  153. return false;
  154. }
  155. if(cmSystemTools::GetLineFromStream(fin, inbuffer) )
  156. {
  157. ++line;
  158. RemoveComments(inbuffer);
  159. cmsys::RegularExpression blankLine("^[ \t\r]*$");
  160. cmsys::RegularExpression oneLiner("^[ \t]*([A-Za-z_0-9]*)[ \t]*\\((.*)\\)[ \t\r]*$");
  161. cmsys::RegularExpression multiLine("^[ \t]*([A-Za-z_0-9]*)[ \t]*\\((.*)$");
  162. cmsys::RegularExpression lastLine("^(.*)\\)[ \t\r]*$");
  163. // check for blank line or comment
  164. if(blankLine.find(inbuffer.c_str()) )
  165. {
  166. return false;
  167. }
  168. // look for a oneline fun(arg arg2)
  169. else if(oneLiner.find(inbuffer.c_str()))
  170. {
  171. // the arguments are the second match
  172. std::string args = oneLiner.match(2);
  173. name = oneLiner.match(1);
  174. // break up the arguments
  175. cmListFileCache::GetArguments(args, arguments);
  176. function.m_Line = line;
  177. return true;
  178. }
  179. // look for a start of a multiline with no trailing ")" fun(arg arg2
  180. else if(multiLine.find(inbuffer.c_str()))
  181. {
  182. name = multiLine.match(1);
  183. std::string args = multiLine.match(2);
  184. cmListFileCache::GetArguments(args, arguments);
  185. function.m_Line = line;
  186. // Read lines until the closing paren is hit
  187. bool done = false;
  188. while(!done)
  189. {
  190. // read lines until the end paren is found
  191. if(cmSystemTools::GetLineFromStream(fin, inbuffer) )
  192. {
  193. ++line;
  194. RemoveComments(inbuffer);
  195. // Check for comment lines and ignore them.
  196. if(blankLine.find(inbuffer.c_str()))
  197. { continue; }
  198. // Is this the last line?
  199. if(lastLine.find(inbuffer.c_str()))
  200. {
  201. done = true;
  202. std::string gargs = lastLine.match(1);
  203. cmListFileCache::GetArguments(gargs, arguments);
  204. }
  205. else
  206. {
  207. cmListFileCache::GetArguments(inbuffer, arguments);
  208. }
  209. }
  210. else
  211. {
  212. parseError = true;
  213. cmOStringStream error;
  214. error << "Error in cmake code at\n"
  215. << filename << ":" << line << ":\n"
  216. << "Parse error. Function missing ending \")\".";
  217. cmSystemTools::Error(error.str().c_str());
  218. return false;
  219. }
  220. }
  221. return true;
  222. }
  223. else
  224. {
  225. parseError = true;
  226. cmOStringStream error;
  227. error << "Error in cmake code at\n"
  228. << filename << ":" << line << ":\n"
  229. << "Parse error.";
  230. cmSystemTools::Error(error.str().c_str());
  231. return false;
  232. }
  233. }
  234. return false;
  235. }
  236. void cmListFileCache::GetArguments(std::string& line,
  237. std::vector<cmListFileArgument>& arguments)
  238. {
  239. // Match a normal argument (not quoted, no spaces).
  240. cmsys::RegularExpression normalArgument("[ \t]*(([^ \t\r\\]|[\\].)+)[ \t\r]*");
  241. // Match a quoted argument (surrounded by double quotes, spaces allowed).
  242. cmsys::RegularExpression quotedArgument("[ \t]*(\"([^\"\\]|[\\].)*\")[ \t\r]*");
  243. bool done = false;
  244. while(!done)
  245. {
  246. std::string arg;
  247. std::string::size_type endpos=0;
  248. bool quoted = false;
  249. bool foundQuoted = quotedArgument.find(line.c_str());
  250. bool foundNormal = normalArgument.find(line.c_str());
  251. if(foundQuoted && foundNormal)
  252. {
  253. // Both matches were found. Take the earlier one.
  254. // Favor double-quoted version if there is a tie.
  255. if(normalArgument.start(1) < quotedArgument.start(1))
  256. {
  257. arg = normalArgument.match(1);
  258. endpos = normalArgument.end(1);
  259. }
  260. else
  261. {
  262. arg = quotedArgument.match(1);
  263. endpos = quotedArgument.end(1);
  264. // Strip off the double quotes on the ends.
  265. arg = arg.substr(1, arg.length()-2);
  266. quoted = true;
  267. }
  268. }
  269. else if(foundQuoted)
  270. {
  271. arg = quotedArgument.match(1);
  272. endpos = quotedArgument.end(1);
  273. // Strip off the double quotes on the ends.
  274. arg = arg.substr(1, arg.length()-2);
  275. quoted = true;
  276. }
  277. else if(foundNormal)
  278. {
  279. arg = normalArgument.match(1);
  280. endpos = normalArgument.end(1);
  281. }
  282. else
  283. {
  284. done = true;
  285. }
  286. if(!done)
  287. {
  288. cmListFileArgument a(cmSystemTools::RemoveEscapes(arg.c_str()), quoted);
  289. arguments.push_back(a);
  290. line = line.substr(endpos, line.length() - endpos);
  291. }
  292. }
  293. }