cmDependsFortran.cxx 23.9 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3 4
#include "cmDependsFortran.h"

5
#include "cmsys/FStream.hxx"
6
#include <assert.h>
7 8 9 10
#include <iostream>
#include <map>
#include <stdlib.h>
#include <utility>
11

12
#include "cmAlgorithms.h"
13 14 15 16 17 18 19 20 21
#include "cmFortranParser.h" /* Interface to parser object.  */
#include "cmGeneratedFileStream.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmOutputConverter.h"
#include "cmStateDirectory.h"
#include "cmStateSnapshot.h"
#include "cmSystemTools.h"

22 23 24 25
// 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.

26 27 28 29 30 31 32
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")) {
    ext_len = 4;
33 34
  } else if (cmHasLiteralSuffix(mod, ".smod")) {
    ext_len = 5;
35 36 37 38 39 40 41
  }
  std::string const& name = mod.substr(0, mod.size() - ext_len);
  std::string const& ext = mod.substr(mod.size() - ext_len);
  mod_upper += cmSystemTools::UpperCase(name) + ext;
  mod_lower += mod;
}

42 43 44 45
class cmDependsFortranInternals
{
public:
  // The set of modules provided by this target.
46
  std::set<std::string> TargetProvides;
47 48

  // Map modules required by this target to locations.
49
  typedef std::map<std::string, std::string> TargetRequiresMap;
50 51 52
  TargetRequiresMap TargetRequires;

  // Information about each object file.
53
  typedef std::map<std::string, cmFortranSourceInfo> ObjectInfoMap;
54 55
  ObjectInfoMap ObjectInfo;

56 57
  cmFortranSourceInfo& CreateObjectInfo(const std::string& obj,
                                        const std::string& src)
58
  {
59
    std::map<std::string, cmFortranSourceInfo>::iterator i =
60
      this->ObjectInfo.find(obj);
61 62 63
    if (i == this->ObjectInfo.end()) {
      std::map<std::string, cmFortranSourceInfo>::value_type entry(
        obj, cmFortranSourceInfo());
64 65 66
      i = this->ObjectInfo.insert(entry).first;
      i->second.Source = src;
    }
67 68
    return i->second;
  }
69 70
};

wahikihiki's avatar
wahikihiki committed
71
cmDependsFortran::cmDependsFortran() = default;
72

73 74 75
cmDependsFortran::cmDependsFortran(cmLocalGenerator* lg)
  : cmDepends(lg)
  , Internal(new cmDependsFortranInternals)
76
{
77 78 79
  // Configure the include file search path.
  this->SetIncludePathFromLanguage("Fortran");

80 81 82
  // Get the list of definitions.
  std::vector<std::string> definitions;
  cmMakefile* mf = this->LocalGenerator->GetMakefile();
83 84
  if (const char* c_defines =
        mf->GetDefinition("CMAKE_TARGET_DEFINITIONS_Fortran")) {
85
    cmSystemTools::ExpandListArgument(c_defines, definitions);
86
  }
87

88
  // translate i.e. FOO=BAR to FOO and add it to the list of defined
89
  // preprocessor symbols
90
  for (std::string def : definitions) {
91
    std::string::size_type assignment = def.find('=');
92
    if (assignment != std::string::npos) {
93
      def = def.substr(0, assignment);
94
    }
95 96
    this->PPDefinitions.insert(def);
  }
97 98

  this->CompilerId = mf->GetSafeDefinition("CMAKE_Fortran_COMPILER_ID");
99 100
  this->SModSep = mf->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_SEP");
  this->SModExt = mf->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_EXT");
101 102 103 104
}

cmDependsFortran::~cmDependsFortran()
{
105
  delete this->Internal;
106 107
}

108
bool cmDependsFortran::WriteDependencies(const std::set<std::string>& sources,
109 110 111
                                         const std::string& obj,
                                         std::ostream& /*makeDepends*/,
                                         std::ostream& /*internalDepends*/)
