Eliminate Executor's creation and use of a build_dict and a subst_dict, which were...
[scons.git] / src / engine / SCons / Scanner / FortranTests.py
1 #
2 # __COPYRIGHT__
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
25
26 import os
27 import os.path
28 import sys
29 import unittest
30
31 import SCons.Scanner.Fortran
32 import SCons.Node.FS
33 import SCons.Warnings
34
35 import TestCmd
36
37 original = os.getcwd()
38
39 test = TestCmd.TestCmd(workdir = '')
40
41 os.chdir(test.workpath(''))
42
43 # create some source files and headers:
44
45 test.write('fff1.f',"""
46       PROGRAM FOO
47       INCLUDE 'f1.f'
48       include 'f2.f'
49       STOP
50       END
51 """)
52
53 test.write('fff2.f',"""
54       PROGRAM FOO
55       INCLUDE 'f2.f'
56       include 'd1/f2.f'
57       INCLUDE 'd2/f2.f'
58       STOP
59       END
60 """)
61
62 test.write('fff3.f',"""
63       PROGRAM FOO
64       INCLUDE 'f3.f' ; INCLUDE\t'd1/f3.f'
65       STOP
66       END
67 """)
68
69
70 # for Emacs -> "
71
72 test.subdir('d1', ['d1', 'd2'])
73
74 headers = ['fi.f', 'never.f',
75            'd1/f1.f', 'd1/f2.f', 'd1/f3.f', 'd1/fi.f',
76            'd1/d2/f1.f', 'd1/d2/f2.f', 'd1/d2/f3.f',
77            'd1/d2/f4.f', 'd1/d2/fi.f']
78
79 for h in headers:
80     test.write(h, "\n")
81
82
83 test.subdir('include', 'subdir', ['subdir', 'include'])
84
85 test.write('fff4.f',"""
86       PROGRAM FOO
87       INCLUDE 'f4.f'
88       STOP
89       END
90 """)
91
92 test.write('include/f4.f', "\n")
93 test.write('subdir/include/f4.f', "\n")
94
95 test.write('fff5.f',"""
96       PROGRAM FOO
97       INCLUDE 'f5.f'
98       INCLUDE 'not_there.f'
99       STOP
100       END
101 """)
102
103 test.write('f5.f', "\n")
104
105 test.subdir('repository', ['repository', 'include'],
106             [ 'repository', 'src' ])
107 test.subdir('work', ['work', 'src'])
108
109 test.write(['repository', 'include', 'iii.f'], "\n")
110
111 test.write(['work', 'src', 'fff.f'], """
112       PROGRAM FOO
113       INCLUDE 'iii.f'
114       INCLUDE 'jjj.f'
115       STOP
116       END
117 """)
118
119 test.write([ 'work', 'src', 'aaa.f'], """
120       PROGRAM FOO
121       INCLUDE 'bbb.f'
122       STOP
123       END
124 """)
125
126 test.write([ 'work', 'src', 'bbb.f'], "\n")
127
128 test.write([ 'repository', 'src', 'ccc.f'], """
129       PROGRAM FOO
130       INCLUDE 'ddd.f'
131       STOP
132       END
133 """)
134
135 test.write([ 'repository', 'src', 'ddd.f'], "\n")
136
137
138 test.write('fff90a.f90',"""
139       PROGRAM FOO
140
141 !  Test comments - these includes should NOT be picked up
142 C     INCLUDE 'fi.f'
143 #     INCLUDE 'fi.f'
144   !   INCLUDE 'fi.f'
145
146       INCLUDE 'f1.f'  ! in-line comments are valid syntax
147       INCLUDE"fi.f"   ! space is significant - this should be ignored
148       INCLUDE  <f2.f>  ! Absoft compiler allows greater than/less than delimiters
149 !
150 !  Allow kind type parameters
151       INCLUDE kindType_"f3.f"
152       INCLUDE kind_Type_"f4.f"
153 !
154 !  Test multiple statements per line - use various spacings between semicolons
155       incLUDE 'f5.f';include "f6.f"  ;  include <f7.f>; include 'f8.f' ;include kindType_'f9.f'
156 !
157 !  Test various USE statement syntaxes
158 !
159       USE Mod01
160       use mod02
161       use use
162       USE mOD03, ONLY : someVar
163       USE MOD04 ,only:someVar
164       USE Mod05 , ONLY: someVar ! in-line comment
165       USE Mod06,ONLY :someVar,someOtherVar
166
167       USE  mod07;USE  mod08; USE mod09 ;USE mod10 ; USE mod11  ! Test various semicolon placements
168       use mod12 ;use mod13! Test comment at end of line
169
170 !     USE modi
171 !     USE modia ; use modib    ! Scanner regexp will only ignore the first - this is a deficiency in the regexp
172     ! USE modic ; ! use modid  ! Scanner regexp should ignore both modules
173       USE mod14 !; USE modi    ! Only ignore the second
174       USE mod15!;USE modi
175       USE mod16  !  ;  USE  modi
176
177 !  Test semicolon syntax - use various spacings
178       USE :: mod17
179       USE::mod18
180       USE ::mod19 ; USE:: mod20
181
182       use, non_intrinsic :: mod21, ONLY : someVar ; use,intrinsic:: mod22
183       USE, NON_INTRINSIC::mod23 ; USE ,INTRINSIC ::mod24
184
185 USE mod25  ! Test USE statement at the beginning of line
186
187
188 ; USE modi   ! Scanner should ignore this since it isn't valid syntax
189       USEmodi   ! No space in between USE and module name - ignore it
190       USE mod01   ! This one is a duplicate - there should only be one dependency to it.
191
192       STOP
193       END
194 """)
195
196 modules = ['mod01.mod', 'mod02.mod', 'mod03.mod', 'mod04.mod', 'mod05.mod',
197            'mod06.mod', 'mod07.mod', 'mod08.mod', 'mod09.mod', 'mod10.mod',
198            'mod11.mod', 'mod12.mod', 'mod13.mod', 'mod14.mod', 'mod15.mod',
199            'mod16.mod', 'mod17.mod', 'mod18.mod', 'mod19.mod', 'mod20.mod',
200            'mod21.mod', 'mod22.mod', 'mod23.mod', 'mod24.mod', 'mod25.mod']
201
202 for m in modules:
203     test.write(m, "\n")
204
205 test.subdir('modules')
206 test.write(['modules', 'use.mod'], "\n")
207
208 # define some helpers:
209
210 class DummyEnvironment:
211     def __init__(self, listCppPath):
212         self.path = listCppPath
213
214     def Dictionary(self, *args):
215         if not args:
216             return { 'FORTRANPATH': self.path, 'FORTRANMODSUFFIX' : ".mod" }
217         elif len(args) == 1 and args[0] == 'FORTRANPATH':
218             return self.path
219         else:
220             raise KeyError, "Dummy environment only has FORTRANPATH attribute."
221
222     def has_key(self, key):
223         return self.Dictionary().has_key(key)
224
225     def __getitem__(self,key):
226         return self.Dictionary()[key]
227
228     def __setitem__(self,key,value):
229         self.Dictionary()[key] = value
230
231     def __delitem__(self,key):
232         del self.Dictionary()[key]
233
234     def subst(self, arg):
235         if arg[0] == '$':
236             return self[arg[1:]]
237         return arg
238
239     def subst_path(self, path, target=None):
240         if type(path) != type([]):
241             path = [path]
242         return map(self.subst, path)
243
244     def get_calculator(self):
245         return None
246
247 def deps_match(self, deps, headers):
248     scanned = map(os.path.normpath, map(str, deps))
249     expect = map(os.path.normpath, headers)
250     self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
251
252 def make_node(filename, fs=SCons.Node.FS.default_fs):
253     return fs.File(test.workpath(filename))
254
255 # define some tests:
256
257 class FortranScannerTestCase1(unittest.TestCase):
258     def runTest(self):
259         test.write('f1.f', "\n")
260         test.write('f2.f', "      INCLUDE 'fi.f'\n")
261         env = DummyEnvironment([])
262         s = SCons.Scanner.Fortran.FortranScan()
263         path = s.path(env)
264         fs = SCons.Node.FS.FS(original)
265         deps = s(make_node('fff1.f', fs), env, path)
266         headers = ['f1.f', 'f2.f']
267         deps_match(self, deps, map(test.workpath, headers))
268         test.unlink('f1.f')
269         test.unlink('f2.f')
270
271 class FortranScannerTestCase2(unittest.TestCase):
272     def runTest(self):
273         test.write('f1.f', "\n")
274         test.write('f2.f', "      INCLUDE 'fi.f'\n")
275         env = DummyEnvironment([test.workpath("d1")])
276         s = SCons.Scanner.Fortran.FortranScan()
277         path = s.path(env)
278         fs = SCons.Node.FS.FS(original)
279         deps = s(make_node('fff1.f', fs), env, path)
280         headers = ['f1.f', 'f2.f']
281         deps_match(self, deps, map(test.workpath, headers))
282         test.unlink('f1.f')
283         test.unlink('f2.f')
284
285 class FortranScannerTestCase3(unittest.TestCase):
286     def runTest(self):
287         env = DummyEnvironment([test.workpath("d1")])
288         s = SCons.Scanner.Fortran.FortranScan()
289         path = s.path(env)
290         fs = SCons.Node.FS.FS(original)
291         deps = s(make_node('fff1.f', fs), env, path)
292         headers = ['d1/f1.f', 'd1/f2.f']
293         deps_match(self, deps, map(test.workpath, headers))
294
295 class FortranScannerTestCase4(unittest.TestCase):
296     def runTest(self):
297         test.write(['d1', 'f2.f'], "      INCLUDE 'fi.f'\n")
298         env = DummyEnvironment([test.workpath("d1")])
299         s = SCons.Scanner.Fortran.FortranScan()
300         path = s.path(env)
301         fs = SCons.Node.FS.FS(original)
302         deps = s(make_node('fff1.f', fs), env, path)
303         headers = ['d1/f1.f', 'd1/f2.f']
304         deps_match(self, deps, map(test.workpath, headers))
305         test.write(['d1', 'f2.f'], "\n")
306
307 class FortranScannerTestCase5(unittest.TestCase):
308     def runTest(self):
309         env = DummyEnvironment([test.workpath("d1")])
310         s = SCons.Scanner.Fortran.FortranScan()
311         path = s.path(env)
312         fs = SCons.Node.FS.FS(original)
313         deps = s(make_node('fff2.f', fs), env, path)
314         headers = ['d1/f2.f', 'd1/d2/f2.f', 'd1/f2.f']
315         deps_match(self, deps, map(test.workpath, headers))
316
317 class FortranScannerTestCase6(unittest.TestCase):
318     def runTest(self):
319         test.write('f2.f', "\n")
320         env = DummyEnvironment([test.workpath("d1")])
321         s = SCons.Scanner.Fortran.FortranScan()
322         path = s.path(env)
323         fs = SCons.Node.FS.FS(original)
324         deps = s(make_node('fff2.f', fs), env, path)
325         headers =  ['d1/f2.f', 'd1/d2/f2.f', 'f2.f']
326         deps_match(self, deps, map(test.workpath, headers))
327         test.unlink('f2.f')
328
329 class FortranScannerTestCase7(unittest.TestCase):
330     def runTest(self):
331         env = DummyEnvironment([test.workpath("d1/d2"), test.workpath("d1")])
332         s = SCons.Scanner.Fortran.FortranScan()
333         path = s.path(env)
334         fs = SCons.Node.FS.FS(original)
335         deps = s(make_node('fff2.f', fs), env, path)
336         headers =  ['d1/f2.f', 'd1/d2/f2.f', 'd1/d2/f2.f']
337         deps_match(self, deps, map(test.workpath, headers))
338
339 class FortranScannerTestCase8(unittest.TestCase):
340     def runTest(self):
341         test.write('f2.f', "\n")
342         env = DummyEnvironment([test.workpath("d1/d2"), test.workpath("d1")])
343         s = SCons.Scanner.Fortran.FortranScan()
344         path = s.path(env)
345         fs = SCons.Node.FS.FS(original)
346         deps = s(make_node('fff2.f', fs), env, path)
347         headers =  ['d1/f2.f', 'd1/d2/f2.f', 'f2.f']
348         deps_match(self, deps, map(test.workpath, headers))
349         test.unlink('f2.f')
350
351 class FortranScannerTestCase9(unittest.TestCase):
352     def runTest(self):
353         test.write('f3.f', "\n")
354         env = DummyEnvironment([])
355         s = SCons.Scanner.Fortran.FortranScan()
356         path = s.path(env)
357
358         n = make_node('fff3.f')
359         def my_rexists(s=n):
360             s.rexists_called = 1
361             return s.old_rexists()
362         setattr(n, 'old_rexists', n.rexists)
363         setattr(n, 'rexists', my_rexists)
364
365         deps = s(n, env, path)
366
367         # Make sure rexists() got called on the file node being
368         # scanned, essential for cooperation with BuildDir functionality.
369         assert n.rexists_called
370
371         headers =  ['d1/f3.f', 'f3.f']
372         deps_match(self, deps, map(test.workpath, headers))
373         test.unlink('f3.f')
374
375 class FortranScannerTestCase10(unittest.TestCase):
376     def runTest(self):
377         fs = SCons.Node.FS.FS(test.workpath(''))
378         env = DummyEnvironment(["include"])
379         s = SCons.Scanner.Fortran.FortranScan(fs = fs)
380         path = s.path(env)
381         deps1 = s(fs.File('fff4.f'), env, path)
382         fs.chdir(fs.Dir('subdir'))
383         dir = fs.getcwd()
384         fs.chdir(fs.Dir('..'))
385         path = s.path(env, dir)
386         deps2 = s(fs.File('#fff4.f'), env, path)
387         headers1 =  ['include/f4.f']
388         headers2 =  ['subdir/include/f4.f']
389         deps_match(self, deps1, headers1)
390         deps_match(self, deps2, headers2)
391
392 class FortranScannerTestCase11(unittest.TestCase):
393     def runTest(self):
394         SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning)
395         class TestOut:
396             def __call__(self, x):
397                 self.out = x
398
399         to = TestOut()
400         to.out = None
401         SCons.Warnings._warningOut = to
402         fs = SCons.Node.FS.FS(test.workpath(''))
403         env = DummyEnvironment([])
404         s = SCons.Scanner.Fortran.FortranScan(fs=fs)
405         path = s.path(env)
406         deps = s(fs.File('fff5.f'), env, path)
407
408         # Did we catch the warning from not finding not_there.f?
409         assert to.out
410
411         deps_match(self, deps, [ 'f5.f' ])
412
413 class FortranScannerTestCase12(unittest.TestCase):
414     def runTest(self):
415         fs = SCons.Node.FS.FS(test.workpath(''))
416         fs.chdir(fs.Dir('include'))
417         env = DummyEnvironment([])
418         s = SCons.Scanner.Fortran.FortranScan(fs=fs)
419         path = s.path(env)
420         test.write('include/fff4.f', test.read('fff4.f'))
421         deps = s(fs.File('#include/fff4.f'), env, path)
422         fs.chdir(fs.Dir('..'))
423         deps_match(self, deps, ['include/f4.f'])
424         test.unlink('include/fff4.f')
425
426 class FortranScannerTestCase13(unittest.TestCase):
427     def runTest(self):
428         os.chdir(test.workpath('work'))
429         fs = SCons.Node.FS.FS(test.workpath('work'))
430         fs.Repository(test.workpath('repository'))
431
432         # Create a derived file in a directory that does not exist yet.
433         # This was a bug at one time.
434         f1=fs.File('include2/jjj.f')
435         f1.builder=1
436         env = DummyEnvironment(['include','include2'])
437         s = SCons.Scanner.Fortran.FortranScan(fs=fs)
438         path = s.path(env)
439         deps = s(fs.File('src/fff.f'), env, path)
440         deps_match(self, deps, [test.workpath('repository/include/iii.f'), 'include2/jjj.f'])
441         os.chdir(test.workpath(''))
442
443 class FortranScannerTestCase14(unittest.TestCase):
444     def runTest(self):
445         os.chdir(test.workpath('work'))
446         fs = SCons.Node.FS.FS(test.workpath('work'))
447         fs.BuildDir('build1', 'src', 1)
448         fs.BuildDir('build2', 'src', 0)
449         fs.Repository(test.workpath('repository'))
450         env = DummyEnvironment([])
451         s = SCons.Scanner.Fortran.FortranScan(fs = fs)
452         path = s.path(env)
453         deps1 = s(fs.File('build1/aaa.f'), env, path)
454         deps_match(self, deps1, [ 'build1/bbb.f' ])
455         deps2 = s(fs.File('build2/aaa.f'), env, path)
456         deps_match(self, deps2, [ 'src/bbb.f' ])
457         deps3 = s(fs.File('build1/ccc.f'), env, path)
458         deps_match(self, deps3, [ 'build1/ddd.f' ])
459         deps4 = s(fs.File('build2/ccc.f'), env, path)
460         deps_match(self, deps4, [ test.workpath('repository/src/ddd.f') ])
461         os.chdir(test.workpath(''))
462
463 class FortranScannerTestCase15(unittest.TestCase):
464     def runTest(self):
465         class SubstEnvironment(DummyEnvironment):
466             def subst(self, arg, test=test):
467                 return test.workpath("d1")
468         test.write(['d1', 'f2.f'], "      INCLUDE 'fi.f'\n")
469         env = SubstEnvironment(["junk"])
470         s = SCons.Scanner.Fortran.FortranScan()
471         path = s.path(env)
472         fs = SCons.Node.FS.FS(original)
473         deps = s(make_node('fff1.f', fs), env, path)
474         headers = ['d1/f1.f', 'd1/f2.f']
475         deps_match(self, deps, map(test.workpath, headers))
476         test.write(['d1', 'f2.f'], "\n")
477
478 class FortranScannerTestCase16(unittest.TestCase):
479     def runTest(self):
480         test.write('f1.f', "\n")
481         test.write('f2.f', "\n")
482         test.write('f3.f', "\n")
483         test.write('f4.f', "\n")
484         test.write('f5.f', "\n")
485         test.write('f6.f', "\n")
486         test.write('f7.f', "\n")
487         test.write('f8.f', "\n")
488         test.write('f9.f', "\n")
489         test.write('f10.f', "\n")
490         env = DummyEnvironment([test.workpath('modules')])
491         s = SCons.Scanner.Fortran.FortranScan()
492         path = s.path(env)
493         fs = SCons.Node.FS.FS(original)
494         deps = s(make_node('fff90a.f90', fs), env, path)
495         headers = ['f1.f', 'f2.f', 'f3.f', 'f4.f', 'f5.f', 'f6.f', 'f7.f', 'f8.f', 'f9.f']
496         modules = ['mod01.mod', 'mod02.mod', 'mod03.mod', 'mod04.mod', 'mod05.mod',
497                    'mod06.mod', 'mod07.mod', 'mod08.mod', 'mod09.mod', 'mod10.mod',
498                    'mod11.mod', 'mod12.mod', 'mod13.mod', 'mod14.mod', 'mod15.mod',
499                    'mod16.mod', 'mod17.mod', 'mod18.mod', 'mod19.mod', 'mod20.mod',
500                    'mod21.mod', 'mod22.mod', 'mod23.mod', 'mod24.mod', 'mod25.mod', 'modules/use.mod']
501         deps_expected = headers + modules
502         deps_match(self, deps, map(test.workpath, deps_expected))
503         test.unlink('f1.f')
504         test.unlink('f2.f')
505         test.unlink('f3.f')
506         test.unlink('f4.f')
507         test.unlink('f5.f')
508         test.unlink('f6.f')
509         test.unlink('f7.f')
510         test.unlink('f8.f')
511         test.unlink('f9.f')
512         test.unlink('f10.f')
513
514 def suite():
515     suite = unittest.TestSuite()
516     suite.addTest(FortranScannerTestCase1())
517     suite.addTest(FortranScannerTestCase2())
518     suite.addTest(FortranScannerTestCase3())
519     suite.addTest(FortranScannerTestCase4())
520     suite.addTest(FortranScannerTestCase5())
521     suite.addTest(FortranScannerTestCase6())
522     suite.addTest(FortranScannerTestCase7())
523     suite.addTest(FortranScannerTestCase8())
524     suite.addTest(FortranScannerTestCase9())
525     suite.addTest(FortranScannerTestCase10())
526     suite.addTest(FortranScannerTestCase11())
527     suite.addTest(FortranScannerTestCase12())
528     suite.addTest(FortranScannerTestCase13())
529     suite.addTest(FortranScannerTestCase14())
530     suite.addTest(FortranScannerTestCase15())
531     suite.addTest(FortranScannerTestCase16())
532     return suite
533
534 if __name__ == "__main__":
535     runner = unittest.TextTestRunner()
536     result = runner.run(suite())
537     if not result.wasSuccessful():
538         sys.exit(1)