cmDependsFortran.cxx 24 KB

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