112 113
{
  // Make sure this is a scanning instance.
114
  if (sources.empty() || sources.begin()->empty()) {
115
    cmSystemTools::Error("Cannot scan dependencies without a source file.");
116
    return false;
117 118
  }
  if (obj.empty()) {
119 120
    cmSystemTools::Error("Cannot scan dependencies without an object file.");
    return false;
121
  }
122

123 124
  cmFortranCompiler fc;
  fc.Id = this->CompilerId;
125 126
  fc.SModSep = this->SModSep;
  fc.SModExt = this->SModExt;
127

128
  bool okay = true;
129
  for (std::string const& src : sources) {
130
    // Get the information object for this source.
131
    cmFortranSourceInfo& info = this->Internal->CreateObjectInfo(obj, src);
132

133 134
    // Create the parser object. The constructor takes info by reference,
    // so we may look into the resulting objects later.
135
    cmFortranParser parser(fc, this->IncludePath, this->PPDefinitions, info);
136

137
    // Push on the starting file.
138
    cmFortranParser_FilePush(&parser, src.c_str());
139

140
    // Parse the translation unit.
141
    if (cmFortran_yyparse(parser.Scanner) != 0) {
142 143
      // Failed to parse the file.  Report failure to write dependencies.
      okay = false;
144 145 146 147 148 149
      /* clang-format off */
      std::cerr <<
        "warning: failed to parse dependencies from Fortran source "
        "'" << src << "': " << parser.Error << std::endl
        ;
      /* clang-format on */
150
    }
151
  }
152
  return okay;
153 154 155 156 157 158 159 160
}

bool cmDependsFortran::Finalize(std::ostream& makeDepends,
                                std::ostream& internalDepends)
{
  // Prepare the module search process.
  this->LocateModules();

161
  // Get the directory in which stamp files will be stored.
162
  const std::string& stamp_dir = this->TargetDirectory;
163 164 165

  // Get the directory in which module files will be created.
  cmMakefile* mf = this->LocalGenerator->GetMakefile();
166 167
  std::string mod_dir =
    mf->GetSafeDefinition("CMAKE_Fortran_TARGET_MODULE_DIR");
168 169 170
  if (mod_dir.empty()) {
    mod_dir = this->LocalGenerator->GetCurrentBinaryDirectory();
  }
171

172 173 174
  // Actually write dependencies to the streams.
  typedef cmDependsFortranInternals::ObjectInfoMap ObjectInfoMap;
  ObjectInfoMap const& objInfo = this->Internal->ObjectInfo;
175
  for (auto const& i : objInfo) {
176 177
    if (!this->WriteDependenciesReal(i.first, i.second, mod_dir, stamp_dir,
                                     makeDepends, internalDepends)) {
178 179
      return false;
    }
180
  }
181 182 183 184

  // Store the list of modules provided by this target.
  std::string fiName = this->TargetDirectory;
  fiName += "/fortran.internal";
185
  cmGeneratedFileStream fiStream(fiName);
186 187
  fiStream << "# The fortran modules provided by this target.\n";
  fiStream << "provides\n";
188
  std::set<std::string> const& provides = this->Internal->TargetProvides;
189 190
  for (std::string const& i : provides) {
    fiStream << " " << i << "\n";
191
  }
192 193

  // Create a script to clean the modules.
194
  if (!provides.empty()) {
195 196
    std::string fcName = this->TargetDirectory;
    fcName += "/cmake_clean_Fortran.cmake";
197
    cmGeneratedFileStream fcStream(fcName);
198
    fcStream << "# Remove fortran modules provided by this target.\n";
199
    fcStream << "FILE(REMOVE";
200 201
    std::string currentBinDir =
      this->LocalGenerator->GetCurrentBinaryDirectory();
202
    for (std::string const& i : provides) {
203 204 205 206
      std::string mod_upper = mod_dir;
      mod_upper += "/";
      std::string mod_lower = mod_dir;
      mod_lower += "/";
207
      cmFortranModuleAppendUpperLower(i, mod_upper, mod_lower);
208 209
      std::string stamp = stamp_dir;
      stamp += "/";
210
      stamp += i;
211
      stamp += ".stamp";
212
      fcStream << "\n";
213
      fcStream << "  \""
214
               << this->MaybeConvertToRelativePath(currentBinDir, mod_lower)
215
               << "\"\n";
216
      fcStream << "  \""
217
               << this->MaybeConvertToRelativePath(currentBinDir, mod_upper)
218
               << "\"\n";
219
      fcStream << "  \""
220
               << this->MaybeConvertToRelativePath(currentBinDir, stamp)
221
               << "\"\n";
222
    }
223 224
    fcStream << "  )\n";
  }
225 226 227 228 229 230 231 232
  return true;
}

