cmDependsFortran.cxx 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "cmDependsFortran.h"
  11. #include "cmSystemTools.h"
  12. #include "cmLocalGenerator.h"
  13. #include "cmMakefile.h"
  14. #include "cmGeneratedFileStream.h"
  15. #include "cmFortranParser.h" /* Interface to parser object. */
  16. #include <cmsys/FStream.hxx>
  17. #include <assert.h>
  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. class cmDependsFortranInternals
  23. {
  24. public:
  25. // The set of modules provided by this target.
  26. std::set<std::string> TargetProvides;
  27. // Map modules required by this target to locations.
  28. typedef std::map<std::string, std::string> TargetRequiresMap;
  29. TargetRequiresMap TargetRequires;
  30. // Information about each object file.
  31. typedef std::map<std::string, cmFortranSourceInfo> ObjectInfoMap;
  32. ObjectInfoMap ObjectInfo;
  33. cmFortranSourceInfo& CreateObjectInfo(const char* obj,
  34. const char* src)
  35. {
  36. std::map<std::string, cmFortranSourceInfo>::iterator i =
  37. this->ObjectInfo.find(obj);
  38. if(i == this->ObjectInfo.end())
  39. {
  40. std::map<std::string, cmFortranSourceInfo>::value_type
  41. entry(obj, cmFortranSourceInfo());
  42. i = this->ObjectInfo.insert(entry).first;
  43. i->second.Source = src;
  44. }
  45. return i->second;
  46. }
  47. };
  48. //----------------------------------------------------------------------------
  49. cmDependsFortran::cmDependsFortran():
  50. Internal(0)
  51. {
  52. }
  53. //----------------------------------------------------------------------------
  54. cmDependsFortran
  55. ::cmDependsFortran(cmLocalGenerator* lg):
  56. cmDepends(lg),
  57. Internal(new cmDependsFortranInternals)
  58. {
  59. // Configure the include file search path.
  60. this->SetIncludePathFromLanguage("Fortran");
  61. // Get the list of definitions.
  62. std::vector<std::string> definitions;
  63. cmMakefile* mf = this->LocalGenerator->GetMakefile();
  64. if(const char* c_defines =
  65. mf->GetDefinition("CMAKE_TARGET_DEFINITIONS_Fortran"))
  66. {
  67. cmSystemTools::ExpandListArgument(c_defines, definitions);
  68. }
  69. // translate i.e. FOO=BAR to FOO and add it to the list of defined
  70. // preprocessor symbols
  71. for(std::vector<std::string>::const_iterator
  72. it = definitions.begin(); it != definitions.end(); ++it)
  73. {
  74. std::string def = *it;
  75. std::string::size_type assignment = def.find("=");
  76. if(assignment != std::string::npos)
  77. {
  78. def = it->substr(0, assignment);
  79. }
  80. this->PPDefinitions.insert(def);
  81. }
  82. }
  83. //----------------------------------------------------------------------------
  84. cmDependsFortran::~cmDependsFortran()
  85. {
  86. delete this->Internal;
  87. }
  88. //----------------------------------------------------------------------------
  89. bool cmDependsFortran::WriteDependencies(
  90. const std::set<std::string>& sources, const std::string& obj,
  91. std::ostream&, std::ostream&)
  92. {
  93. // Make sure this is a scanning instance.
  94. if(sources.empty() || sources.begin()->empty())
  95. {
  96. cmSystemTools::Error("Cannot scan dependencies without a source file.");
  97. return false;
  98. }
  99. if(obj.empty())
  100. {
  101. cmSystemTools::Error("Cannot scan dependencies without an object file.");
  102. return false;
  103. }
  104. bool okay = true;
  105. for(std::set<std::string>::const_iterator it = sources.begin();
  106. it != sources.end(); ++it)
  107. {
  108. const std::string& src = *it;
  109. // Get the information object for this source.
  110. cmFortranSourceInfo& info =
  111. this->Internal->CreateObjectInfo(obj.c_str(), src.c_str());
  112. // Create the parser object. The constructor takes info by reference,
  113. // so we may look into the resulting objects later.
  114. cmFortranParser parser(this->IncludePath, this->PPDefinitions, info);
  115. // Push on the starting file.
  116. cmFortranParser_FilePush(&parser, src.c_str());
  117. // Parse the translation unit.
  118. if(cmFortran_yyparse(parser.Scanner) != 0)
  119. {
  120. // Failed to parse the file. Report failure to write dependencies.
  121. okay = false;
  122. }
  123. }
  124. return okay;
  125. }
  126. //----------------------------------------------------------------------------
  127. bool cmDependsFortran::Finalize(std::ostream& makeDepends,
  128. std::ostream& internalDepends)
  129. {
  130. // Prepare the module search process.
  131. this->LocateModules();
  132. // Get the directory in which stamp files will be stored.
  133. const char* stamp_dir = this->TargetDirectory.c_str();
  134. // Get the directory in which module files will be created.
  135. cmMakefile* mf = this->LocalGenerator->GetMakefile();
  136. std::string mod_dir =
  137. mf->GetSafeDefinition("CMAKE_Fortran_TARGET_MODULE_DIR");
  138. if (mod_dir.empty())
  139. {
  140. mod_dir =
  141. this->LocalGenerator->GetCurrentBinaryDirectory();
  142. }
  143. // Actually write dependencies to the streams.
  144. typedef cmDependsFortranInternals::ObjectInfoMap ObjectInfoMap;
  145. ObjectInfoMap const& objInfo = this->Internal->ObjectInfo;
  146. for(ObjectInfoMap::const_iterator i = objInfo.begin();
  147. i != objInfo.end(); ++i)
  148. {
  149. if(!this->WriteDependenciesReal(i->first.c_str(), i->second,
  150. mod_dir, stamp_dir,
  151. makeDepends, internalDepends))
  152. {
  153. return false;
  154. }
  155. }
  156. // Store the list of modules provided by this target.
  157. std::string fiName = this->TargetDirectory;
  158. fiName += "/fortran.internal";
  159. cmGeneratedFileStream fiStream(fiName.c_str());
  160. fiStream << "# The fortran modules provided by this target.\n";
  161. fiStream << "provides\n";
  162. std::set<std::string> const& provides = this->Internal->TargetProvides;
  163. for(std::set<std::string>::const_iterator i = provides.begin();
  164. i != provides.end(); ++i)
  165. {
  166. fiStream << " " << *i << "\n";
  167. }
  168. // Create a script to clean the modules.
  169. if(!provides.empty())
  170. {
  171. std::string fcName = this->TargetDirectory;
  172. fcName += "/cmake_clean_Fortran.cmake";
  173. cmGeneratedFileStream fcStream(fcName.c_str());
  174. fcStream << "# Remove fortran modules provided by this target.\n";
  175. fcStream << "FILE(REMOVE";
  176. for(std::set<std::string>::const_iterator i = provides.begin();
  177. i != provides.end(); ++i)
  178. {
  179. std::string mod_upper = mod_dir;
  180. mod_upper += "/";
  181. mod_upper += cmSystemTools::UpperCase(*i);
  182. mod_upper += ".mod";
  183. std::string mod_lower = mod_dir;
  184. mod_lower += "/";
  185. mod_lower += *i;
  186. mod_lower += ".mod";
  187. std::string stamp = stamp_dir;
  188. stamp += "/";
  189. stamp += *i;
  190. stamp += ".mod.stamp";
  191. fcStream << "\n";
  192. fcStream << " \"" <<
  193. this->LocalGenerator->Convert(mod_lower,
  194. cmLocalGenerator::START_OUTPUT)
  195. << "\"\n";
  196. fcStream << " \"" <<
  197. this->LocalGenerator->Convert(mod_upper,
  198. cmLocalGenerator::START_OUTPUT)
  199. << "\"\n";
  200. fcStream << " \"" <<
  201. this->LocalGenerator->Convert(stamp,
  202. cmLocalGenerator::START_OUTPUT)
  203. << "\"\n";
  204. }
  205. fcStream << " )\n";
  206. }
  207. return true;
  208. }
  209. //----------------------------------------------------------------------------
  210. void cmDependsFortran::LocateModules()
  211. {
  212. // Collect the set of modules provided and required by all sources.
  213. typedef cmDependsFortranInternals::ObjectInfoMap ObjectInfoMap;
  214. ObjectInfoMap const& objInfo = this->Internal->ObjectInfo;
  215. for(ObjectInfoMap::const_iterator infoI = objInfo.begin();
  216. infoI != objInfo.end(); ++infoI)
  217. {
  218. cmFortranSourceInfo const& info = infoI->second;
  219. // Include this module in the set provided by this target.
  220. this->Internal->TargetProvides.insert(info.Provides.begin(),
  221. info.Provides.end());
  222. for(std::set<std::string>::const_iterator i = info.Requires.begin();
  223. i != info.Requires.end(); ++i)
  224. {
  225. this->Internal->TargetRequires[*i] = "";
  226. }
  227. }
  228. // Short-circuit for simple targets.
  229. if(this->Internal->TargetRequires.empty())
  230. {
  231. return;
  232. }
  233. // Match modules provided by this target to those it requires.
  234. this->MatchLocalModules();
  235. // Load information about other targets.
  236. cmMakefile* mf = this->LocalGenerator->GetMakefile();
  237. std::vector<std::string> infoFiles;
  238. if(const char* infoFilesValue =
  239. mf->GetDefinition("CMAKE_TARGET_LINKED_INFO_FILES"))
  240. {
  241. cmSystemTools::ExpandListArgument(infoFilesValue, infoFiles);
  242. }
  243. for(std::vector<std::string>::const_iterator i = infoFiles.begin();
  244. i != infoFiles.end(); ++i)
  245. {
  246. std::string targetDir = cmSystemTools::GetFilenamePath(*i);
  247. std::string fname = targetDir + "/fortran.internal";
  248. cmsys::ifstream fin(fname.c_str());
  249. if(fin)
  250. {
  251. this->MatchRemoteModules(fin, targetDir.c_str());
  252. }
  253. }
  254. }
  255. //----------------------------------------------------------------------------
  256. void cmDependsFortran::MatchLocalModules()
  257. {
  258. const char* stampDir = this->TargetDirectory.c_str();
  259. std::set<std::string> const& provides = this->Internal->TargetProvides;
  260. for(std::set<std::string>::const_iterator i = provides.begin();
  261. i != provides.end(); ++i)
  262. {
  263. this->ConsiderModule(i->c_str(), stampDir);
  264. }
  265. }
  266. //----------------------------------------------------------------------------
  267. void cmDependsFortran::MatchRemoteModules(std::istream& fin,
  268. const char* stampDir)
  269. {
  270. std::string line;
  271. bool doing_provides = false;
  272. while(cmSystemTools::GetLineFromStream(fin, line))
  273. {
  274. // Ignore comments and empty lines.
  275. if(line.empty() || line[0] == '#' || line[0] == '\r')
  276. {
  277. continue;
  278. }
  279. if(line[0] == ' ')
  280. {
  281. if(doing_provides)
  282. {
  283. this->ConsiderModule(line.c_str()+1, stampDir);
  284. }
  285. }
  286. else if(line == "provides")
  287. {
  288. doing_provides = true;
  289. }
  290. else
  291. {
  292. doing_provides = false;
  293. }
  294. }
  295. }
  296. //----------------------------------------------------------------------------
  297. void cmDependsFortran::ConsiderModule(const char* name,
  298. const char* stampDir)
  299. {
  300. // Locate each required module.
  301. typedef cmDependsFortranInternals::TargetRequiresMap TargetRequiresMap;
  302. TargetRequiresMap::iterator required =
  303. this->Internal->TargetRequires.find(name);
  304. if(required != this->Internal->TargetRequires.end() &&
  305. required->second.empty())
  306. {
  307. // The module is provided by a CMake target. It will have a stamp file.
  308. std::string stampFile = stampDir;
  309. stampFile += "/";
  310. stampFile += name;
  311. stampFile += ".mod.stamp";
  312. required->second = stampFile;
  313. }
  314. }
  315. //----------------------------------------------------------------------------
  316. bool
  317. cmDependsFortran
  318. ::WriteDependenciesReal(const char *obj,
  319. cmFortranSourceInfo const& info,
  320. std::string const& mod_dir,
  321. const char* stamp_dir,
  322. std::ostream& makeDepends,
  323. std::ostream& internalDepends)
  324. {
  325. typedef cmDependsFortranInternals::TargetRequiresMap TargetRequiresMap;
  326. // Get the source file for this object.
  327. const char* src = info.Source.c_str();
  328. // Write the include dependencies to the output stream.
  329. std::string obj_i =
  330. this->LocalGenerator->Convert(obj, cmLocalGenerator::HOME_OUTPUT);
  331. std::string obj_m =
  332. this->LocalGenerator->ConvertToOutputFormat(obj_i,
  333. cmLocalGenerator::MAKERULE);
  334. internalDepends << obj_i << std::endl;
  335. internalDepends << " " << src << std::endl;
  336. for(std::set<std::string>::const_iterator i = info.Includes.begin();
  337. i != info.Includes.end(); ++i)
  338. {
  339. makeDepends << obj_m << ": " <<
  340. this->LocalGenerator->Convert(*i,
  341. cmLocalGenerator::HOME_OUTPUT,
  342. cmLocalGenerator::MAKERULE)
  343. << std::endl;
  344. internalDepends << " " << *i << std::endl;
  345. }
  346. makeDepends << std::endl;
  347. // Write module requirements to the output stream.
  348. for(std::set<std::string>::const_iterator i = info.Requires.begin();
  349. i != info.Requires.end(); ++i)
  350. {
  351. // Require only modules not provided in the same source.
  352. if(std::set<std::string>::const_iterator(info.Provides.find(*i)) !=
  353. info.Provides.end())
  354. {
  355. continue;
  356. }
  357. // If the module is provided in this target special handling is
  358. // needed.
  359. if(this->Internal->TargetProvides.find(*i) !=
  360. this->Internal->TargetProvides.end())
  361. {
  362. // The module is provided by a different source in the same
  363. // target. Add the proxy dependency to make sure the other
  364. // source builds first.
  365. std::string proxy = stamp_dir;
  366. proxy += "/";
  367. proxy += *i;
  368. proxy += ".mod.proxy";
  369. proxy = this->LocalGenerator->Convert(proxy,
  370. cmLocalGenerator::HOME_OUTPUT,
  371. cmLocalGenerator::MAKERULE);
  372. // since we require some things add them to our list of requirements
  373. makeDepends << obj_m << ".requires: " << proxy << std::endl;
  374. }
  375. // The object file should depend on timestamped files for the
  376. // modules it uses.
  377. TargetRequiresMap::const_iterator required =
  378. this->Internal->TargetRequires.find(*i);
  379. if(required == this->Internal->TargetRequires.end()) { abort(); }
  380. if(!required->second.empty())
  381. {
  382. // This module is known. Depend on its timestamp file.
  383. std::string stampFile =
  384. this->LocalGenerator->Convert(required->second,
  385. cmLocalGenerator::HOME_OUTPUT,
  386. cmLocalGenerator::MAKERULE);
  387. makeDepends << obj_m << ": " << stampFile << "\n";
  388. }
  389. else
  390. {
  391. // This module is not known to CMake. Try to locate it where
  392. // the compiler will and depend on that.
  393. std::string module;
  394. if(this->FindModule(*i, module))
  395. {
  396. module =
  397. this->LocalGenerator->Convert(module,
  398. cmLocalGenerator::HOME_OUTPUT,
  399. cmLocalGenerator::MAKERULE);
  400. makeDepends << obj_m << ": " << module << "\n";
  401. }
  402. }
  403. }
  404. // Write provided modules to the output stream.
  405. for(std::set<std::string>::const_iterator i = info.Provides.begin();
  406. i != info.Provides.end(); ++i)
  407. {
  408. std::string proxy = stamp_dir;
  409. proxy += "/";
  410. proxy += *i;
  411. proxy += ".mod.proxy";
  412. proxy = this->LocalGenerator->Convert(proxy,
  413. cmLocalGenerator::HOME_OUTPUT,
  414. cmLocalGenerator::MAKERULE);
  415. makeDepends << proxy << ": " << obj_m << ".provides" << std::endl;
  416. }
  417. // If any modules are provided then they must be converted to stamp files.
  418. if(!info.Provides.empty())
  419. {
  420. // Create a target to copy the module after the object file
  421. // changes.
  422. makeDepends << obj_m << ".provides.build:\n";
  423. for(std::set<std::string>::const_iterator i = info.Provides.begin();
  424. i != info.Provides.end(); ++i)
  425. {
  426. // Include this module in the set provided by this target.
  427. this->Internal->TargetProvides.insert(*i);
  428. // Always use lower case for the mod stamp file name. The
  429. // cmake_copy_f90_mod will call back to this class, which will
  430. // try various cases for the real mod file name.
  431. std::string m = cmSystemTools::LowerCase(*i);
  432. std::string modFile = mod_dir;
  433. modFile += "/";
  434. modFile += *i;
  435. modFile =
  436. this->LocalGenerator->Convert(modFile,
  437. cmLocalGenerator::HOME_OUTPUT,
  438. cmLocalGenerator::SHELL);
  439. std::string stampFile = stamp_dir;
  440. stampFile += "/";
  441. stampFile += m;
  442. stampFile += ".mod.stamp";
  443. stampFile =
  444. this->LocalGenerator->Convert(stampFile,
  445. cmLocalGenerator::HOME_OUTPUT,
  446. cmLocalGenerator::SHELL);
  447. makeDepends << "\t$(CMAKE_COMMAND) -E cmake_copy_f90_mod "
  448. << modFile << " " << stampFile;
  449. cmMakefile* mf = this->LocalGenerator->GetMakefile();
  450. const char* cid = mf->GetDefinition("CMAKE_Fortran_COMPILER_ID");
  451. if(cid && *cid)
  452. {
  453. makeDepends << " " << cid;
  454. }
  455. makeDepends << "\n";
  456. }
  457. // After copying the modules update the timestamp file so that
  458. // copying will not be done again until the source rebuilds.
  459. makeDepends << "\t$(CMAKE_COMMAND) -E touch " << obj_m
  460. << ".provides.build\n";
  461. // Make sure the module timestamp rule is evaluated by the time
  462. // the target finishes building.
  463. std::string driver = this->TargetDirectory;
  464. driver += "/build";
  465. driver = this->LocalGenerator->Convert(driver,
  466. cmLocalGenerator::HOME_OUTPUT,
  467. cmLocalGenerator::MAKERULE);
  468. makeDepends << driver << ": " << obj_m << ".provides.build\n";
  469. }
  470. return true;
  471. }
  472. //----------------------------------------------------------------------------
  473. bool cmDependsFortran::FindModule(std::string const& name,
  474. std::string& module)
  475. {
  476. // Construct possible names for the module file.
  477. std::string mod_upper = cmSystemTools::UpperCase(name);
  478. std::string mod_lower = name;
  479. mod_upper += ".mod";
  480. mod_lower += ".mod";
  481. // Search the include path for the module.
  482. std::string fullName;
  483. for(std::vector<std::string>::const_iterator i =
  484. this->IncludePath.begin(); i != this->IncludePath.end(); ++i)
  485. {
  486. // Try the lower-case name.
  487. fullName = *i;
  488. fullName += "/";
  489. fullName += mod_lower;
  490. if(cmSystemTools::FileExists(fullName.c_str(), true))
  491. {
  492. module = fullName;
  493. return true;
  494. }
  495. // Try the upper-case name.
  496. fullName = *i;
  497. fullName += "/";
  498. fullName += mod_upper;
  499. if(cmSystemTools::FileExists(fullName.c_str(), true))
  500. {
  501. module = fullName;
  502. return true;
  503. }
  504. }
  505. return false;
  506. }
  507. //----------------------------------------------------------------------------
  508. bool cmDependsFortran::CopyModule(const std::vector<std::string>& args)
  509. {
  510. // Implements
  511. //
  512. // $(CMAKE_COMMAND) -E cmake_copy_f90_mod input.mod output.mod.stamp
  513. // [compiler-id]
  514. //
  515. // Note that the case of the .mod file depends on the compiler. In
  516. // the future this copy could also account for the fact that some
  517. // compilers include a timestamp in the .mod file so it changes even
  518. // when the interface described in the module does not.
  519. std::string mod = args[2];
  520. std::string stamp = args[3];
  521. std::string compilerId;
  522. if(args.size() >= 5)
  523. {
  524. compilerId = args[4];
  525. }
  526. std::string mod_dir = cmSystemTools::GetFilenamePath(mod);
  527. if(!mod_dir.empty()) { mod_dir += "/"; }
  528. std::string mod_upper = mod_dir;
  529. mod_upper += cmSystemTools::UpperCase(cmSystemTools::GetFilenameName(mod));
  530. std::string mod_lower = mod_dir;
  531. mod_lower += cmSystemTools::LowerCase(cmSystemTools::GetFilenameName(mod));
  532. mod += ".mod";
  533. mod_upper += ".mod";
  534. mod_lower += ".mod";
  535. if(cmSystemTools::FileExists(mod_upper.c_str(), true))
  536. {
  537. if(cmDependsFortran::ModulesDiffer(mod_upper.c_str(), stamp.c_str(),
  538. compilerId.c_str()))
  539. {
  540. if(!cmSystemTools::CopyFileAlways(mod_upper, stamp))
  541. {
  542. std::cerr << "Error copying Fortran module from \""
  543. << mod_upper << "\" to \"" << stamp
  544. << "\".\n";
  545. return false;
  546. }
  547. }
  548. return true;
  549. }
  550. else if(cmSystemTools::FileExists(mod_lower.c_str(), true))
  551. {
  552. if(cmDependsFortran::ModulesDiffer(mod_lower.c_str(), stamp.c_str(),
  553. compilerId.c_str()))
  554. {
  555. if(!cmSystemTools::CopyFileAlways(mod_lower, stamp))
  556. {
  557. std::cerr << "Error copying Fortran module from \""
  558. << mod_lower << "\" to \"" << stamp
  559. << "\".\n";
  560. return false;
  561. }
  562. }
  563. return true;
  564. }
  565. std::cerr << "Error copying Fortran module \"" << args[2]
  566. << "\". Tried \"" << mod_upper
  567. << "\" and \"" << mod_lower << "\".\n";
  568. return false;
  569. }
  570. //----------------------------------------------------------------------------
  571. // Helper function to look for a short sequence in a stream. If this
  572. // is later used for longer sequences it should be re-written using an
  573. // efficient string search algorithm such as Boyer-Moore.
  574. static
  575. bool cmFortranStreamContainsSequence(std::istream& ifs,
  576. const char* seq, int len)
  577. {
  578. assert(len > 0);
  579. int cur = 0;
  580. while(cur < len)
  581. {
  582. // Get the next character.
  583. int token = ifs.get();
  584. if(!ifs)
  585. {
  586. return false;
  587. }
  588. // Check the character.
  589. if(token == static_cast<int>(seq[cur]))
  590. {
  591. ++cur;
  592. }
  593. else
  594. {
  595. // Assume the sequence has no repeating subsequence.
  596. cur = 0;
  597. }
  598. }
  599. // The entire sequence was matched.
  600. return true;
  601. }
  602. //----------------------------------------------------------------------------
  603. // Helper function to compare the remaining content in two streams.
  604. static bool cmFortranStreamsDiffer(std::istream& ifs1,
  605. std::istream& ifs2)
  606. {
  607. // Compare the remaining content.
  608. for(;;)
  609. {
  610. int ifs1_c = ifs1.get();
  611. int ifs2_c = ifs2.get();
  612. if(!ifs1 && !ifs2)
  613. {
  614. // We have reached the end of both streams simultaneously.
  615. // The streams are identical.
  616. return false;
  617. }
  618. if(!ifs1 || !ifs2 || ifs1_c != ifs2_c)
  619. {
  620. // We have reached the end of one stream before the other or
  621. // found differing content. The streams are different.
  622. break;
  623. }
  624. }
  625. return true;
  626. }
  627. //----------------------------------------------------------------------------
  628. bool cmDependsFortran::ModulesDiffer(const char* modFile,
  629. const char* stampFile,
  630. const char* compilerId)
  631. {
  632. /*
  633. gnu >= 4.9:
  634. A mod file is an ascii file compressed with gzip.
  635. Compiling twice produces identical modules.
  636. gnu < 4.9:
  637. A mod file is an ascii file.
  638. <bar.mod>
  639. FORTRAN module created from /path/to/foo.f90 on Sun Dec 30 22:47:58 2007
  640. If you edit this, you'll get what you deserve.
  641. ...
  642. </bar.mod>
  643. As you can see the first line contains the date.
  644. intel:
  645. A mod file is a binary file.
  646. However, looking into both generated bar.mod files with a hex editor
  647. shows that they differ only before a sequence linefeed-zero (0x0A 0x00)
  648. which is located some bytes in front of the absoulte path to the source
  649. file.
  650. sun:
  651. A mod file is a binary file. Compiling twice produces identical modules.
  652. others:
  653. TODO ...
  654. */
  655. /* Compilers which do _not_ produce different mod content when the same
  656. * source is compiled twice
  657. * -SunPro
  658. */
  659. if(strcmp(compilerId, "SunPro") == 0)
  660. {
  661. return cmSystemTools::FilesDiffer(modFile, stampFile);
  662. }
  663. #if defined(_WIN32) || defined(__CYGWIN__)
  664. cmsys::ifstream finModFile(modFile, std::ios::in | std::ios::binary);
  665. cmsys::ifstream finStampFile(stampFile, std::ios::in | std::ios::binary);
  666. #else
  667. cmsys::ifstream finModFile(modFile, std::ios::in);
  668. cmsys::ifstream finStampFile(stampFile, std::ios::in);
  669. #endif
  670. if(!finModFile || !finStampFile)
  671. {
  672. // At least one of the files does not exist. The modules differ.
  673. return true;
  674. }
  675. /* Compilers which _do_ produce different mod content when the same
  676. * source is compiled twice
  677. * -GNU
  678. * -Intel
  679. *
  680. * Eat the stream content until all recompile only related changes
  681. * are left behind.
  682. */
  683. if (strcmp(compilerId, "GNU") == 0 )
  684. {
  685. // GNU Fortran 4.9 and later compress .mod files with gzip
  686. // but also do not include a date so we can fall through to
  687. // compare them without skipping any prefix.
  688. unsigned char hdr[2];
  689. bool okay = finModFile.read(reinterpret_cast<char*>(hdr), 2)? true:false;
  690. finModFile.seekg(0);
  691. if(!(okay && hdr[0] == 0x1f && hdr[1] == 0x8b))
  692. {
  693. const char seq[1] = {'\n'};
  694. const int seqlen = 1;
  695. if(!cmFortranStreamContainsSequence(finModFile, seq, seqlen))
  696. {
  697. // The module is of unexpected format. Assume it is different.
  698. std::cerr << compilerId << " fortran module " << modFile
  699. << " has unexpected format." << std::endl;
  700. return true;
  701. }
  702. if(!cmFortranStreamContainsSequence(finStampFile, seq, seqlen))
  703. {
  704. // The stamp must differ if the sequence is not contained.
  705. return true;
  706. }
  707. }
  708. }
  709. else if(strcmp(compilerId, "Intel") == 0)
  710. {
  711. const char seq[2] = {'\n', '\0'};
  712. const int seqlen = 2;
  713. if(!cmFortranStreamContainsSequence(finModFile, seq, seqlen))
  714. {
  715. // The module is of unexpected format. Assume it is different.
  716. std::cerr << compilerId << " fortran module " << modFile
  717. << " has unexpected format." << std::endl;
  718. return true;
  719. }
  720. if(!cmFortranStreamContainsSequence(finStampFile, seq, seqlen))
  721. {
  722. // The stamp must differ if the sequence is not contained.
  723. return true;
  724. }
  725. }
  726. // Compare the remaining content. If no compiler id matched above,
  727. // including the case none was given, this will compare the whole
  728. // content.
  729. if(!cmFortranStreamsDiffer(finModFile, finStampFile))
  730. {
  731. return false;
  732. }
  733. // The modules are different.
  734. return true;
  735. }