cmDependsFortran.cxx 17 KB

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