void cmDependsFortran::LocateModules()
{
  // Collect the set of modules provided and required by all sources.
  typedef cmDependsFortranInternals::ObjectInfoMap ObjectInfoMap;
  ObjectInfoMap const& objInfo = this->Internal->ObjectInfo;
233 234
  for (auto const& infoI : objInfo) {
    cmFortranSourceInfo const& info = infoI.second;
235 236 237
    // Include this module in the set provided by this target.
    this->Internal->TargetProvides.insert(info.Provides.begin(),
                                          info.Provides.end());
238

239
    for (std::string const& r : info.Requires) {
240
      this->Internal->TargetRequires[r].clear();
241
    }
242
  }
243 244

  // Short-circuit for simple targets.
245
  if (this->Internal->TargetRequires.empty()) {
246
    return;
247
  }
248 249 250 251 252 253 254

  // Match modules provided by this target to those it requires.
  this->MatchLocalModules();

  // Load information about other targets.
  cmMakefile* mf = this->LocalGenerator->GetMakefile();
  std::vector<std::string> infoFiles;
255 256
  if (const char* infoFilesValue =
        mf->GetDefinition("CMAKE_TARGET_LINKED_INFO_FILES")) {
257
    cmSystemTools::ExpandListArgument(infoFilesValue, infoFiles);
258
  }
259 260
  for (std::string const& i : infoFiles) {
    std::string targetDir = cmSystemTools::GetFilenamePath(i);
261
    std::string fname = targetDir + "/fortran.internal";
262
    cmsys::ifstream fin(fname.c_str());
263
    if (fin) {
264
      this->MatchRemoteModules(fin, targetDir);
265
    }
266
  }
267 268 269 270
}

void cmDependsFortran::MatchLocalModules()
{
271
  std::string const& stampDir = this->TargetDirectory;
272
  std::set<std::string> const& provides = this->Internal->TargetProvides;
273
  for (std::string const& i : provides) {
274
    this->ConsiderModule(i, stampDir);
275
  }
276 277 278
}

void cmDependsFortran::MatchRemoteModules(std::istream& fin,
279
                                          const std::string& stampDir)
280 281
{
  std::string line;
282
  bool doing_provides = false;
283
  while (cmSystemTools::GetLineFromStream(fin, line)) {
284
    // Ignore comments and empty lines.
285
    if (line.empty() || line[0] == '#' || line[0] == '\r') {
286
      continue;
287
    }
288

289 290
    if (line[0] == ' ') {
      if (doing_provides) {
291
        std::string mod = line;
292 293
        if (!cmHasLiteralSuffix(mod, ".mod") &&
            !cmHasLiteralSuffix(mod, ".smod")) {
294 295 296 297 298
          // Support fortran.internal files left by older versions of CMake.
          // They do not include the ".mod" extension.
          mod += ".mod";
        }
        this->ConsiderModule(mod.c_str() + 1, stampDir);
299
      }
300
    } else if (line == "provides") {
301
      doing_provides = true;
302
    } else {
303 304
      doing_provides = false;
    }
305
  }
306 307
}

308 309
void cmDependsFortran::ConsiderModule(const std::string& name,
                                      const std::string& stampDir)
310 311 312 313 314
{
  // Locate each required module.
  typedef cmDependsFortranInternals::TargetRequiresMap TargetRequiresMap;
  TargetRequiresMap::iterator required =
    this->Internal->TargetRequires.find(name);
315 316
  if (required != this->Internal->TargetRequires.end() &&
      required->second.empty()) {
317
    // The module is provided by a CMake target.  It will have a stamp file.
318
    std::string stampFile = stampDir;
319 320
    stampFile += "/";
    stampFile += name;
321
    stampFile += ".stamp";
322
    required->second = stampFile;
323
  }
324 325
}

326
bool cmDependsFortran::WriteDependenciesReal(std::string const& obj,
327 328
                                             cmFortranSourceInfo const& info,
                                             std::string const& mod_dir,
329
                                             std::string const& stamp_dir,
330 331
                                             std::ostream& makeDepends,
                                             std::ostream& internalDepends)
