cmDependsFortran.cxx 39.8 KB
Newer Older
1
2
3
/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4

5
6
  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.
7

8
9
10
11
  This software is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the License for more information.
============================================================================*/
12
13
14
#include "cmDependsFortran.h"

#include "cmSystemTools.h"
15
16
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
17
#include "cmGeneratedFileStream.h"
18
19

#include "cmDependsFortranParser.h" /* Interface to parser object.  */
20
#include <cmsys/FStream.hxx>
21
#include <assert.h>
22
23
#include <stack>

24
25
26
27
// 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.

28
29
30
31
32
//----------------------------------------------------------------------------
// Information about a single source file.
class cmDependsFortranSourceInfo
{
public:
33
  // The name of the source file.
34
35
36
  std::string Source;

  // Set of provided and required modules.
37
38
  std::set<std::string> Provides;
  std::set<std::string> Requires;
39
40

  // Set of files included in the translation unit.
41
  std::set<std::string> Includes;
42
43
};

44
45
46
//----------------------------------------------------------------------------
// Parser methods not included in generated interface.

47
48
49
50
51
// Get the current buffer processed by the lexer.
YY_BUFFER_STATE cmDependsFortranLexer_GetCurrentBuffer(yyscan_t yyscanner);

// The parser entry point.
int cmDependsFortran_yyparse(yyscan_t);
52

53
//----------------------------------------------------------------------------
54
55
56
// Define parser object internal structure.
struct cmDependsFortranFile
{
57
58
59
  cmDependsFortranFile(FILE* file, YY_BUFFER_STATE buffer,
                       const std::string& dir):
    File(file), Buffer(buffer), Directory(dir) {}
60
61
62
63
  FILE* File;
  YY_BUFFER_STATE Buffer;
  std::string Directory;
};
64

65
66
struct cmDependsFortranParser_s
{
67
  cmDependsFortranParser_s(cmDependsFortran* self,
68
                           std::set<std::string>& ppDefines,
69
                           cmDependsFortranSourceInfo& info);
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  ~cmDependsFortranParser_s();

  // Pointer back to the main class.
  cmDependsFortran* Self;

  // Lexical scanner instance.
  yyscan_t Scanner;

  // Stack of open files in the translation unit.
  std::stack<cmDependsFortranFile> FileStack;

  // Buffer for string literals.
  std::string TokenString;

  // Flag for whether lexer is reading from inside an interface.
85
86
87
  bool InInterface;

  int OldStartcond;
88
  std::set<std::string> PPDefinitions;
Brad King's avatar
Brad King committed
89
  size_t InPPFalseBranch;
90
  std::stack<bool> SkipToEnd;
91

92
93
94
  // Information about the parsed source.
  cmDependsFortranSourceInfo& Info;
};
95

96
97
98
99
100
//----------------------------------------------------------------------------
class cmDependsFortranInternals
{
public:
  // The set of modules provided by this target.
101
  std::set<std::string> TargetProvides;
102
103

  // Map modules required by this target to locations.
104
  typedef std::map<std::string, std::string> TargetRequiresMap;
105
106
107
  TargetRequiresMap TargetRequires;

  // Information about each object file.
108
  typedef std::map<std::string, cmDependsFortranSourceInfo> ObjectInfoMap;
109
110
111
112
113
  ObjectInfoMap ObjectInfo;

  cmDependsFortranSourceInfo& CreateObjectInfo(const char* obj,
                                               const char* src)
    {
114
    std::map<std::string, cmDependsFortranSourceInfo>::iterator i =
115
116
117
      this->ObjectInfo.find(obj);
    if(i == this->ObjectInfo.end())
      {
118
      std::map<std::string, cmDependsFortranSourceInfo>::value_type
119
120
121
122
123
124
        entry(obj, cmDependsFortranSourceInfo());
      i = this->ObjectInfo.insert(entry).first;
      i->second.Source = src;
      }
    return i->second;
    }
125
126
127
};

//----------------------------------------------------------------------------
128
cmDependsFortran::cmDependsFortran():
129
  PPDefinitions(0), Internal(0)
130
131
132
133
{
}

//----------------------------------------------------------------------------
134
cmDependsFortran
135
136
::cmDependsFortran(cmLocalGenerator* lg):
  cmDepends(lg),
137
  Internal(new cmDependsFortranInternals)
