diff --git a/Source/cmDependsFortran.cxx b/Source/cmDependsFortran.cxx index aeb37e75a..2dd40e251 100644 --- a/Source/cmDependsFortran.cxx +++ b/Source/cmDependsFortran.cxx @@ -619,23 +619,29 @@ bool cmDependsFortran::CopyModule(const std::vector& args) mod_lower += ".mod"; if(cmSystemTools::FileExists(mod_upper.c_str(), true)) { - if(!cmSystemTools::CopyFileIfDifferent(mod_upper.c_str(), stamp.c_str())) + if(cmDependsFortran::ModulesDiffer(mod_upper.c_str(), stamp.c_str())) { - std::cerr << "Error copying Fortran module from \"" - << mod_upper.c_str() << "\" to \"" << stamp.c_str() - << "\".\n"; - return false; + if(!cmSystemTools::CopyFileAlways(mod_upper.c_str(), stamp.c_str())) + { + std::cerr << "Error copying Fortran module from \"" + << mod_upper.c_str() << "\" to \"" << stamp.c_str() + << "\".\n"; + return false; + } } return true; } else if(cmSystemTools::FileExists(mod_lower.c_str(), true)) { - if(!cmSystemTools::CopyFileIfDifferent(mod_lower.c_str(), stamp.c_str())) + if(cmDependsFortran::ModulesDiffer(mod_lower.c_str(), stamp.c_str())) { - std::cerr << "Error copying Fortran module from \"" - << mod_lower.c_str() << "\" to \"" << stamp.c_str() - << "\".\n"; - return false; + if(!cmSystemTools::CopyFileAlways(mod_lower.c_str(), stamp.c_str())) + { + std::cerr << "Error copying Fortran module from \"" + << mod_lower.c_str() << "\" to \"" << stamp.c_str() + << "\".\n"; + return false; + } } return true; } @@ -646,6 +652,102 @@ bool cmDependsFortran::CopyModule(const std::vector& args) return false; } +//---------------------------------------------------------------------------- +bool cmDependsFortran::ModulesDiffer(const char* modFile, + const char* stampFile) +{ + /* + This following is valid for intel and gnu. For others this may be valid + too, but has to be proven. + + ASSUMPTION: If one compiles the source foo.f90 which provides module bar, + two times then both generated bar.mod files will differ only before the + first linefeed (\n or 0x0A). + + gnu: + A mod file is an ascii file. + + FORTRAN module created from /path/to/foo.f90 on Sun Dec 30 22:47:58 2007 + If you edit this, you'll get what you deserve. + ... + + As you can see the first line contains the date. + + intel: + A mod file is a binary file. + However, looking into both generated bar.mod files with a hex editor + shows that they differ only before a linefeed (0x0A) which is located + some bytes in front of the absoulte path to the source file. + + sun: + A mod file is a binary file. It always starts in the line of text + ! + Compiling twice produces identical modules anyway, so the + assumption is valid. + + others: + TODO: is this valid for every common fortran compiler? + */ +#if defined(_WIN32) || defined(__CYGWIN__) + std::ifstream finModFile(modFile, std::ios::in | std::ios::binary); + std::ifstream finStampFile(stampFile, std::ios::in | std::ios::binary); +#else + std::ifstream finModFile(modFile, std::ios::in); + std::ifstream finStampFile(stampFile, std::ios::in); +#endif + if(!finModFile || !finStampFile) + { + // At least one of the files does not exist. The modules differ. + return true; + } + + // Remove the first line from each file and make sure newlines were found. + std::string modLine; + bool modHasNewline = false; + if(!cmSystemTools::GetLineFromStream(finModFile, modLine, + &modHasNewline, 1024) || + !modHasNewline) + { + std::cerr + << "WARNING in cmDependsFortran::ModulesDiffer:\n" + << "The fortran module \"" << modFile << "\" format is not known.\n" + << "Please report this at: http://www.cmake.org/Bug\n" + << "\n"; + return true; + } + std::string stampLine; + bool stampHasNewline = false; + if(!cmSystemTools::GetLineFromStream(finStampFile, stampLine, + &stampHasNewline, 1024) || + !stampHasNewline) + { + // The stamp file is invalid. + return true; + } + + // Compare the remaining content. + for(;;) + { + int mod_c = finModFile.get(); + int stamp_c = finStampFile.get(); + if(!finModFile && !finStampFile) + { + // We have reached the end of both files simultaneously. + // The modules are identical. + return false; + } + else if(!finModFile || !finStampFile || mod_c != stamp_c) + { + // We have reached the end of one file before the other. + // The modules are different. + break; + } + } + + // The modules are different. + return true; +} + //---------------------------------------------------------------------------- bool cmDependsFortran::FindIncludeFile(const char* dir, const char* includeName, diff --git a/Source/cmDependsFortran.h b/Source/cmDependsFortran.h index e8c4a7899..61de7e858 100644 --- a/Source/cmDependsFortran.h +++ b/Source/cmDependsFortran.h @@ -46,6 +46,10 @@ public: corresponding stamp file. */ static bool CopyModule(const std::vector& args); + /** Determine if a mod file and the corresponding mod.stamp file + are representing different module information. */ + static bool ModulesDiffer(const char* modFile, const char* stampFile); + /** Method to find an included file in the include path. Fortran always searches the directory containing the including source first. */