332 333 334 335
{
  typedef cmDependsFortranInternals::TargetRequiresMap TargetRequiresMap;

  // Get the source file for this object.
336
  std::string const& src = info.Source;
337

338
  // Write the include dependencies to the output stream.
339
  std::string binDir = this->LocalGenerator->GetBinaryDirectory();
340
  std::string obj_i = this->MaybeConvertToRelativePath(binDir, obj);
341
  std::string obj_m = cmSystemTools::ConvertToOutputPath(obj_i);
342
  internalDepends << obj_i << std::endl;
343
  internalDepends << " " << src << std::endl;
344
  for (std::string const& i : info.Includes) {
345 346
    makeDepends << obj_m << ": "
                << cmSystemTools::ConvertToOutputPath(
347
                     this->MaybeConvertToRelativePath(binDir, i))
348
                << std::endl;
349
    internalDepends << " " << i << std::endl;
350
  }
351
  makeDepends << std::endl;
352

353
  // Write module requirements to the output stream.
354
  for (std::string const& i : info.Requires) {
355
    // Require only modules not provided in the same source.
356
    if (info.Provides.find(i) != info.Provides.cend()) {
357
      continue;
358
    }
359 360 361 362

    // The object file should depend on timestamped files for the
    // modules it uses.
    TargetRequiresMap::const_iterator required =
363
      this->Internal->TargetRequires.find(i);
364 365 366 367
    if (required == this->Internal->TargetRequires.end()) {
      abort();
    }
    if (!required->second.empty()) {
368
      // This module is known.  Depend on its timestamp file.
369
      std::string stampFile = cmSystemTools::ConvertToOutputPath(
370
        this->MaybeConvertToRelativePath(binDir, required->second));
371
      makeDepends << obj_m << ": " << stampFile << "\n";
372
    } else {
373 374 375
      // This module is not known to CMake.  Try to locate it where
      // the compiler will and depend on that.
      std::string module;
376
      if (this->FindModule(i, module)) {
377
        module = cmSystemTools::ConvertToOutputPath(
378
          this->MaybeConvertToRelativePath(binDir, module));
379
        makeDepends << obj_m << ": " << module << "\n";
380 381
      }
    }
382
  }
383 384

  // If any modules are provided then they must be converted to stamp files.
385
  if (!info.Provides.empty()) {
386 387
    // Create a target to copy the module after the object file
    // changes.
388
    for (std::string const& i : info.Provides) {
389
      // Include this module in the set provided by this target.
390
      this->Internal->TargetProvides.insert(i);
391

392 393 394
      // 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.
395 396
      std::string modFile = mod_dir;
      modFile += "/";
397
      modFile += i;
398
      modFile = this->LocalGenerator->ConvertToOutputFormat(
399
        this->MaybeConvertToRelativePath(binDir, modFile),
400
        cmOutputConverter::SHELL);
401
      std::string stampFile = stamp_dir;
402
      stampFile += "/";
403 404
      stampFile += i;
      stampFile += ".stamp";
405 406 407 408 409
      stampFile = this->MaybeConvertToRelativePath(binDir, stampFile);
      std::string const stampFileForShell =
        this->LocalGenerator->ConvertToOutputFormat(stampFile,
                                                    cmOutputConverter::SHELL);
      std::string const stampFileForMake =
410
        cmSystemTools::ConvertToOutputPath(stampFile);
411 412 413 414 415 416 417 418 419 420 421

      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 preforming recursive make
      // calls that would considerably slow down the building process.
      makeDepends << stampFileForMake << ": " << obj_m << "\n";
422
      makeDepends << "\t$(CMAKE_COMMAND) -E cmake_copy_f90_mod " << modFile
423
                  << " " << stampFileForShell;
424 425
      cmMakefile* mf = this->LocalGenerator->GetMakefile();
      const char* cid = mf->GetDefinition("CMAKE_Fortran_COMPILER_ID");
426
      if (cid && *cid) {
427
        makeDepends << " " << cid;
428
      }
429 430
      makeDepends << "\n";
    }
431
    makeDepends << obj_m << ".provides.build:\n";
432
    // After copying the modules update the timestamp file.
433
    makeDepends << "\t$(CMAKE_COMMAND) -E touch " << obj_m
Bill Hoffman's avatar
Bill Hoffman committed
434
                << ".provides.build\n";
435 436 437

    // Make sure the module timestamp rule is evaluated by the time
    // the target finishes building.
438 439
    std::string driver = this->TargetDirectory;
    driver += "/build";
440
    driver = cmSystemTools::ConvertToOutputPath(
441
      this->MaybeConvertToRelativePath(binDir, driver));
442
    makeDepends << driver << ": " << obj_m << ".provides.build\n";
443
  }
444

445 446 447
  return true;
}