138
{
139
140
141
  // Configure the include file search path.
  this->SetIncludePathFromLanguage("Fortran");

142
143
144
145
146
147
148
149
150
  // Get the list of definitions.
  std::vector<std::string> definitions;
  cmMakefile* mf = this->LocalGenerator->GetMakefile();
  if(const char* c_defines =
     mf->GetDefinition("CMAKE_TARGET_DEFINITIONS"))
    {
    cmSystemTools::ExpandListArgument(c_defines, definitions);
    }

151
  // translate i.e. FOO=BAR to FOO and add it to the list of defined
152
153
154
155
  // preprocessor symbols
  for(std::vector<std::string>::const_iterator
      it = definitions.begin(); it != definitions.end(); ++it)
    {
156
157
158
    std::string def = *it;
    std::string::size_type assignment = def.find("=");
    if(assignment != std::string::npos)
159
      {
160
      def = it->substr(0, assignment);
161
      }
162
    this->PPDefinitions.push_back(def);
163
    }
164
165
166
167
168
}

//----------------------------------------------------------------------------
cmDependsFortran::~cmDependsFortran()
{
169
  delete this->Internal;
170
171
172
}

//----------------------------------------------------------------------------
173
174
175
bool cmDependsFortran::WriteDependencies(
    const std::set<std::string>& sources, const std::string& obj,
    std::ostream&, std::ostream&)
176
177
{
  // Make sure this is a scanning instance.
178
  if(sources.empty() || sources.begin()->empty())
179
    {
180
    cmSystemTools::Error("Cannot scan dependencies without a source file.");
181
182
    return false;
    }
183
  if(obj.empty())
184
185
186
187
    {
    cmSystemTools::Error("Cannot scan dependencies without an object file.");
    return false;
    }
188

189
190
191
192
193
194
195
196
  bool okay = true;
  for(std::set<std::string>::const_iterator it = sources.begin();
      it != sources.end(); ++it)
    {
    const std::string& src = *it;
    // Get the information object for this source.
    cmDependsFortranSourceInfo& info =
      this->Internal->CreateObjectInfo(obj.c_str(), src.c_str());
197

198
199
200
    // Make a copy of the macros defined via ADD_DEFINITIONS
    std::set<std::string> ppDefines(this->PPDefinitions.begin(),
                                    this->PPDefinitions.end());
201

202
203
204
    // Create the parser object. The constructor takes ppMacro and info per
    // reference, so we may look into the resulting objects later.
    cmDependsFortranParser parser(this, ppDefines, info);
205

206
207
    // Push on the starting file.
    cmDependsFortranParser_FilePush(&parser, src.c_str());
208

209
210
211
212
213
214
    // Parse the translation unit.
    if(cmDependsFortran_yyparse(parser.Scanner) != 0)
      {
      // Failed to parse the file.  Report failure to write dependencies.
      okay = false;
      }
215
    }
216
  return okay;
217
218
219
220
221
222
223
224
225
}

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

226
  // Get the directory in which stamp files will be stored.
227
  const char* stamp_dir = this->TargetDirectory.c_str();
228
229
230
231
232
233
234
235
236
237
238

  // Get the directory in which module files will be created.
  const char* mod_dir;
  cmMakefile* mf = this->LocalGenerator->GetMakefile();
  if(const char* target_mod_dir =
     mf->GetDefinition("CMAKE_Fortran_TARGET_MODULE_DIR"))
    {
    mod_dir = target_mod_dir;
    }
  else
    {
239
240
    mod_dir =
      this->LocalGenerator->GetMakefile()->GetCurrentOutputDirectory();
241
242
    }

243
244
245
246
247
248
249
  // Actually write dependencies to the streams.
  typedef cmDependsFortranInternals::ObjectInfoMap ObjectInfoMap;
  ObjectInfoMap const& objInfo = this->Internal->ObjectInfo;
  for(ObjectInfoMap::const_iterator i = objInfo.begin();
      i != objInfo.end(); ++i)
    {
    if(!this->WriteDependenciesReal(i->first.c_str(), i->second,
250
                                    mod_dir, stamp_dir,
251
252
253
254
255
256
257
258
259
260
261
262
                                    makeDepends, internalDepends))
      {
      return false;
      }
    }

  // Store the list of modules provided by this target.
  std::string fiName = this->TargetDirectory;
  fiName += "/fortran.internal";
  cmGeneratedFileStream fiStream(fiName.c_str());
  fiStream << "# The fortran modules provided by this target.\n";
  fiStream << "provides\n";
263
264
  std::set<std::string> const& provides = this->Internal->TargetProvides;
  for(std::set<std::string>::const_iterator i = provides.begin();
265
266
267
268
      i != provides.end(); ++i)
    {
    fiStream << " " << *i << "\n";
    }
