cmListFileCache.cxx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  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. //----------------------------------------------------------------------------
  132. inline bool cmListFileCachePreprocessLine(std::string& line)
  133. {
  134. // Keep track of whether characters are inside a quoted argument.
  135. bool quoted = false;
  136. // Keep track of whether the line is blank.
  137. bool blank = true;
  138. // Loop over every character in the line.
  139. std::string::iterator c;
  140. for(c = line.begin(); c != line.end(); ++c)
  141. {
  142. if((*c == '\\') && (c < line.end()-1))
  143. {
  144. // A backslash escapes any character, so skip the next
  145. // character.
  146. ++c;
  147. // We have encountered a non-whitespace character.
  148. blank = false;
  149. }
  150. else if(*c == '"')
  151. {
  152. // A double-quote either starts or ends a quoted argument.
  153. quoted = !quoted;
  154. // We have encountered a non-whitespace character.
  155. blank = false;
  156. }
  157. else if(*c == '#' && !quoted)
  158. {
  159. // A pound character outside a double-quoted argument marks the
  160. // rest of the line as a comment. Skip it.
  161. break;
  162. }
  163. else if((*c != ' ') && (*c != '\t') && (*c != '\r'))
  164. {
  165. // We have encountered a non-whitespace character.
  166. blank = false;
  167. }
  168. }
  169. // Erase from the comment character to the end of the line. If no
  170. // comment was present, both iterators are end() iterators and this
  171. // does nothing.
  172. line.erase(c, line.end());
  173. // Return true if there is anything useful on this line.
  174. return !blank;
  175. }
  176. //----------------------------------------------------------------------------
  177. bool cmListFileCache::ParseFunction(std::ifstream& fin,
  178. cmListFileFunction& function,
  179. const char* filename,
  180. bool& parseError,
  181. long& line)
  182. {
  183. parseError = false;
  184. std::string& name = function.m_Name;
  185. std::vector<cmListFileArgument>& arguments = function.m_Arguments;
  186. name = "";
  187. arguments = std::vector<cmListFileArgument>();
  188. std::string inbuffer;
  189. if(!fin)
  190. {
  191. return false;
  192. }
  193. if(cmSystemTools::GetLineFromStream(fin, inbuffer) )
  194. {
  195. // Count this line in line numbering.
  196. ++line;
  197. // Preprocess the line to remove comments. Only use it if there
  198. // is non-whitespace.
  199. if(!cmListFileCachePreprocessLine(inbuffer))
  200. {
  201. return false;
  202. }
  203. // Regular expressions to match portions of a command invocation.
  204. cmsys::RegularExpression oneLiner("^[ \t]*([A-Za-z_0-9]*)[ \t]*\\((.*)\\)[ \t\r]*$");
  205. cmsys::RegularExpression multiLine("^[ \t]*([A-Za-z_0-9]*)[ \t]*\\((.*)$");
  206. cmsys::RegularExpression lastLine("^(.*)\\)[ \t\r]*$");
  207. // look for a oneline fun(arg arg2)
  208. if(oneLiner.find(inbuffer.c_str()))
  209. {
  210. // the arguments are the second match
  211. std::string args = oneLiner.match(2);
  212. name = oneLiner.match(1);
  213. // break up the arguments
  214. cmListFileCache::GetArguments(args, arguments);
  215. function.m_Line = line;
  216. return true;
  217. }
  218. // look for a start of a multiline with no trailing ")" fun(arg arg2
  219. else if(multiLine.find(inbuffer.c_str()))
  220. {
  221. name = multiLine.match(1);
  222. std::string args = multiLine.match(2);
  223. cmListFileCache::GetArguments(args, arguments);
  224. function.m_Line = line;
  225. // Read lines until the closing paren is hit
  226. bool done = false;
  227. while(!done)
  228. {
  229. // read lines until the end paren is found
  230. if(cmSystemTools::GetLineFromStream(fin, inbuffer) )
  231. {
  232. // Count this line in line numbering.
  233. ++line;
  234. // Preprocess the line to remove comments. Only use it if there
  235. // is non-whitespace.
  236. if(!cmListFileCachePreprocessLine(inbuffer))
  237. {
  238. continue;
  239. }
  240. // Is this the last line?
  241. if(lastLine.find(inbuffer.c_str()))
  242. {
  243. done = true;
  244. std::string gargs = lastLine.match(1);
  245. cmListFileCache::GetArguments(gargs, arguments);
  246. }
  247. else
  248. {
  249. cmListFileCache::GetArguments(inbuffer, arguments);
  250. }
  251. }
  252. else
  253. {
  254. parseError = true;
  255. cmOStringStream error;
  256. error << "Error in cmake code at\n"
  257. << filename << ":" << line << ":\n"
  258. << "Parse error. Function missing ending \")\".";
  259. cmSystemTools::Error(error.str().c_str());
  260. return false;
  261. }
  262. }
  263. return true;
  264. }
  265. else
  266. {
  267. parseError = true;
  268. cmOStringStream error;
  269. error << "Error in cmake code at\n"
  270. << filename << ":" << line << ":\n"
  271. << "Parse error.";
  272. cmSystemTools::Error(error.str().c_str());
  273. return false;
  274. }
  275. }
  276. return false;
  277. }
  278. void cmListFileCache::GetArguments(std::string& line,
  279. std::vector<cmListFileArgument>& arguments)
  280. {
  281. // Match a normal argument (not quoted, no spaces).
  282. cmsys::RegularExpression normalArgument("[ \t]*(([^ \t\r\\]|[\\].)+)[ \t\r]*");
  283. // Match a quoted argument (surrounded by double quotes, spaces allowed).
  284. cmsys::RegularExpression quotedArgument("[ \t]*(\"([^\"\\]|[\\].)*\")[ \t\r]*");
  285. bool done = false;
  286. while(!done)
  287. {
  288. std::string arg;
  289. std::string::size_type endpos=0;
  290. bool quoted = false;
  291. bool foundQuoted = quotedArgument.find(line.c_str());
  292. bool foundNormal = normalArgument.find(line.c_str());
  293. if(foundQuoted && foundNormal)
  294. {
  295. // Both matches were found. Take the earlier one.
  296. // Favor double-quoted version if there is a tie.
  297. if(normalArgument.start(1) < quotedArgument.start(1))
  298. {
  299. arg = normalArgument.match(1);
  300. endpos = normalArgument.end(1);
  301. }
  302. else
  303. {
  304. arg = quotedArgument.match(1);
  305. endpos = quotedArgument.end(1);
  306. // Strip off the double quotes on the ends.
  307. arg = arg.substr(1, arg.length()-2);
  308. quoted = true;
  309. }
  310. }
  311. else if(foundQuoted)
  312. {
  313. arg = quotedArgument.match(1);
  314. endpos = quotedArgument.end(1);
  315. // Strip off the double quotes on the ends.
  316. arg = arg.substr(1, arg.length()-2);
  317. quoted = true;
  318. }
  319. else if(foundNormal)
  320. {
  321. arg = normalArgument.match(1);
  322. endpos = normalArgument.end(1);
  323. }
  324. else
  325. {
  326. done = true;
  327. }
  328. if(!done)
  329. {
  330. cmListFileArgument a(cmSystemTools::RemoveEscapes(arg.c_str()), quoted);
  331. arguments.push_back(a);
  332. line = line.substr(endpos, line.length() - endpos);
  333. }
  334. }
  335. }