448
bool cmDependsFortran::FindModule(std::string const& name, std::string& module)
449 450
{
  // Construct possible names for the module file.
451 452 453
  std::string mod_upper;
  std::string mod_lower;
  cmFortranModuleAppendUpperLower(name, mod_upper, mod_lower);
454 455 456

  // Search the include path for the module.
  std::string fullName;
457
  for (std::string const& ip : this->IncludePath) {
458
    // Try the lower-case name.
459
    fullName = ip;
460 461
    fullName += "/";
    fullName += mod_lower;
462
    if (cmSystemTools::FileExists(fullName, true)) {
463 464
      module = fullName;
      return true;
465
    }
466 467

    // Try the upper-case name.
468
    fullName = ip;
469 470
    fullName += "/";
    fullName += mod_upper;
471
    if (cmSystemTools::FileExists(fullName, true)) {
472 473 474
      module = fullName;
      return true;
    }
475
  }
476 477 478
  return false;
}

479 480 481 482 483
bool cmDependsFortran::CopyModule(const std::vector<std::string>& args)
{
  // Implements
  //
  //   $(CMAKE_COMMAND) -E cmake_copy_f90_mod input.mod output.mod.stamp
484
  //                                          [compiler-id]
485 486 487 488 489 490 491 492
  //
  // 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 stamp = args[3];
493
  std::string compilerId;
494
  if (args.size() >= 5) {
495
    compilerId = args[4];
496
  }
497
  if (!cmHasLiteralSuffix(mod, ".mod") && !cmHasLiteralSuffix(mod, ".smod")) {
498 499 500 501
    // Support depend.make files left by older versions of CMake.
    // They do not include the ".mod" extension.
    mod += ".mod";
  }
502
  std::string mod_dir = cmSystemTools::GetFilenamePath(mod);
503 504 505
  if (!mod_dir.empty()) {
    mod_dir += "/";
  }
506 507
  std::string mod_upper = mod_dir;
  std::string mod_lower = mod_dir;
508 509
  cmFortranModuleAppendUpperLower(cmSystemTools::GetFilenameName(mod),
                                  mod_upper, mod_lower);
510
  if (cmSystemTools::FileExists(mod_upper, true)) {
511
    if (cmDependsFortran::ModulesDiffer(mod_upper, stamp, compilerId)) {
512 513 514
      if (!cmSystemTools::CopyFileAlways(mod_upper, stamp)) {
        std::cerr << "Error copying Fortran module from \"" << mod_upper
                  << "\" to \"" << stamp << "\".\n";
515
        return false;
516 517
      }
    }
518
    return true;
519
  }
520
  if (cmSystemTools::FileExists(mod_lower, true)) {
521
    if (cmDependsFortran::ModulesDiffer(mod_lower, stamp, compilerId)) {
522 523 524
      if (!cmSystemTools::CopyFileAlways(mod_lower, stamp)) {
        std::cerr << "Error copying Fortran module from \"" << mod_lower
                  << "\" to \"" << stamp << "\".\n";
525
        return false;
526 527
      }
    }
528 529
    return true;
  }
530

531 532
  std::cerr << "Error copying Fortran module \"" << args[2] << "\".  Tried \""
            << mod_upper << "\" and \"" << mod_lower << "\".\n";
533 534 535
  return false;
}

536 537 538
// 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.
539 540
static bool cmFortranStreamContainsSequence(std::istream& ifs, const char* seq,
                                            int len)
541 542 543 544
{
  assert(len > 0);

  int cur = 0;
545
  while (cur < len) {
546 547
    // Get the next character.
    int token = ifs.get();
548
    if (!ifs) {
549
      return false;
550
    }
551 552

    // Check the character.
553
    if (token == static_cast<int>(seq[cur])) {
554
      ++cur;
555
    } else {
556 557 558
      // Assume the sequence has no repeating subsequence.
      cur = 0;
    }
559
  }
560 561 562 563 564 565

  // The entire sequence was matched.
  return true;
}

// Helper function to compare the remaining content in two streams.
566
static bool cmFortranStreamsDiffer(std::istream& ifs1, std::istream& ifs2)
567 568
{
  // Compare the remaining content.
569
  for (;;) {
570 571
    int ifs1_c = ifs1.get();
    int ifs2_c = ifs2.get();
572
    if (!ifs1 && !ifs2) {
573 574 575
      // We have reached the end of both streams simultaneously.
      // The streams are identical.
      return false;
576
    }
577

578
    if (!ifs1 || !ifs2 || ifs1_c != ifs2_c) {
579 580 581 582
      // We have reached the end of one stream before the other or
      // found differing content.  The streams are different.
      break;
    }
583
  }
584 585 586 587

  return true;
}