269
270
271
272
273
274
275
276

  // Create a script to clean the modules.
  if(!provides.empty())
    {
    std::string fcName = this->TargetDirectory;
    fcName += "/cmake_clean_Fortran.cmake";
    cmGeneratedFileStream fcStream(fcName.c_str());
    fcStream << "# Remove fortran modules provided by this target.\n";
277
    fcStream << "FILE(REMOVE";
278
    for(std::set<std::string>::const_iterator i = provides.begin();
279
280
        i != provides.end(); ++i)
      {
281
282
283
284
285
286
287
288
289
290
291
292
293
294
      std::string mod_upper = mod_dir;
      mod_upper += "/";
      mod_upper += cmSystemTools::UpperCase(*i);
      mod_upper += ".mod";
      std::string mod_lower = mod_dir;
      mod_lower += "/";
      mod_lower += *i;
      mod_lower += ".mod";
      std::string stamp = stamp_dir;
      stamp += "/";
      stamp += *i;
      stamp += ".mod.stamp";
      fcStream << "\n";
      fcStream << "  \"" <<
Stephen Kelly's avatar
Stephen Kelly committed
295
        this->LocalGenerator->Convert(mod_lower,
296
297
298
                                      cmLocalGenerator::START_OUTPUT)
               << "\"\n";
      fcStream << "  \"" <<
Stephen Kelly's avatar
Stephen Kelly committed
299
        this->LocalGenerator->Convert(mod_upper,
300
301
302
                                      cmLocalGenerator::START_OUTPUT)
               << "\"\n";
      fcStream << "  \"" <<
Stephen Kelly's avatar
Stephen Kelly committed
303
        this->LocalGenerator->Convert(stamp,
304
305
                                      cmLocalGenerator::START_OUTPUT)
               << "\"\n";
306
307
308
      }
    fcStream << "  )\n";
    }
309
310
311
312
313
314
315
316
317
318
319
320
321
  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;
  for(ObjectInfoMap::const_iterator infoI = objInfo.begin();
      infoI != objInfo.end(); ++infoI)
    {
    cmDependsFortranSourceInfo const& info = infoI->second;
322
    for(std::set<std::string>::const_iterator i = info.Provides.begin();
323
324
325
326
327
328
        i != info.Provides.end(); ++i)
      {
      // Include this module in the set provided by this target.
      this->Internal->TargetProvides.insert(*i);
      }

329
    for(std::set<std::string>::const_iterator i = info.Requires.begin();
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
        i != info.Requires.end(); ++i)
      {
      // Include this module in the set required by this target.
      this->Internal->TargetRequires[*i] = "";
      }
    }

  // Short-circuit for simple targets.
  if(this->Internal->TargetRequires.empty())
    {
    return;
    }

  // 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;
  if(const char* infoFilesValue =
     mf->GetDefinition("CMAKE_TARGET_LINKED_INFO_FILES"))
    {
    cmSystemTools::ExpandListArgument(infoFilesValue, infoFiles);
    }
  for(std::vector<std::string>::const_iterator i = infoFiles.begin();
      i != infoFiles.end(); ++i)
    {
    std::string targetDir = cmSystemTools::GetFilenamePath(*i);
    std::string fname = targetDir + "/fortran.internal";
359
    cmsys::ifstream fin(fname.c_str());
360
361
    if(fin)
      {
362
      this->MatchRemoteModules(fin, targetDir.c_str());
363
364
365
366
367
368
369
      }
    }
}

//----------------------------------------------------------------------------
void cmDependsFortran::MatchLocalModules()
{
370
  const char* stampDir = this->TargetDirectory.c_str();
371
372
  std::set<std::string> const& provides = this->Internal->TargetProvides;
  for(std::set<std::string>::const_iterator i = provides.begin();
373
374
      i != provides.end(); ++i)
    {
375
    this->ConsiderModule(i->c_str(), stampDir);
376
377
378
379
380
    }
}

//----------------------------------------------------------------------------
void cmDependsFortran::MatchRemoteModules(std::istream& fin,
381
                                          const char* stampDir)
382
383
{
  std::string line;
384
  bool doing_provides = false;
385
386
387
388
389
390
391
392
393
394
395
396
  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)
        {
397
        this->ConsiderModule(line.c_str()+1, stampDir);
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
        }
      }
    else if(line == "provides")
      {
      doing_provides = true;
      }
    else
      {
      doing_provides = false;
      }
    }
}

//----------------------------------------------------------------------------
void cmDependsFortran::ConsiderModule(const char* name,
413
                                      const char* stampDir)
414
415
416
417
418
419
420
421
422
{
  // Locate each required module.
  typedef cmDependsFortranInternals::TargetRequiresMap TargetRequiresMap;
  TargetRequiresMap::iterator 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.
423
    std::string stampFile = stampDir;
424
425
426
427
428
429
430
431
432
433
434
435
    stampFile += "/";
    stampFile += name;
    stampFile += ".mod.stamp";
    required->second = stampFile;
    }
}

