cmDependsFortran.cxx 25 KB

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