588 589 590
bool cmDependsFortran::ModulesDiffer(const std::string& modFile,
                                     const std::string& stampFile,
                                     const std::string& compilerId)
591 592
{
  /*
593 594 595 596 597
  gnu >= 4.9:
    A mod file is an ascii file compressed with gzip.
    Compiling twice produces identical modules.

  gnu < 4.9:
598 599 600 601 602 603 604 605 606 607 608
    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
609
    shows that they differ only before a sequence linefeed-zero (0x0A 0x00)
Luz Paz's avatar
Luz Paz committed
610
    which is located some bytes in front of the absolute path to the source
611
    file.
612 613

  sun:
614
    A mod file is a binary file.  Compiling twice produces identical modules.
615 616

  others:
617
    TODO ...
618
  */
619 620 621 622 623

  /* Compilers which do _not_ produce different mod content when the same
   * source is compiled twice
   *   -SunPro
   */
624
  if (compilerId == "SunPro") {
625
    return cmSystemTools::FilesDiffer(modFile, stampFile);
626
  }
627

628
#if defined(_WIN32) || defined(__CYGWIN__)
629 630 631
  cmsys::ifstream finModFile(modFile.c_str(), std::ios::in | std::ios::binary);
  cmsys::ifstream finStampFile(stampFile.c_str(),
                               std::ios::in | std::ios::binary);
632
#else
633 634
  cmsys::ifstream finModFile(modFile.c_str());
  cmsys::ifstream finStampFile(stampFile.c_str());
635
#endif
636
  if (!finModFile || !finStampFile) {
637 638
    // At least one of the files does not exist.  The modules differ.
    return true;
639
  }
640

641 642 643 644 645
  /* Compilers which _do_ produce different mod content when the same
   * source is compiled twice
   *   -GNU
   *   -Intel
   *
646 647
   * Eat the stream content until all recompile only related changes
   * are left behind.
648
   */
649
  if (compilerId == "GNU") {
650 651 652 653
    // 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];
654
    bool okay = !finModFile.read(reinterpret_cast<char*>(hdr), 2).fail();
655
    finModFile.seekg(0);
656
    if (!okay || hdr[0] != 0x1f || hdr[1] != 0x8b) {
657
      const char seq[1] = { '\n' };
658
      const int seqlen = 1;
659

660
      if (!cmFortranStreamContainsSequence(finModFile, seq, seqlen)) {
661 662 663 664
        // The module is of unexpected format.  Assume it is different.
        std::cerr << compilerId << " fortran module " << modFile
                  << " has unexpected format." << std::endl;
        return true;
665
      }
666

667
      if (!cmFortranStreamContainsSequence(finStampFile, seq, seqlen)) {
668 669
        // The stamp must differ if the sequence is not contained.
        return true;
670
      }
671
    }
672
  } else if (compilerId == "Intel") {
673
    const char seq[2] = { '\n', '\0' };
674
    const int seqlen = 2;
675

676 677 678 679 680 681
    // 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();

682
    if (!cmFortranStreamContainsSequence(finModFile, seq, seqlen)) {
683 684 685 686
      // The module is of unexpected format.  Assume it is different.
      std::cerr << compilerId << " fortran module " << modFile
                << " has unexpected format." << std::endl;
      return true;
687
    }
688

689
    if (!cmFortranStreamContainsSequence(finStampFile, seq, seqlen)) {
690 691
      // The stamp must differ if the sequence is not contained.
      return true;
692
    }
693
  }
694

695
  // Compare the remaining content.  If no compiler id matched above,
696 697
  // including the case none was given, this will compare the whole
  // content.
698
  return cmFortranStreamsDiffer(finModFile, finStampFile);
699
}
700 701 702 703

std::string cmDependsFortran::MaybeConvertToRelativePath(
  std::string const& base, std::string const& path)
{
704 705
  if (!this->LocalGenerator->GetStateSnapshot().GetDirectory().ContainsBoth(
        base, path)) {
706 707
    return path;
  }
708
  return cmSystemTools::ForceToRelativePath(base, path);
709
}