//----------------------------------------------------------------------------
bool
cmDependsFortran
::WriteDependenciesReal(const char *obj,
                        cmDependsFortranSourceInfo const& info,
436
                        const char* mod_dir, const char* stamp_dir,
437
438
439
440
441
442
443
444
                        std::ostream& makeDepends,
                        std::ostream& internalDepends)
{
  typedef cmDependsFortranInternals::TargetRequiresMap TargetRequiresMap;

  // Get the source file for this object.
  const char* src = info.Source.c_str();

445
  // Write the include dependencies to the output stream.
446
  internalDepends << obj << std::endl;
447
  internalDepends << " " << src << std::endl;
448
  for(std::set<std::string>::const_iterator i = info.Includes.begin();
449
      i != info.Includes.end(); ++i)
450
    {
451
    makeDepends << obj << ": " <<
Stephen Kelly's avatar
Stephen Kelly committed
452
      this->LocalGenerator->Convert(*i,
453
454
455
                                    cmLocalGenerator::HOME_OUTPUT,
                                    cmLocalGenerator::MAKEFILE)
                << std::endl;
456
    internalDepends << " " << *i << std::endl;
457
    }
458
  makeDepends << std::endl;
459

460
  // Write module requirements to the output stream.
461
  for(std::set<std::string>::const_iterator i = info.Requires.begin();
462
      i != info.Requires.end(); ++i)
463
464
    {
    // Require only modules not provided in the same source.
465
    if(std::set<std::string>::const_iterator(info.Provides.find(*i)) !=
Brad King's avatar
Brad King committed
466
       info.Provides.end())
467
      {
468
469
470
471
472
473
474
475
476
477
478
      continue;
      }

    // If the module is provided in this target special handling is
    // needed.
    if(this->Internal->TargetProvides.find(*i) !=
       this->Internal->TargetProvides.end())
      {
      // The module is provided by a different source in the same
      // target.  Add the proxy dependency to make sure the other
      // source builds first.
479
      std::string proxy = stamp_dir;
480
481
482
      proxy += "/";
      proxy += *i;
      proxy += ".mod.proxy";
Stephen Kelly's avatar
Stephen Kelly committed
483
      proxy = this->LocalGenerator->Convert(proxy,
484
485
486
                                            cmLocalGenerator::HOME_OUTPUT,
                                            cmLocalGenerator::MAKEFILE);

487
      // since we require some things add them to our list of requirements
488
      makeDepends << obj << ".requires: " << proxy << std::endl;
489
      }
490

491
492
493
494
495
496
497
498
499
    // The object file should depend on timestamped files for the
    // modules it uses.
    TargetRequiresMap::const_iterator 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 =
Stephen Kelly's avatar
Stephen Kelly committed
500
        this->LocalGenerator->Convert(required->second,
501
                                      cmLocalGenerator::HOME_OUTPUT,
502
                                      cmLocalGenerator::MAKEFILE);
503
      makeDepends << obj << ": " << stampFile << "\n";
504
505
506
507
508
509
510
      }
    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))
511
        {
512
        module =
Stephen Kelly's avatar
Stephen Kelly committed
513
          this->LocalGenerator->Convert(module,
514
                                        cmLocalGenerator::HOME_OUTPUT,
515
                                        cmLocalGenerator::MAKEFILE);
516
        makeDepends << obj << ": " << module << "\n";
517
        }
518
519
520
521
      }
    }

  // Write provided modules to the output stream.
522
  for(std::set<std::string>::const_iterator i = info.Provides.begin();
523
      i != info.Provides.end(); ++i)
524
    {
525
    std::string proxy = stamp_dir;
526
527
528
    proxy += "/";
    proxy += *i;
    proxy += ".mod.proxy";
Stephen Kelly's avatar
Stephen Kelly committed
529
    proxy = this->LocalGenerator->Convert(proxy,
530
531
532
                                          cmLocalGenerator::HOME_OUTPUT,
                                          cmLocalGenerator::MAKEFILE);
    makeDepends << proxy << ": " << obj << ".provides" << std::endl;
533
    }
534

535
  // If any modules are provided then they must be converted to stamp files.
536
  if(!info.Provides.empty())
