Skip to content

Commit b06ab42

Browse files
author
Tarun Prabhu
committed
Initial commit of script to generate (and eventually update) a static test configuration file for the gfortran tests.
1 parent d680839 commit b06ab42

File tree

1 file changed

+224
-0
lines changed

1 file changed

+224
-0
lines changed
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
#!/usr/bin/env python3
2+
3+
import chardet
4+
import os
5+
import re
6+
7+
# Class representing a single test.
8+
class Test:
9+
UNKNOWN = 0
10+
PREPROCESS = 1
11+
COMPILE = 2
12+
LINK = 3
13+
RUN = 4
14+
15+
# int, [os.path], [str], [str], bool
16+
def __init__(self, kind, sources, options, targets, expected_fail):
17+
self.kind = kind
18+
19+
# The sources needed by the test. This will have at least one element.
20+
# The first element of the list will be the "main" file. The rest must
21+
# be in the order in which they should be compiled. The elements will be
22+
# the basename's of the file because all dependent files are in the
23+
# same directory, so there is no need to have the full (or relative)
24+
# path.
25+
self.sources = sources
26+
27+
# The command-line options needed by the test.
28+
self.options = options
29+
30+
# The optional targets for the test.
31+
self.target = targets
32+
33+
# Whether the test is expected to fail.
34+
self.expected_fail = expected_fail
35+
36+
# Whether to use separate compilation for this test (is this even
37+
# necessary?)
38+
self.separate_compilation = False
39+
40+
def __str__(self):
41+
m = ['', 'PREPROCESS', 'COMPILE', 'LINK', 'RUN']
42+
return '{}: {}'.format(m[self.kind], self.sources)
43+
44+
# Checks if the basename of a file has a Fortran extension.
45+
re_fortran = re.compile('^.+[.][Ff].*$')
46+
47+
# Checks if the line has a preprocess annotation.
48+
re_preprocess = re.compile('[{][ ]*dg-do preprocess[ ]*[}]')
49+
50+
# Checks if the line has a compile annotation.
51+
# FIXME: Add match for target
52+
re_compile = re.compile('[{][ ]*dg-do[ ]*compile[ ]*(.*)[}]')
53+
54+
# Checks if the line has a link annotation.
55+
re_link = re.compile('[{][ ]*dg-do[ ]*link[ ]+(.*)[}]')
56+
57+
# Checks if the line has a run annotation or lto-run annotation.
58+
# FIXME: Add match for target
59+
re_run = re.compile('[{][ ]*dg-(lto-)?do[ ]*run[ ]+(.*)[}]')
60+
61+
# Checks if the line has an additional-sources annotation.
62+
re_sources = re.compile('[{][ ]*dg-additional-sources[ ]*["]?([^"])["]?[ ]*[}]')
63+
64+
# Checks if the line has a dg-compile-aux-modules annotation.
65+
re_aux_modules = re.compile(
66+
'[{][ ]*dg-compile-aux-modules[ ]*["]?([^"])["]?[}]'
67+
)
68+
69+
# Checks if the line has an options or additional-options annotation. The
70+
# option may have an optional target.
71+
re_options = re.compile(
72+
'[{][ ]*dg-(additional-)?options[ ]*["]?([^\"]*)["]?[ ]*[}][ ]*([{][ ]*target[ ]*(.+)?[ ][}])?'
73+
)
74+
75+
# Checks if the line has a shouldfail annotation.
76+
re_shouldfail = re.compile('[{][ ]*dg-shouldfail[ ]*.*[}]')
77+
78+
# Checks if the line has a dg-error annotation.
79+
# TODO: There may be
80+
re_error = re.compile('[{][ ]*dg-error[ ]*[}]')
81+
82+
# Get the n-th level ancestor of the given file. The 1st level ancestor is
83+
# the directory containing the file. The 2nd level ancestor is the parent of
84+
# that directory and so on.
85+
# os.path, int -> os.path
86+
def get_ancestor(f, n):
87+
anc = f
88+
for _ in range(0, n):
89+
anc = os.path.dirname(anc)
90+
return anc
91+
92+
# Get the encoding of the file.
93+
# os.path -> str
94+
def get_encoding(filepath):
95+
with open(filepath, 'rb') as f:
96+
return chardet.detect(f.read())['encoding']
97+
return None
98+
99+
# Get the lines in the file.
100+
# os.path -> [str]
101+
def get_lines(filepath):
102+
lines = []
103+
try:
104+
with open(filepath, 'r', encoding = get_encoding(filepath)) as f:
105+
lines = f.readlines()
106+
except:
107+
print('WARNING: Could not open file:', os.path.basename(filepath))
108+
finally:
109+
return lines
110+
111+
# Collect the subdirectories of the gfortran directory which may contain tests.
112+
# os.path -> [os.path]
113+
def get_subdirs(gfortran):
114+
regression = os.path.join(gfortran, 'regression')
115+
torture = os.path.join(gfortran, 'torture')
116+
117+
subdirs = [regression]
118+
for root, dirs, _ in os.walk(regression):
119+
subdirs.extend([os.path.join(root, d) for d in dirs])
120+
subdirs.append(torture)
121+
for root, dirs, _ in os.walk(torture):
122+
subdirs.extend([os.path.join(root, d) for d in dirs])
123+
return subdirs
124+
125+
# Try to match the line with the regex. If the line matches, add the match
126+
# object to the MOUT list and return True. Otherwise, leave the MOUT list
127+
# unchanged and return False.
128+
# re, str, [re.MATCH] -> bool
129+
def try_match(regex, line, mout):
130+
m = regex.search(line)
131+
if m:
132+
mout.append(m)
133+
return True
134+
return False
135+
136+
# () -> int
137+
def main():
138+
root = get_ancestor(os.path.realpath(__file__), 4)
139+
gfortran = os.path.join(root, 'Fortran', 'gfortran')
140+
subdirs = get_subdirs(gfortran)
141+
142+
for subdir in subdirs:
143+
print(subdir)
144+
files = []
145+
for e in os.scandir(subdir):
146+
if e.is_file() and re_fortran.match(e.name):
147+
files.append(e.path)
148+
149+
# Find all the files that are dependencies of some file that is the
150+
# main file in a test.
151+
dependencies = set([])
152+
for filename in files:
153+
for l in get_lines(filename):
154+
mout = []
155+
if try_match(re_sources, l, mout):
156+
m = mout[0]
157+
d = os.path.dirname(filename)
158+
for src in m[1].split():
159+
dependencies.add(os.path.join(d, src))
160+
print(dependencies)
161+
return 0
162+
163+
tests = []
164+
for f in files:
165+
if f in dependencies:
166+
continue
167+
filename = os.path.basename(f)
168+
kind = None
169+
sources = [filename]
170+
options = []
171+
targets = []
172+
expect_error = False
173+
174+
for l in get_lines(f):
175+
mout = []
176+
if try_match(re_preprocess, l, mout):
177+
kind = Test.PREPROCESS
178+
elif try_match(re_compile, l, mout):
179+
kind = Test.COMPILE
180+
# TODO: Handle the optional target.
181+
elif try_match(re_link, l, mout):
182+
kind = Test.LINK
183+
# TODO: Assume that this has an optional target.
184+
elif try_match(re_run, l, mout):
185+
kind = Test.RUN
186+
# TODO: Handle the optional target.
187+
elif try_match(re_shouldfail, l, mout) or \
188+
try_match(re_error, l, mout):
189+
expect_error = True
190+
elif try_match(re_sources, l, mout) or \
191+
try_match(re_aux_modules, l, mout):
192+
m = mout[0]
193+
sources.extend(m[1].split())
194+
elif try_match(re_options, l, mout):
195+
m = mout[0]
196+
options.extend(m[2].split())
197+
# TODO: Handle the optional target.
198+
if kind:
199+
test = Test(kind, sources, options, targets, expect_error)
200+
tests.append(test)
201+
elif len(sources) > 1:
202+
print(
203+
' WARNING: Additional sources without action annotation:',
204+
filename
205+
)
206+
elif len(options) > 1:
207+
print(
208+
' WARNING: Compile options without action annotation:',
209+
filename
210+
)
211+
elif expect_error:
212+
print(
213+
' WARNING: Expect error set without action annotation',
214+
filename
215+
)
216+
else:
217+
pass
218+
# print(' WARNING: No action annotation:', filename)
219+
for t in tests:
220+
print(str(t))
221+
# print(' ', len(tests))
222+
223+
if __name__ == '__main__':
224+
exit(main())

0 commit comments

Comments
 (0)