| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmDependsFortran.h" | |
| #include <cassert> | |
| #include <cstdlib> | |
| #include <iostream> | |
| #include <map> | |
| #include <utility> | |
| #include "cmsys/FStream.hxx" | |
| #include "cmFortranParser.h" /* Interface to parser object. */ | |
| #include "cmGeneratedFileStream.h" | |
| #include "cmGlobalUnixMakefileGenerator3.h" | |
| #include "cmList.h" | |
| #include "cmLocalUnixMakefileGenerator3.h" | |
| #include "cmMakefile.h" | |
| #include "cmOutputConverter.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmValue.h" | |
| // TODO: Test compiler for the case of the mod file. Some always | |
| // use lower case and some always use upper case. I do not know if any | |
| // use the case from the source code. | |
| static void cmFortranModuleAppendUpperLower(std::string const& mod, | |
| std::string& mod_upper, | |
| std::string& mod_lower) | |
| { | |
| std::string::size_type ext_len = 0; | |
| if (cmHasLiteralSuffix(mod, ".mod") || cmHasLiteralSuffix(mod, ".sub")) { | |
| ext_len = 4; | |
| } else if (cmHasLiteralSuffix(mod, ".smod")) { | |
| ext_len = 5; | |
| } | |
| std::string const& name = mod.substr(0, mod.size() - ext_len); | |
| std::string const& ext = mod.substr(mod.size() - ext_len); | |
| mod_upper = | |
| cmStrCat(std::move(mod_upper), cmSystemTools::UpperCase(name), ext); | |
| mod_lower += mod; | |
| } | |
| class cmDependsFortranInternals | |
| { | |
| public: | |
| // The set of modules provided by this target. | |
| std::set<std::string> TargetProvides; | |
| // Map modules required by this target to locations. | |
| using TargetRequiresMap = std::map<std::string, std::string>; | |
| TargetRequiresMap TargetRequires; | |
| // Information about each object file. | |
| using ObjectInfoMap = std::map<std::string, cmFortranSourceInfo>; | |
| ObjectInfoMap ObjectInfo; | |
| cmFortranSourceInfo& CreateObjectInfo(std::string const& obj, | |
| std::string const& src) | |
| { | |
| auto i = this->ObjectInfo.find(obj); | |
| if (i == this->ObjectInfo.end()) { | |
| std::map<std::string, cmFortranSourceInfo>::value_type entry( | |
| obj, cmFortranSourceInfo()); | |
| i = this->ObjectInfo.insert(entry).first; | |
| i->second.Source = src; | |
| } | |
| return i->second; | |
| } | |
| }; | |
| cmDependsFortran::cmDependsFortran() = default; | |
| cmDependsFortran::cmDependsFortran(cmLocalUnixMakefileGenerator3* lg) | |
| : cmDepends(lg) | |
| , Internal(new cmDependsFortranInternals) | |
| { | |
| // Configure the include file search path. | |
| this->SetIncludePathFromLanguage("Fortran"); | |
| // Get the list of definitions. | |
| cmMakefile* mf = this->LocalGenerator->GetMakefile(); | |
| cmList definitions{ mf->GetDefinition("CMAKE_TARGET_DEFINITIONS_Fortran") }; | |
| // translate i.e. FOO=BAR to FOO and add it to the list of defined | |
| // preprocessor symbols | |
| for (std::string def : definitions) { | |
| std::string::size_type assignment = def.find('='); | |
| if (assignment != std::string::npos) { | |
| def.resize(assignment); | |
| } | |
| this->PPDefinitions.insert(def); | |
| } | |
| this->CompilerId = mf->GetSafeDefinition("CMAKE_Fortran_COMPILER_ID"); | |
| this->SModSep = mf->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_SEP"); | |
| this->SModExt = mf->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_EXT"); | |
| } | |
| cmDependsFortran::~cmDependsFortran() = default; | |
| bool cmDependsFortran::WriteDependencies(std::set<std::string> const& sources, | |
| std::string const& obj, | |
| std::ostream& /*makeDepends*/, | |
| std::ostream& /*internalDepends*/) | |
| { | |
| // Make sure this is a scanning instance. | |
| if (sources.empty() || sources.begin()->empty()) { | |
| cmSystemTools::Error("Cannot scan dependencies without a source file."); | |
| return false; | |
| } | |
| if (obj.empty()) { | |
| cmSystemTools::Error("Cannot scan dependencies without an object file."); | |
| return false; | |
| } | |
| cmFortranCompiler fc; | |
| fc.Id = this->CompilerId; | |
| fc.SModSep = this->SModSep; | |
| fc.SModExt = this->SModExt; | |
| bool okay = true; | |
| for (std::string const& src : sources) { | |
| // Get the information object for this source. | |
| cmFortranSourceInfo& info = this->Internal->CreateObjectInfo(obj, src); | |
| // Create the parser object. The constructor takes info by reference, | |
| // so we may look into the resulting objects later. | |
| cmFortranParser parser(fc, this->IncludePath, this->PPDefinitions, info); | |
| // Push on the starting file. | |
| cmFortranParser_FilePush(&parser, src.c_str()); | |
| // Parse the translation unit. | |
| if (cmFortran_yyparse(parser.Scanner) != 0) { | |
| // Failed to parse the file. Report failure to write dependencies. | |
| okay = false; | |
| /* clang-format off */ | |
| std::cerr << | |
| "warning: failed to parse dependencies from Fortran source " | |
| "'" << src << "': " << parser.Error << std::endl | |
| ; | |
| /* clang-format on */ | |
| } | |
| } | |
| return okay; | |
| } | |
| bool cmDependsFortran::Finalize(std::ostream& makeDepends, | |
| std::ostream& internalDepends) | |
| { | |
| // Prepare the module search process. | |
| if (!this->LocateModules()) { | |
| return false; | |
| } | |
| // Get the directory in which stamp files will be stored. | |
| std::string const& stamp_dir = this->TargetDirectory; | |
| // Get the directory in which module files will be created. | |
| cmMakefile* mf = this->LocalGenerator->GetMakefile(); | |
| std::string mod_dir = | |
| mf->GetSafeDefinition("CMAKE_Fortran_TARGET_MODULE_DIR"); | |
| if (mod_dir.empty()) { | |
| mod_dir = this->LocalGenerator->GetCurrentBinaryDirectory(); | |
| } | |
| // ATTENTION Before 4.0 the property name was misspelled. | |
| // Check the correct name first and than the old name. | |
| bool building_intrinsics = | |
| !mf->GetSafeDefinition("CMAKE_Fortran_TARGET_BUILDING_INTRINSIC_MODULES") | |
| .empty() || | |
| !mf->GetSafeDefinition("CMAKE_Fortran_TARGET_BUILDING_INSTRINSIC_MODULES") | |
| .empty(); | |
| // Actually write dependencies to the streams. | |
| using ObjectInfoMap = cmDependsFortranInternals::ObjectInfoMap; | |
| ObjectInfoMap const& objInfo = this->Internal->ObjectInfo; | |
| for (auto const& i : objInfo) { | |
| if (!this->WriteDependenciesReal(i.first, i.second, mod_dir, stamp_dir, | |
| makeDepends, internalDepends, | |
| building_intrinsics)) { | |
| return false; | |
| } | |
| } | |
| // Store the list of modules provided by this target. | |
| std::string fiName = cmStrCat(this->TargetDirectory, "/fortran.internal"); | |
| cmGeneratedFileStream fiStream(fiName); | |
| fiStream << "# The fortran modules provided by this target.\n"; | |
| fiStream << "provides\n"; | |
| std::set<std::string> const& provides = this->Internal->TargetProvides; | |
| for (std::string const& i : provides) { | |
| fiStream << ' ' << i << '\n'; | |
| } | |
| // Create a script to clean the modules. | |
| if (!provides.empty()) { | |
| std::string fcName = | |
| cmStrCat(this->TargetDirectory, "/cmake_clean_Fortran.cmake"); | |
| cmGeneratedFileStream fcStream(fcName); | |
| fcStream << "# Remove fortran modules provided by this target.\n"; | |
| fcStream << "FILE(REMOVE"; | |
| for (std::string const& i : provides) { | |
| std::string mod_upper = cmStrCat(mod_dir, '/'); | |
| std::string mod_lower = cmStrCat(mod_dir, '/'); | |
| cmFortranModuleAppendUpperLower(i, mod_upper, mod_lower); | |
| std::string stamp = cmStrCat(stamp_dir, '/', i, ".stamp"); | |
| fcStream << "\n" | |
| " \"" | |
| << this->LocalGenerator->MaybeRelativeToCurBinDir(mod_lower) | |
| << "\"\n" | |
| " \"" | |
| << this->LocalGenerator->MaybeRelativeToCurBinDir(mod_upper) | |
| << "\"\n" | |
| " \"" | |
| << this->LocalGenerator->MaybeRelativeToCurBinDir(stamp) | |
| << "\"\n"; | |
| } | |
| fcStream << " )\n"; | |
| } | |
| return true; | |
| } | |
| bool cmDependsFortran::LocateModules() | |
| { | |
| // Collect the set of modules provided and required by all sources. | |
| using ObjectInfoMap = cmDependsFortranInternals::ObjectInfoMap; | |
| ObjectInfoMap const& objInfo = this->Internal->ObjectInfo; | |
| for (auto const& infoI : objInfo) { | |
| cmFortranSourceInfo const& info = infoI.second; | |
| // Include this module in the set provided by this target. | |
| this->Internal->TargetProvides.insert(info.Provides.begin(), | |
| info.Provides.end()); | |
| for (std::string const& r : info.Requires) { | |
| this->Internal->TargetRequires[r].clear(); | |
| } | |
| } | |
| // Short-circuit for simple targets. | |
| if (this->Internal->TargetRequires.empty()) { | |
| return true; | |
| } | |
| // Match modules provided by this target to those it requires. | |
| this->MatchLocalModules(); | |
| // Load information about other targets. | |
| cmMakefile* mf = this->LocalGenerator->GetMakefile(); | |
| cmList infoFiles{ mf->GetDefinition( | |
| "CMAKE_Fortran_TARGET_LINKED_INFO_FILES") }; | |
| for (auto const& i : infoFiles) { | |
| std::string targetDir = cmSystemTools::GetFilenamePath(i); | |
| std::string fname = targetDir + "/fortran.internal"; | |
| cmsys::ifstream fin(fname.c_str()); | |
| if (!fin) { | |
| cmSystemTools::Error(cmStrCat("-E cmake_depends failed to open ", fname, | |
| " for module information")); | |
| return false; | |
| } | |
| this->MatchRemoteModules(fin, targetDir); | |
| } | |
| // TODO: Use `CMAKE_Fortran_TARGET_FORWARD_LINKED_INFO_FILES` to handle cases | |
| // described in #25425. Note that because Makefiles generators do not | |
| // implement relaxed object compilation as described in #15555, the issues | |
| // never actually cause build failures; only incremental build incorrectness. | |
| return true; | |
| } | |
| void cmDependsFortran::MatchLocalModules() | |
| { | |
| std::string const& stampDir = this->TargetDirectory; | |
| std::set<std::string> const& provides = this->Internal->TargetProvides; | |
| for (std::string const& i : provides) { | |
| this->ConsiderModule(i, stampDir); | |
| } | |
| } | |
| void cmDependsFortran::MatchRemoteModules(std::istream& fin, | |
| std::string const& stampDir) | |
| { | |
| std::string line; | |
| bool doing_provides = false; | |
| while (cmSystemTools::GetLineFromStream(fin, line)) { | |
| // Ignore comments and empty lines. | |
| if (line.empty() || line[0] == '#' || line[0] == '\r') { | |
| continue; | |
| } | |
| if (line[0] == ' ') { | |
| if (doing_provides) { | |
| std::string mod = line; | |
| if (!cmHasLiteralSuffix(mod, ".mod") && | |
| !cmHasLiteralSuffix(mod, ".smod") && | |
| !cmHasLiteralSuffix(mod, ".sub")) { | |
| // Support fortran.internal files left by older versions of CMake. | |
| // They do not include the ".mod" extension. | |
| mod += ".mod"; | |
| } | |
| this->ConsiderModule(mod.substr(1), stampDir); | |
| } | |
| } else if (line == "provides") { | |
| doing_provides = true; | |
| } else { | |
| doing_provides = false; | |
| } | |
| } | |
| } | |
| void cmDependsFortran::ConsiderModule(std::string const& name, | |
| std::string const& stampDir) | |
| { | |
| // Locate each required module. | |
| auto required = this->Internal->TargetRequires.find(name); | |
| if (required != this->Internal->TargetRequires.end() && | |
| required->second.empty()) { | |
| // The module is provided by a CMake target. It will have a stamp file. | |
| std::string stampFile = cmStrCat(stampDir, '/', name, ".stamp"); | |
| required->second = stampFile; | |
| } | |
| } | |
| bool cmDependsFortran::WriteDependenciesReal(std::string const& obj, | |
| cmFortranSourceInfo const& info, | |
| std::string const& mod_dir, | |
| std::string const& stamp_dir, | |
| std::ostream& makeDepends, | |
| std::ostream& internalDepends, | |
| bool buildingIntrinsics) | |
| { | |
| // Get the source file for this object. | |
| std::string const& src = info.Source; | |
| // Write the include dependencies to the output stream. | |
| std::string obj_i = this->LocalGenerator->MaybeRelativeToTopBinDir(obj); | |
| std::string obj_m = cmSystemTools::ConvertToOutputPath(obj_i); | |
| internalDepends << obj_i << "\n " << src << '\n'; | |
| if (!info.Includes.empty()) { | |
| auto const& lineContinue = static_cast<cmGlobalUnixMakefileGenerator3*>( | |
| this->LocalGenerator->GetGlobalGenerator()) | |
| ->LineContinueDirective; | |
| bool supportLongLineDepend = static_cast<cmGlobalUnixMakefileGenerator3*>( | |
| this->LocalGenerator->GetGlobalGenerator()) | |
| ->SupportsLongLineDependencies(); | |
| if (supportLongLineDepend) { | |
| makeDepends << obj_m << ':'; | |
| } | |
| for (std::string const& i : info.Includes) { | |
| std::string dependee = cmSystemTools::ConvertToOutputPath( | |
| this->LocalGenerator->MaybeRelativeToTopBinDir(i)); | |
| if (supportLongLineDepend) { | |
| makeDepends << ' ' << lineContinue << ' ' << dependee; | |
| } else { | |
| makeDepends << obj_m << ": " << dependee << '\n'; | |
| } | |
| internalDepends << ' ' << i << '\n'; | |
| } | |
| makeDepends << '\n'; | |
| } | |
| std::set<std::string> req = info.Requires; | |
| if (buildingIntrinsics) { | |
| req.insert(info.Intrinsics.begin(), info.Intrinsics.end()); | |
| } | |
| // Write module requirements to the output stream. | |
| for (std::string const& i : req) { | |
| // Require only modules not provided in the same source. | |
| if (info.Provides.find(i) != info.Provides.cend()) { | |
| continue; | |
| } | |
| // The object file should depend on timestamped files for the | |
| // modules it uses. | |
| auto required = this->Internal->TargetRequires.find(i); | |
| if (required == this->Internal->TargetRequires.end()) { | |
| abort(); | |
| } | |
| if (!required->second.empty()) { | |
| // This module is known. Depend on its timestamp file. | |
| std::string stampFile = cmSystemTools::ConvertToOutputPath( | |
| this->LocalGenerator->MaybeRelativeToTopBinDir(required->second)); | |
| makeDepends << obj_m << ": " << stampFile << '\n'; | |
| } else { | |
| // This module is not known to CMake. Try to locate it where | |
| // the compiler will and depend on that. | |
| std::string module; | |
| if (this->FindModule(i, module)) { | |
| module = cmSystemTools::ConvertToOutputPath( | |
| this->LocalGenerator->MaybeRelativeToTopBinDir(module)); | |
| makeDepends << obj_m << ": " << module << '\n'; | |
| } | |
| } | |
| } | |
| // If any modules are provided then they must be converted to stamp files. | |
| if (!info.Provides.empty()) { | |
| // Create a target to copy the module after the object file | |
| // changes. | |
| for (std::string const& i : info.Provides) { | |
| // Include this module in the set provided by this target. | |
| this->Internal->TargetProvides.insert(i); | |
| // Always use lower case for the mod stamp file name. The | |
| // cmake_copy_f90_mod will call back to this class, which will | |
| // try various cases for the real mod file name. | |
| std::string modFile = cmStrCat(mod_dir, '/', i); | |
| modFile = this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->MaybeRelativeToTopBinDir(modFile), | |
| cmOutputConverter::SHELL); | |
| std::string stampFile = cmStrCat(stamp_dir, '/', i, ".stamp"); | |
| stampFile = this->LocalGenerator->MaybeRelativeToTopBinDir(stampFile); | |
| std::string const stampFileForShell = | |
| this->LocalGenerator->ConvertToOutputFormat(stampFile, | |
| cmOutputConverter::SHELL); | |
| std::string const stampFileForMake = | |
| cmSystemTools::ConvertToOutputPath(stampFile); | |
| makeDepends << obj_m << ".provides.build" | |
| << ": " << stampFileForMake << '\n'; | |
| // Note that when cmake_copy_f90_mod finds that a module file | |
| // and the corresponding stamp file have no differences, the stamp | |
| // file is not updated. In such case the stamp file will be always | |
| // older than its prerequisite and trigger cmake_copy_f90_mod | |
| // on each new build. This is expected behavior for incremental | |
| // builds and can not be changed without performing recursive make | |
| // calls that would considerably slow down the building process. | |
| makeDepends << stampFileForMake << ": " << obj_m << '\n'; | |
| makeDepends << "\t$(CMAKE_COMMAND) -E cmake_copy_f90_mod " << modFile | |
| << ' ' << stampFileForShell; | |
| cmMakefile* mf = this->LocalGenerator->GetMakefile(); | |
| cmValue cid = mf->GetDefinition("CMAKE_Fortran_COMPILER_ID"); | |
| if (cmNonempty(cid)) { | |
| makeDepends << ' ' << *cid; | |
| } | |
| makeDepends << '\n'; | |
| } | |
| makeDepends << obj_m << ".provides.build:\n"; | |
| // After copying the modules update the timestamp file. | |
| makeDepends << "\t$(CMAKE_COMMAND) -E touch " << obj_m | |
| << ".provides.build\n"; | |
| // Make sure the module timestamp rule is evaluated by the time | |
| // the target finishes building. | |
| std::string driver = cmStrCat(this->TargetDirectory, "/build"); | |
| driver = cmSystemTools::ConvertToOutputPath( | |
| this->LocalGenerator->MaybeRelativeToTopBinDir(driver)); | |
| makeDepends << driver << ": " << obj_m << ".provides.build\n"; | |
| } | |
| return true; | |
| } | |
| bool cmDependsFortran::FindModule(std::string const& name, std::string& module) | |
| { | |
| // Construct possible names for the module file. | |
| std::string mod_upper; | |
| std::string mod_lower; | |
| cmFortranModuleAppendUpperLower(name, mod_upper, mod_lower); | |
| // Search the include path for the module. | |
| std::string fullName; | |
| for (std::string const& ip : this->IncludePath) { | |
| // Try the lower-case name. | |
| fullName = cmStrCat(ip, '/', mod_lower); | |
| if (cmSystemTools::FileExists(fullName, true)) { | |
| module = fullName; | |
| return true; | |
| } | |
| // Try the upper-case name. | |
| fullName = cmStrCat(ip, '/', mod_upper); | |
| if (cmSystemTools::FileExists(fullName, true)) { | |
| module = fullName; | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool cmDependsFortran::CopyModule(std::vector<std::string> const& args) | |
| { | |
| // Implements | |
| // | |
| // $(CMAKE_COMMAND) -E cmake_copy_f90_mod input.mod output.mod.stamp | |
| // [compiler-id] | |
| // | |
| // Note that the case of the .mod file depends on the compiler. In | |
| // the future this copy could also account for the fact that some | |
| // compilers include a timestamp in the .mod file so it changes even | |
| // when the interface described in the module does not. | |
| std::string mod = args[2]; | |
| std::string const& stamp = args[3]; | |
| std::string compilerId; | |
| if (args.size() >= 5) { | |
| compilerId = args[4]; | |
| } | |
| if (!cmHasLiteralSuffix(mod, ".mod") && !cmHasLiteralSuffix(mod, ".smod") && | |
| !cmHasLiteralSuffix(mod, ".sub")) { | |
| // Support depend.make files left by older versions of CMake. | |
| // They do not include the ".mod" extension. | |
| mod += ".mod"; | |
| } | |
| std::string mod_dir = cmSystemTools::GetFilenamePath(mod); | |
| if (!mod_dir.empty()) { | |
| mod_dir += "/"; | |
| } | |
| std::string mod_upper = mod_dir; | |
| std::string mod_lower = mod_dir; | |
| cmFortranModuleAppendUpperLower(cmSystemTools::GetFilenameName(mod), | |
| mod_upper, mod_lower); | |
| if (cmSystemTools::FileExists(mod_upper, true)) { | |
| if (cmDependsFortran::ModulesDiffer(mod_upper, stamp, compilerId)) { | |
| if (!cmSystemTools::CopyFileAlways(mod_upper, stamp)) { | |
| std::cerr << "Error copying Fortran module from \"" << mod_upper | |
| << "\" to \"" << stamp << "\".\n"; | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| if (cmSystemTools::FileExists(mod_lower, true)) { | |
| if (cmDependsFortran::ModulesDiffer(mod_lower, stamp, compilerId)) { | |
| if (!cmSystemTools::CopyFileAlways(mod_lower, stamp)) { | |
| std::cerr << "Error copying Fortran module from \"" << mod_lower | |
| << "\" to \"" << stamp << "\".\n"; | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| std::cerr << "Error copying Fortran module \"" << args[2] << "\". Tried \"" | |
| << mod_upper << "\" and \"" << mod_lower << "\".\n"; | |
| return false; | |
| } | |
| // Helper function to look for a short sequence in a stream. If this | |
| // is later used for longer sequences it should be re-written using an | |
| // efficient string search algorithm such as Boyer-Moore. | |
| static bool cmFortranStreamContainsSequence(std::istream& ifs, char const* seq, | |
| int len) | |
| { | |
| assert(len > 0); | |
| int cur = 0; | |
| while (cur < len) { | |
| // Get the next character. | |
| int token = ifs.get(); | |
| if (!ifs) { | |
| return false; | |
| } | |
| // Check the character. | |
| if (token == static_cast<int>(seq[cur])) { | |
| ++cur; | |
| } else { | |
| // Assume the sequence has no repeating subsequence. | |
| cur = 0; | |
| } | |
| } | |
| // The entire sequence was matched. | |
| return true; | |
| } | |
| // Helper function to compare the remaining content in two streams. | |
| static bool cmFortranStreamsDiffer(std::istream& ifs1, std::istream& ifs2) | |
| { | |
| // Compare the remaining content. | |
| for (;;) { | |
| int ifs1_c = ifs1.get(); | |
| int ifs2_c = ifs2.get(); | |
| if (!ifs1 && !ifs2) { | |
| // We have reached the end of both streams simultaneously. | |
| // The streams are identical. | |
| return false; | |
| } | |
| if (!ifs1 || !ifs2 || ifs1_c != ifs2_c) { | |
| // We have reached the end of one stream before the other or | |
| // found differing content. The streams are different. | |
| break; | |
| } | |
| } | |
| return true; | |
| } | |
| bool cmDependsFortran::ModulesDiffer(std::string const& modFile, | |
| std::string const& stampFile, | |
| std::string const& compilerId) | |
| { | |
| /* | |
| gnu >= 4.9: | |
| A mod file is an ascii file compressed with gzip. | |
| Compiling twice produces identical modules. | |
| gnu < 4.9: | |
| A mod file is an ascii file. | |
| <bar.mod> | |
| 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. | |
| ... | |
| </bar.mod> | |
| 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 sequence linefeed-zero (0x0A 0x00) | |
| which is located some bytes in front of the absolute path to the source | |
| file. | |
| sun: | |
| A mod file is a binary file. Compiling twice produces identical modules. | |
| others: | |
| TODO ... | |
| */ | |
| /* Compilers which do _not_ produce different mod content when the same | |
| * source is compiled twice | |
| * -SunPro | |
| */ | |
| if (compilerId == "SunPro") { | |
| return cmSystemTools::FilesDiffer(modFile, stampFile); | |
| } | |
| #if defined(_WIN32) || defined(__CYGWIN__) | |
| cmsys::ifstream finModFile(modFile.c_str(), std::ios::in | std::ios::binary); | |
| cmsys::ifstream finStampFile(stampFile.c_str(), | |
| std::ios::in | std::ios::binary); | |
| #else | |
| cmsys::ifstream finModFile(modFile.c_str()); | |
| cmsys::ifstream finStampFile(stampFile.c_str()); | |
| #endif | |
| if (!finModFile || !finStampFile) { | |
| // At least one of the files does not exist. The modules differ. | |
| return true; | |
| } | |
| /* Compilers which _do_ produce different mod content when the same | |
| * source is compiled twice | |
| * -GNU | |
| * -Intel | |
| * | |
| * Eat the stream content until all recompile only related changes | |
| * are left behind. | |
| */ | |
| if (compilerId == "GNU") { | |
| // GNU Fortran 4.9 and later compress .mod files with gzip | |
| // but also do not include a date so we can fall through to | |
| // compare them without skipping any prefix. | |
| unsigned char hdr[2]; | |
| bool okay = !finModFile.read(reinterpret_cast<char*>(hdr), 2).fail(); | |
| finModFile.seekg(0); | |
| if (!okay || hdr[0] != 0x1f || hdr[1] != 0x8b) { | |
| char const seq[1] = { '\n' }; | |
| int const seqlen = 1; | |
| if (!cmFortranStreamContainsSequence(finModFile, seq, seqlen)) { | |
| // The module is of unexpected format. Assume it is different. | |
| std::cerr << compilerId << " fortran module " << modFile | |
| << " has unexpected format." << std::endl; | |
| return true; | |
| } | |
| if (!cmFortranStreamContainsSequence(finStampFile, seq, seqlen)) { | |
| // The stamp must differ if the sequence is not contained. | |
| return true; | |
| } | |
| } | |
| } else if (compilerId == "Intel" || compilerId == "IntelLLVM") { | |
| char const seq[2] = { '\n', '\0' }; | |
| int const seqlen = 2; | |
| // Skip the leading byte which appears to be a version number. | |
| // We do not need to check for an error because the sequence search | |
| // below will fail in that case. | |
| finModFile.get(); | |
| finStampFile.get(); | |
| if (!cmFortranStreamContainsSequence(finModFile, seq, seqlen)) { | |
| // The module is of unexpected format. Assume it is different. | |
| std::cerr << compilerId << " fortran module " << modFile | |
| << " has unexpected format." << std::endl; | |
| return true; | |
| } | |
| if (!cmFortranStreamContainsSequence(finStampFile, seq, seqlen)) { | |
| // The stamp must differ if the sequence is not contained. | |
| return true; | |
| } | |
| } | |
| // Compare the remaining content. If no compiler id matched above, | |
| // including the case none was given, this will compare the whole | |
| // content. | |
| return cmFortranStreamsDiffer(finModFile, finStampFile); | |
| } |