537
    {
538
539
    // Create a target to copy the module after the object file
    // changes.
540
    makeDepends << obj << ".provides.build:\n";
541
    for(std::set<std::string>::const_iterator i = info.Provides.begin();
542
        i != info.Provides.end(); ++i)
543
      {
544
545
546
      // Include this module in the set provided by this target.
      this->Internal->TargetProvides.insert(*i);

547
548
549
      // 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.
550
      std::string m = cmSystemTools::LowerCase(*i);
551
552
553
554
      std::string modFile = mod_dir;
      modFile += "/";
      modFile += *i;
      modFile =
Stephen Kelly's avatar
Stephen Kelly committed
555
        this->LocalGenerator->Convert(modFile,
556
557
                                      cmLocalGenerator::HOME_OUTPUT,
                                      cmLocalGenerator::SHELL);
558
      std::string stampFile = stamp_dir;
559
560
561
562
      stampFile += "/";
      stampFile += m;
      stampFile += ".mod.stamp";
      stampFile =
Stephen Kelly's avatar
Stephen Kelly committed
563
        this->LocalGenerator->Convert(stampFile,
564
565
                                      cmLocalGenerator::HOME_OUTPUT,
                                      cmLocalGenerator::SHELL);
566
      makeDepends << "\t$(CMAKE_COMMAND) -E cmake_copy_f90_mod "
567
568
569
570
571
572
573
574
                  << modFile << " " << stampFile;
      cmMakefile* mf = this->LocalGenerator->GetMakefile();
      const char* cid = mf->GetDefinition("CMAKE_Fortran_COMPILER_ID");
      if(cid && *cid)
        {
        makeDepends << " " << cid;
        }
      makeDepends << "\n";
575
      }
576
577
    // After copying the modules update the timestamp file so that
    // copying will not be done again until the source rebuilds.
578
    makeDepends << "\t$(CMAKE_COMMAND) -E touch " << obj
Bill Hoffman's avatar
Bill Hoffman committed
579
                << ".provides.build\n";
580
581
582

    // Make sure the module timestamp rule is evaluated by the time
    // the target finishes building.
583
584
    std::string driver = this->TargetDirectory;
    driver += "/build";
Stephen Kelly's avatar
Stephen Kelly committed
585
    driver = this->LocalGenerator->Convert(driver,
586
587
588
                                           cmLocalGenerator::HOME_OUTPUT,
                                           cmLocalGenerator::MAKEFILE);
    makeDepends << driver << ": " << obj << ".provides.build\n";
589
590
    }

591
592
593
  return true;
}

594
595
596
597
598
599
600
601
602
603
604
605
606
//----------------------------------------------------------------------------
bool cmDependsFortran::FindModule(std::string const& name,
                                  std::string& module)
{
  // Construct possible names for the module file.
  std::string mod_upper = cmSystemTools::UpperCase(name);
  std::string mod_lower = name;
  mod_upper += ".mod";
  mod_lower += ".mod";

  // Search the include path for the module.
  std::string fullName;
  for(std::vector<std::string>::const_iterator i =
607
        this->IncludePath.begin(); i != this->IncludePath.end(); ++i)
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
    {
    // Try the lower-case name.
    fullName = *i;
    fullName += "/";
    fullName += mod_lower;
    if(cmSystemTools::FileExists(fullName.c_str(), true))
      {
      module = fullName;
      return true;
      }

    // Try the upper-case name.
    fullName = *i;
    fullName += "/";
    fullName += mod_upper;
    if(cmSystemTools::FileExists(fullName.c_str(), true))
      {
      module = fullName;
      return true;
      }
    }
  return false;
}

632
633
634
635
636
637
//----------------------------------------------------------------------------
bool cmDependsFortran::CopyModule(const std::vector<std::string>& args)
{
  // Implements
  //
  //   $(CMAKE_COMMAND) -E cmake_copy_f90_mod input.mod output.mod.stamp
638
  //                                          [compiler-id]
639
640
641
642
643
644
645
646
  //
  // 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];
647
648
649
650
651
  std::string compilerId;
  if(args.size() >= 5)
    {
    compilerId = args[4];
    }
652
653
654
655
656
657
  std::string mod_dir = cmSystemTools::GetFilenamePath(mod);
  if(!mod_dir.empty()) { mod_dir += "/"; }
  std::string mod_upper = mod_dir;
  mod_upper += cmSystemTools::UpperCase(cmSystemTools::GetFilenameName(mod));
  std::string mod_lower = mod_dir;
  mod_lower += cmSystemTools::LowerCase(cmSystemTools::GetFilenameName(mod));
658
659
660
  mod += ".mod";
  mod_upper += ".mod";
  mod_lower += ".mod";
661
  if(cmSystemTools::FileExists(mod_upper.c_str(), true))
662
    {
663
664
    if(cmDependsFortran::ModulesDiffer(mod_upper.c_str(), stamp.c_str(),
                                       compilerId.c_str()))
665
      {
666
667
668
      if(!cmSystemTools::CopyFileAlways(mod_upper.c_str(), stamp.c_str()))
        {
        std::cerr << "Error copying Fortran module from \""
669
                  << mod_upper << "\" to \"" << stamp
670
671
672
                  << "\".\n";
        return false;
        }
673
674
675
      }
    return true;
    }
676
  else if(cmSystemTools::FileExists(mod_lower.c_str(), true))
677
    {
678
679
    if(cmDependsFortran::ModulesDiffer(mod_lower.c_str(), stamp.c_str(),
                                       compilerId.c_str()))
680
      {
681
682
683
      if(!cmSystemTools::CopyFileAlways(mod_lower.c_str(), stamp.c_str()))
        {
        std::cerr << "Error copying Fortran module from \""
684
                  << mod_lower << "\" to \"" << stamp
685
686
687
                  << "\".\n";
        return false;
        }
688
689
690
691
      }
    return true;
    }

692
693
694
  std::cerr << "Error copying Fortran module \"" << args[2]
            << "\".  Tried \"" << mod_upper
            << "\" and \"" << mod_lower << "\".\n";
695
696
697
  return false;
}

