1
0

cmDependsFortran.cxx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  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 "cmDependsFortran.h"
  14. #include "cmSystemTools.h"
  15. #include "cmDependsFortranParser.h" /* Interface to parser object. */
  16. #include <assert.h>
  17. #include <stack>
  18. //----------------------------------------------------------------------------
  19. // Parser methods not included in generated interface.
  20. // Get the current buffer processed by the lexer.
  21. YY_BUFFER_STATE cmDependsFortranLexer_GetCurrentBuffer(yyscan_t yyscanner);
  22. // The parser entry point.
  23. int cmDependsFortran_yyparse(yyscan_t);
  24. //----------------------------------------------------------------------------
  25. // Define parser object internal structure.
  26. struct cmDependsFortranFile
  27. {
  28. cmDependsFortranFile(FILE* file, YY_BUFFER_STATE buffer,
  29. const std::string& dir):
  30. File(file), Buffer(buffer), Directory(dir) {}
  31. FILE* File;
  32. YY_BUFFER_STATE Buffer;
  33. std::string Directory;
  34. };
  35. struct cmDependsFortranParser_s
  36. {
  37. cmDependsFortranParser_s(cmDependsFortran* self);
  38. ~cmDependsFortranParser_s();
  39. // Pointer back to the main class.
  40. cmDependsFortran* Self;
  41. // Lexical scanner instance.
  42. yyscan_t Scanner;
  43. // Stack of open files in the translation unit.
  44. std::stack<cmDependsFortranFile> FileStack;
  45. // Buffer for string literals.
  46. std::string TokenString;
  47. // Flag for whether lexer is reading from inside an interface.
  48. int InInterface;
  49. // Set of provided and required modules.
  50. std::set<cmStdString> Provides;
  51. std::set<cmStdString> Requires;
  52. // Set of files included in the translation unit.
  53. std::set<cmStdString> Includes;
  54. };
  55. //----------------------------------------------------------------------------
  56. cmDependsFortran::cmDependsFortran(const char* dir, const char* targetFile):
  57. cmDepends(dir, targetFile),
  58. m_SourceFile(),
  59. m_IncludePath(0)
  60. {
  61. }
  62. //----------------------------------------------------------------------------
  63. cmDependsFortran::cmDependsFortran(const char* dir, const char* targetFile,
  64. const char* sourceFile,
  65. std::vector<std::string> const& includes):
  66. cmDepends(dir, targetFile),
  67. m_SourceFile(sourceFile),
  68. m_IncludePath(&includes)
  69. {
  70. }
  71. //----------------------------------------------------------------------------
  72. cmDependsFortran::~cmDependsFortran()
  73. {
  74. }
  75. //----------------------------------------------------------------------------
  76. bool cmDependsFortran::WriteDependencies(std::ostream& os)
  77. {
  78. // Make sure this is a scanning instance.
  79. if(m_SourceFile == "")
  80. {
  81. cmSystemTools::Error("Cannot scan dependencies without an source file.");
  82. return false;
  83. }
  84. if(!m_IncludePath)
  85. {
  86. cmSystemTools::Error("Cannot scan dependencies without an include path.");
  87. return false;
  88. }
  89. // Create the parser object.
  90. cmDependsFortranParser parser(this);
  91. // Push on the starting file.
  92. cmDependsFortranParser_FilePush(&parser, m_SourceFile.c_str());
  93. // Parse the translation unit.
  94. if(cmDependsFortran_yyparse(parser.Scanner) != 0)
  95. {
  96. // Failed to parse the file. Report failure to write dependencies.
  97. return false;
  98. }
  99. // Write the include dependencies to the output stream.
  100. for(std::set<cmStdString>::const_iterator i = parser.Includes.begin();
  101. i != parser.Includes.end(); ++i)
  102. {
  103. os << m_TargetFile.c_str() << ": "
  104. << cmSystemTools::ConvertToOutputPath(i->c_str()).c_str()
  105. << std::endl;
  106. }
  107. os << std::endl;
  108. // Write module requirements to the output stream.
  109. for(std::set<cmStdString>::const_iterator i = parser.Requires.begin();
  110. i != parser.Requires.end(); ++i)
  111. {
  112. // Require only modules not provided in the same source.
  113. if(parser.Provides.find(*i) == parser.Provides.end())
  114. {
  115. os << m_TargetFile.c_str() << ": " << i->c_str() << ".mod.stamp"
  116. << std::endl;
  117. os << m_TargetFile.c_str() << ".requires: " << i->c_str() << ".mod.proxy"
  118. << std::endl;
  119. os << i->c_str() << ".mod.proxy:" << std::endl;
  120. std::string stampName = m_Directory;
  121. stampName += "/";
  122. stampName += *i;
  123. stampName += ".mod.stamp";
  124. if(!cmSystemTools::FileExists(stampName.c_str()))
  125. {
  126. std::ofstream stamp(stampName.c_str());
  127. stamp << "# Dummy stamp file in case nothing provides it."
  128. << std::endl;
  129. }
  130. }
  131. }
  132. // Write provided modules to the output stream.
  133. for(std::set<cmStdString>::const_iterator i = parser.Provides.begin();
  134. i != parser.Provides.end(); ++i)
  135. {
  136. os << i->c_str() << ".mod.proxy: " << m_TargetFile.c_str()
  137. << ".requires" << std::endl;
  138. }
  139. // If any modules are provided then they must be converted to stamp files.
  140. if(!parser.Provides.empty())
  141. {
  142. os << m_TargetFile.c_str() << ".provides:\n";
  143. for(std::set<cmStdString>::const_iterator i = parser.Provides.begin();
  144. i != parser.Provides.end(); ++i)
  145. {
  146. os << "\t@$(CMAKE_COMMAND) -E copy_if_different "
  147. << i->c_str() << ".mod " << i->c_str() << ".mod.stamp\n";
  148. }
  149. os << "\t@touch " << m_TargetFile.c_str() << ".provides\n";
  150. }
  151. /*
  152. // TODO:
  153. What about .mod files provided in another directory and found with a
  154. -M search path? The stamp file will not be updated, so things might
  155. not rebuild. Possible solutions (not all thought through):
  156. Solution 1: Have all the .o.requires in a directory depend on a
  157. single .outside.requires that searches for .mod files in another
  158. directory of the build tree and uses copy-if-different to produce
  159. the local directory's stamp files. (won't work because the single
  160. rule cannot know about the modules)
  161. Solution 2: When the dependency is detected search the module
  162. include path for a mark file indicating the module is provided. If
  163. not found just write the dummy stamp file. If found, we need a rule
  164. to copy-if-different the module file. When a module is provided,
  165. write this mark file.
  166. Solution 3: Use a set of make rules like this:
  167. # When required:
  168. foo.mod.proxy: foo.mod.default
  169. foo.mod.default:: foo.mod.hack
  170. @echo foo.mod.default2 # Search for and copy-if-different the mod file.
  171. foo.mod.hack:
  172. # When provided:
  173. foo.mod.proxy: foo.o.requires
  174. @rm -f foo.mod.hack foo.mod.default
  175. foo.o.requires: foo.mod.hack
  176. @echo foo.o.requires
  177. foo.mod.hack:
  178. @touch foo.mod.hack
  179. @touch foo.mod.default
  180. Solution 4:
  181. When scanning dependencies and providing a module:
  182. - Create a .mod.provided.
  183. - Add .mod.proxy rule depending on corresponding .o.requires.
  184. When scanning dependencies and requiring a module:
  185. - Search the module path for a .mod.provided or a .mod.
  186. - If a .mod.provided is found depend on the corresponding .mod.stamp
  187. (it is provided by CMake in another directory)
  188. - Else, if a .mod is found depend on it directly
  189. (it is provided in another directory by a non-CMake project)
  190. - Else:
  191. - Add the empty proxy rule (if it is provided locally this will hook it)
  192. - Depend on a local .mod.stamp (it might be provided locally)
  193. - Create the dummy local .mod.stamp (it might not be provided locally)
  194. */
  195. return true;
  196. }
  197. //----------------------------------------------------------------------------
  198. bool cmDependsFortran::CheckDependencies(std::istream&)
  199. {
  200. // TODO: Parse and check dependencies.
  201. return true;
  202. }
  203. //----------------------------------------------------------------------------
  204. bool cmDependsFortran::FindIncludeFile(const char* dir,
  205. const char* includeName,
  206. std::string& fileName)
  207. {
  208. // If the file is a full path, include it directly.
  209. if(cmSystemTools::FileIsFullPath(includeName))
  210. {
  211. fileName = includeName;
  212. return cmSystemTools::FileExists(fileName.c_str());
  213. }
  214. else
  215. {
  216. // Check for the file in the directory containing the including
  217. // file.
  218. std::string fullName = dir;
  219. fullName += "/";
  220. fullName += includeName;
  221. if(cmSystemTools::FileExists(fullName.c_str()))
  222. {
  223. fileName = fullName;
  224. return true;
  225. }
  226. // Search the include path for the file.
  227. for(std::vector<std::string>::const_iterator i = m_IncludePath->begin();
  228. i != m_IncludePath->end(); ++i)
  229. {
  230. fullName = *i;
  231. fullName += "/";
  232. fullName += includeName;
  233. if(cmSystemTools::FileExists(fullName.c_str()))
  234. {
  235. fileName = fullName;
  236. return true;
  237. }
  238. }
  239. }
  240. return false;
  241. }
  242. //----------------------------------------------------------------------------
  243. cmDependsFortranParser_s::cmDependsFortranParser_s(cmDependsFortran* self):
  244. Self(self)
  245. {
  246. this->InInterface = 0;
  247. // Initialize the lexical scanner.
  248. cmDependsFortran_yylex_init(&this->Scanner);
  249. cmDependsFortran_yyset_extra(this, this->Scanner);
  250. // Create a dummy buffer that is never read but is the fallback
  251. // buffer when the last file is popped off the stack.
  252. YY_BUFFER_STATE buffer =
  253. cmDependsFortran_yy_create_buffer(0, 4, this->Scanner);
  254. cmDependsFortran_yy_switch_to_buffer(buffer, this->Scanner);
  255. }
  256. //----------------------------------------------------------------------------
  257. cmDependsFortranParser_s::~cmDependsFortranParser_s()
  258. {
  259. cmDependsFortran_yylex_destroy(this->Scanner);
  260. }
  261. //----------------------------------------------------------------------------
  262. int cmDependsFortranParser_FilePush(cmDependsFortranParser* parser,
  263. const char* fname)
  264. {
  265. // Open the new file and push it onto the stack. Save the old
  266. // buffer with it on the stack.
  267. if(FILE* file = fopen(fname, "rb"))
  268. {
  269. YY_BUFFER_STATE current =
  270. cmDependsFortranLexer_GetCurrentBuffer(parser->Scanner);
  271. std::string dir = cmSystemTools::GetParentDirectory(fname);
  272. cmDependsFortranFile f(file, current, dir);
  273. YY_BUFFER_STATE buffer =
  274. cmDependsFortran_yy_create_buffer(0, 16384, parser->Scanner);
  275. cmDependsFortran_yy_switch_to_buffer(buffer, parser->Scanner);
  276. parser->FileStack.push(f);
  277. return 1;
  278. }
  279. else
  280. {
  281. return 0;
  282. }
  283. }
  284. //----------------------------------------------------------------------------
  285. int cmDependsFortranParser_FilePop(cmDependsFortranParser* parser)
  286. {
  287. // Pop one file off the stack and close it. Switch the lexer back
  288. // to the next one on the stack.
  289. if(parser->FileStack.empty())
  290. {
  291. return 0;
  292. }
  293. else
  294. {
  295. cmDependsFortranFile f = parser->FileStack.top(); parser->FileStack.pop();
  296. fclose(f.File);
  297. YY_BUFFER_STATE current =
  298. cmDependsFortranLexer_GetCurrentBuffer(parser->Scanner);
  299. cmDependsFortran_yy_delete_buffer(current, parser->Scanner);
  300. cmDependsFortran_yy_switch_to_buffer(f.Buffer, parser->Scanner);
  301. return 1;
  302. }
  303. }
  304. //----------------------------------------------------------------------------
  305. int cmDependsFortranParser_Input(cmDependsFortranParser* parser,
  306. char* buffer, size_t bufferSize)
  307. {
  308. // Read from the file on top of the stack. If the stack is empty,
  309. // the end of the translation unit has been reached.
  310. if(!parser->FileStack.empty())
  311. {
  312. FILE* file = parser->FileStack.top().File;
  313. return (int)fread(buffer, 1, bufferSize, file);
  314. }
  315. return 0;
  316. }
  317. //----------------------------------------------------------------------------
  318. void cmDependsFortranParser_StringStart(cmDependsFortranParser* parser)
  319. {
  320. parser->TokenString = "";
  321. }
  322. //----------------------------------------------------------------------------
  323. const char* cmDependsFortranParser_StringEnd(cmDependsFortranParser* parser)
  324. {
  325. return parser->TokenString.c_str();
  326. }
  327. //----------------------------------------------------------------------------
  328. void cmDependsFortranParser_StringAppend(cmDependsFortranParser* parser,
  329. char c)
  330. {
  331. parser->TokenString += c;
  332. }
  333. //----------------------------------------------------------------------------
  334. void cmDependsFortranParser_SetInInterface(cmDependsFortranParser* parser,
  335. int in)
  336. {
  337. parser->InInterface = in;
  338. }
  339. //----------------------------------------------------------------------------
  340. int cmDependsFortranParser_GetInInterface(cmDependsFortranParser* parser)
  341. {
  342. return parser->InInterface;
  343. }
  344. //----------------------------------------------------------------------------
  345. void cmDependsFortranParser_Error(cmDependsFortranParser*, const char*)
  346. {
  347. // If there is a parser error just ignore it. The source will not
  348. // compile and the user will edit it. Then dependencies will have
  349. // to be regenerated anyway.
  350. }
  351. //----------------------------------------------------------------------------
  352. void cmDependsFortranParser_RuleUse(cmDependsFortranParser* parser,
  353. const char* name)
  354. {
  355. parser->Requires.insert(name);
  356. }
  357. //----------------------------------------------------------------------------
  358. void cmDependsFortranParser_RuleInclude(cmDependsFortranParser* parser,
  359. const char* name)
  360. {
  361. // If processing an include statement there must be an open file.
  362. assert(!parser->FileStack.empty());
  363. // Get the directory containing the source in which the include
  364. // statement appears. This is always the first search location for
  365. // Fortran include files.
  366. std::string dir = parser->FileStack.top().Directory;
  367. // Find the included file. If it cannot be found just ignore the
  368. // problem because either the source will not compile or the user
  369. // does not care about depending on this included source.
  370. std::string fullName;
  371. if(parser->Self->FindIncludeFile(dir.c_str(), name, fullName))
  372. {
  373. // Found the included file. Save it in the set of included files.
  374. parser->Includes.insert(fullName);
  375. // Parse it immediately to translate the source inline.
  376. cmDependsFortranParser_FilePush(parser, fullName.c_str());
  377. }
  378. }
  379. //----------------------------------------------------------------------------
  380. void cmDependsFortranParser_RuleModule(cmDependsFortranParser* parser,
  381. const char* name)
  382. {
  383. parser->Provides.insert(name);
  384. }
  385. //----------------------------------------------------------------------------
  386. void cmDependsFortranParser_RuleDefine(cmDependsFortranParser*, const char*)
  387. {
  388. }
  389. //----------------------------------------------------------------------------
  390. void cmDependsFortranParser_RuleUndef(cmDependsFortranParser*, const char*)
  391. {
  392. }
  393. //----------------------------------------------------------------------------
  394. void cmDependsFortranParser_RuleIfdef(cmDependsFortranParser*, const char*)
  395. {
  396. }
  397. //----------------------------------------------------------------------------
  398. void cmDependsFortranParser_RuleIfndef(cmDependsFortranParser*, const char*)
  399. {
  400. }
  401. //----------------------------------------------------------------------------
  402. void cmDependsFortranParser_RuleIf(cmDependsFortranParser*)
  403. {
  404. }
  405. //----------------------------------------------------------------------------
  406. void cmDependsFortranParser_RuleElif(cmDependsFortranParser*)
  407. {
  408. }
  409. //----------------------------------------------------------------------------
  410. void cmDependsFortranParser_RuleElse(cmDependsFortranParser*)
  411. {
  412. }
  413. //----------------------------------------------------------------------------
  414. void cmDependsFortranParser_RuleEndif(cmDependsFortranParser*)
  415. {
  416. }