698
699
700
701
702
//----------------------------------------------------------------------------
// 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
703
bool cmDependsFortranStreamContainsSequence(std::istream& ifs,
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
                                            const char* 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.
736
737
static bool cmDependsFortranStreamsDiffer(std::istream& ifs1,
                                          std::istream& ifs2)
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
{
  // 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;
}

762
763
//----------------------------------------------------------------------------
bool cmDependsFortran::ModulesDiffer(const char* modFile,
764
765
                                     const char* stampFile,
                                     const char* compilerId)
766
767
{
  /*
768
769
770
771
772
  gnu >= 4.9:
    A mod file is an ascii file compressed with gzip.
    Compiling twice produces identical modules.

  gnu < 4.9:
773
774
775
776
777
778
779
780
781
782
783
    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
784
785
786
    shows that they differ only before a sequence linefeed-zero (0x0A 0x00)
    which is located some bytes in front of the absoulte path to the source
    file.
787
788

  sun:
789
    A mod file is a binary file.  Compiling twice produces identical modules.
790
791

  others:
792
    TODO ...
793
  */
794
795
796
797
798
799


  /* Compilers which do _not_ produce different mod content when the same
   * source is compiled twice
   *   -SunPro
   */
Bill Hoffman's avatar
Bill Hoffman committed
800
  if(strcmp(compilerId, "SunPro") == 0)
801
802
803
804
    {
    return cmSystemTools::FilesDiffer(modFile, stampFile);
    }

805
#if defined(_WIN32) || defined(__CYGWIN__)
806
807
  cmsys::ifstream finModFile(modFile, std::ios::in | std::ios::binary);
  cmsys::ifstream finStampFile(stampFile, std::ios::in | std::ios::binary);
808
#else
809
810
  cmsys::ifstream finModFile(modFile, std::ios::in);
  cmsys::ifstream finStampFile(stampFile, std::ios::in);
811
812
813
814
815
816
817
#endif
  if(!finModFile || !finStampFile)
    {
    // At least one of the files does not exist.  The modules differ.
    return true;
    }

818
819
820
821
822
  /* Compilers which _do_ produce different mod content when the same
   * source is compiled twice
   *   -GNU
   *   -Intel
   *
823
824
   * Eat the stream content until all recompile only related changes
   * are left behind.
825
   */
Bill Hoffman's avatar
Bill Hoffman committed
826
  if (strcmp(compilerId, "GNU") == 0 )
827
    {
828
829
830
831
832
833
834
    // 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)? true:false;
    finModFile.seekg(0);
    if(!(okay && hdr[0] == 0x1f && hdr[1] == 0x8b))
835
      {
836
837
      const char seq[1] = {'\n'};
      const int seqlen = 1;
838

839
840
841
842
843
844
845
846
847
848
849
850
851
      if(!cmDependsFortranStreamContainsSequence(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(!cmDependsFortranStreamContainsSequence(finStampFile, seq, seqlen))
        {
        // The stamp must differ if the sequence is not contained.
        return true;
        }
852
      }
853
    }
Bill Hoffman's avatar
Bill Hoffman committed
854
  else if(strcmp(compilerId, "Intel") == 0)
855
    {
856
857
    const char seq[2] = {'\n', '\0'};
    const int seqlen = 2;
858

859
    if(!cmDependsFortranStreamContainsSequence(finModFile, seq, seqlen))
860
      {
861
862
863
864
      // The module is of unexpected format.  Assume it is different.
      std::cerr << compilerId << " fortran module " << modFile
                << " has unexpected format." << std::endl;
      return true;
865
      }
866
867

    if(!cmDependsFortranStreamContainsSequence(finStampFile, seq, seqlen))
868
      {
869
870
      // The stamp must differ if the sequence is not contained.
      return true;
871
872
873
      }
    }

874
  // Compare the remaining content.  If no compiler id matched above,
875
876
877
878
879
880
881
  // including the case none was given, this will compare the whole
  // content.
  if(!cmDependsFortranStreamsDiffer(finModFile, finStampFile))
    {
    return false;
    }

882
883
884
885
   // The modules are different.
   return true;
}

886
887
888
889
890
891
892
893
894
//----------------------------------------------------------------------------
bool cmDependsFortran::FindIncludeFile(const char* dir,
                                       const char* includeName,
                                       std::string& fileName)
{
  // If the file is a full path, include it directly.
  if(cmSystemTools::FileIsFullPath(includeName))
    {
    fileName = includeName;
895
    return cmSystemTools::FileExists(fileName.c_str(), true);
896
897
898
899
900
901
902
903
    }
  else
    {
    // Check for the file in the directory containing the including
    // file.
    std::string fullName = dir;
    fullName += "/";
    fullName += includeName;
904
    if(cmSystemTools::FileExists(fullName.c_str(), true))
905
906
907
908
909
910
      {
      fileName = fullName;
      return true;
      }

    // Search the include path for the file.
911
    for(std::vector<std::string>::const_iterator i =
912
          this->IncludePath.begin(); i != this->IncludePath.end(); ++i)
913
914
915
916
      {
      fullName = *i;
      fullName += "/";
      fullName += includeName;
917
      if(cmSystemTools::FileExists(fullName.c_str(), true))
918
919
920
921
922
923
924
925
926
927
        {
        fileName = fullName;
        return true;
        }
      }
    }
  return false;
}

//----------------------------------------------------------------------------
928
929
cmDependsFortranParser_s
::cmDependsFortranParser_s(cmDependsFortran* self,
930
                           std::set<std::string>& ppDefines,
931
                           cmDependsFortranSourceInfo& info):
932
  Self(self), PPDefinitions(ppDefines), Info(info)
933
934
{
  this->InInterface = 0;
935
  this->InPPFalseBranch = 0;
936
937

  // Initialize the lexical scanner.
938
939
  cmDependsFortran_yylex_init(&this->Scanner);
  cmDependsFortran_yyset_extra(this, this->Scanner);
940
941
942
943

  // Create a dummy buffer that is never read but is the fallback
  // buffer when the last file is popped off the stack.
  YY_BUFFER_STATE buffer =
944
945
    cmDependsFortran_yy_create_buffer(0, 4, this->Scanner);
  cmDependsFortran_yy_switch_to_buffer(buffer, this->Scanner);
946
947
948
949
950
}

//----------------------------------------------------------------------------
cmDependsFortranParser_s::~cmDependsFortranParser_s()
{
951
  cmDependsFortran_yylex_destroy(this->Scanner);
952
953
954
}

//----------------------------------------------------------------------------
955
bool cmDependsFortranParser_FilePush(cmDependsFortranParser* parser,
956
957
958
959
                                    const char* fname)
{
  // Open the new file and push it onto the stack.  Save the old
  // buffer with it on the stack.
960
  if(FILE* file = cmsys::SystemTools::Fopen(fname, "rb"))
961
962
963
964
    {
    YY_BUFFER_STATE current =
      cmDependsFortranLexer_GetCurrentBuffer(parser->Scanner);
    std::string dir = cmSystemTools::GetParentDirectory(fname);
965
    cmDependsFortranFile f(file, current, dir);
966
    YY_BUFFER_STATE buffer =
967
968
      cmDependsFortran_yy_create_buffer(0, 16384, parser->Scanner);
    cmDependsFortran_yy_switch_to_buffer(buffer, parser->Scanner);
969
970
971
972
973
974
975
976
977
978
    parser->FileStack.push(f);
    return 1;
    }
  else
    {
    return 0;
    }
}

//----------------------------------------------------------------------------
979
bool cmDependsFortranParser_FilePop(cmDependsFortranParser* parser)
980
981
982
983
984
985
986
987
988
989
990
991
992
{
  // Pop one file off the stack and close it.  Switch the lexer back
  // to the next one on the stack.
  if(parser->FileStack.empty())
    {
    return 0;
    }
  else
    {
    cmDependsFortranFile f = parser->FileStack.top(); parser->FileStack.pop();
    fclose(f.File);
    YY_BUFFER_STATE current =
      cmDependsFortranLexer_GetCurrentBuffer(parser->Scanner);
993
994
    cmDependsFortran_yy_delete_buffer(current, parser->Scanner);
    cmDependsFortran_yy_switch_to_buffer(f.Buffer, parser->Scanner);
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
    return 1;
    }
}

//----------------------------------------------------------------------------
int cmDependsFortranParser_Input(cmDependsFortranParser* parser,
                                 char* buffer, size_t bufferSize)
{
  // Read from the file on top of the stack.  If the stack is empty,
  // the end of the translation unit has been reached.
  if(!parser->FileStack.empty())
    {
    FILE* file = parser->FileStack.top().File;
    return (int)fread(buffer, 1, bufferSize, file);
    }
  return 0;
}

//----------------------------------------------------------------------------
void cmDependsFortranParser_StringStart(cmDependsFortranParser* parser)
{
  parser->TokenString = "";
}

//----------------------------------------------------------------------------
const char* cmDependsFortranParser_StringEnd(cmDependsFortranParser* parser)
{
  return parser->TokenString.c_str();
}

//----------------------------------------------------------------------------
void cmDependsFortranParser_StringAppend(cmDependsFortranParser* parser,
                                         char c)
{
  parser->TokenString += c;
}

//----------------------------------------------------------------------------
void cmDependsFortranParser_SetInInterface(cmDependsFortranParser* parser,
1034
                                           bool in)
1035
{
1036
1037
1038
1039
1040
  if(parser->InPPFalseBranch)
    {
    return;
    }

1041
1042
1043
1044
  parser->InInterface = in;
}

//----------------------------------------------------------------------------
1045
bool cmDependsFortranParser_GetInInterface(cmDependsFortranParser* parser)
1046
1047
1048
1049
{
  return parser->InInterface;
}

1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
//----------------------------------------------------------------------------
void cmDependsFortranParser_SetOldStartcond(cmDependsFortranParser* parser,
                                            int arg)
{
  parser->OldStartcond = arg;
}

//----------------------------------------------------------------------------
int cmDependsFortranParser_GetOldStartcond(cmDependsFortranParser* parser)
{
  return parser->OldStartcond;
}

1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
//----------------------------------------------------------------------------
void cmDependsFortranParser_Error(cmDependsFortranParser*, const char*)
{
  // If there is a parser error just ignore it.  The source will not
  // compile and the user will edit it.  Then dependencies will have
  // to be regenerated anyway.
}

//----------------------------------------------------------------------------
void cmDependsFortranParser_RuleUse(cmDependsFortranParser* parser,
                                    const char* name)
{
1075
1076
1077
1078
  if(!parser->InPPFalseBranch)
    {
    parser->Info.Requires.insert(cmSystemTools::LowerCase(name) );
    }
1079
1080
1081
1082
1083
1084
}

//----------------------------------------------------------------------------
void cmDependsFortranParser_RuleInclude(cmDependsFortranParser* parser,
                                        const char* name)
{
1085
1086
1087
1088
1089
  if(parser->InPPFalseBranch)
    {
    return;
    }

1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
  // If processing an include statement there must be an open file.
  assert(!parser->FileStack.empty());

  // Get the directory containing the source in which the include
  // statement appears.  This is always the first search location for
  // Fortran include files.
  std::string dir = parser->FileStack.top().Directory;

  // Find the included file.  If it cannot be found just ignore the
  // problem because either the source will not compile or the user
  // does not care about depending on this included source.
  std::string fullName;
  if(parser->Self->FindIncludeFile(dir.c_str(), name, fullName))
    {
    // Found the included file.  Save it in the set of included files.
1105
    parser->Info.Includes.insert(fullName);
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115

    // Parse it immediately to translate the source inline.
    cmDependsFortranParser_FilePush(parser, fullName.c_str());
    }
}

//----------------------------------------------------------------------------
void cmDependsFortranParser_RuleModule(cmDependsFortranParser* parser,
                                       const char* name)
{
1116
  if(!parser->InPPFalseBranch && !parser->InInterface)
1117
    {
1118
    parser->Info.Provides.insert(cmSystemTools::LowerCase(name));
1119
    }
1120
1121
1122
}

//----------------------------------------------------------------------------
1123
1124
void cmDependsFortranParser_RuleDefine(cmDependsFortranParser* parser,
                                       const char* macro)
1125
{
1126
1127
1128
1129
  if(!parser->InPPFalseBranch)
    {
    parser->PPDefinitions.insert(macro);
    }
1130
1131
1132
}

//----------------------------------------------------------------------------
1133
1134
void cmDependsFortranParser_RuleUndef(cmDependsFortranParser* parser,
                                      const char* macro)
1135
{
1136
1137
1138
1139
1140
1141
1142
1143
1144
  if(!parser->InPPFalseBranch)
    {
    std::set<std::string>::iterator match;
    match = parser->PPDefinitions.find(macro);
    if(match != parser->PPDefinitions.end())
      {
      parser->PPDefinitions.erase(match);
      }
    }
1145
1146
1147
}

//----------------------------------------------------------------------------
1148
1149
void cmDependsFortranParser_RuleIfdef(cmDependsFortranParser* parser,
                                      const char* macro)
1150
{
1151
1152
1153
1154
1155
1156
1157
1158