source: tests/pybin/tools.py@ 8fd52e90

ADT arm-eh ast-experimental enum forall-pointer-decay jacob/cs343-translation jenkins-sandbox new-ast new-ast-unique-expr pthread-emulation qualifiedEnum
Last change on this file since 8fd52e90 was 35a408b7, checked in by Thierry Delisle <tdelisle@…>, 6 years ago

Fixed global timeout

  • Property mode set to 100644
File size: 8.1 KB
Line 
1import __main__
2import argparse
3import contextlib
4import fileinput
5import multiprocessing
6import os
7import re
8import resource
9import signal
10import stat
11import subprocess
12import sys
13import tempfile
14import time
15import types
16
17from pybin import settings
18
19################################################################################
20# shell helpers
21################################################################################
22
23# helper functions to run terminal commands
24def sh(*cmd, timeout = False, output = None, input = None, error = subprocess.STDOUT):
25 cmd = list(cmd)
26
27 # if this is a dry_run, only print the commands that would be ran
28 if settings.dry_run :
29 cmd = "{} cmd: {}".format(os.getcwd(), ' '.join(cmd))
30 if output and not isinstance(output, int):
31 cmd += " > "
32 cmd += output
33
34 if error and not isinstance(error, int):
35 cmd += " 2> "
36 cmd += error
37
38 if input and not isinstance(input, int) and os.path.isfile(input):
39 cmd += " < "
40 cmd += input
41
42 print(cmd)
43 return 0, None
44
45 with contextlib.ExitStack() as onexit:
46 # add input redirection if needed
47 input = openfd(input, 'r', onexit, True)
48
49 # add output redirection if needed
50 output = openfd(output, 'w', onexit, False)
51
52 # add error redirection if needed
53 error = openfd(error, 'w', onexit, False)
54
55 # run the desired command
56 try:
57 proc = subprocess.run(
58 cmd,
59 stdin =input,
60 stdout=output,
61 stderr=error,
62 timeout=settings.timeout.single if timeout else None
63 )
64 return proc.returncode, proc.stdout.decode("utf-8") if proc.stdout else None
65 except subprocess.TimeoutExpired:
66 return 124, str(None)
67
68def is_ascii(fname):
69 if settings.dry_run:
70 print("is_ascii: %s" % fname)
71 return True
72
73 if not os.path.isfile(fname):
74 return False
75
76 code, out = sh("file %s" % fname, output=subprocess.PIPE)
77 if code != 0:
78 return False
79
80 match = re.search(".*: (.*)", out)
81
82 if not match:
83 return False
84
85 return match.group(1).startswith("ASCII text")
86
87def is_exe(fname):
88 return os.path.isfile(fname) and os.access(fname, os.X_OK)
89
90def openfd(file, mode, exitstack, checkfile):
91 if not file:
92 return file
93
94 if isinstance(file, int):
95 return file
96
97 if checkfile and not os.path.isfile(file):
98 return None
99
100 file = open(file, mode)
101 exitstack.push(file)
102 return file
103
104# Remove 1 or more files silently
105def rm( files ):
106 if isinstance(files, str ): files = [ files ]
107 for file in files:
108 sh( 'rm', '-f', file, output=subprocess.DEVNULL, error=subprocess.DEVNULL )
109
110# Create 1 or more directory
111def mkdir( files ):
112 if isinstance(files, str ): files = [ files ]
113 for file in files:
114 p = os.path.normpath( file )
115 d = os.path.dirname ( p )
116 sh( 'mkdir', '-p', d, output=subprocess.DEVNULL, error=subprocess.DEVNULL )
117
118
119def chdir( dest = __main__.__file__ ):
120 abspath = os.path.abspath(dest)
121 dname = os.path.dirname(abspath)
122 os.chdir(dname)
123
124# diff two files
125def diff( lhs, rhs ):
126 # fetch return code and error from the diff command
127 return sh(
128 '''diff''',
129 '''--text''',
130 '''--old-group-format=\t\tmissing lines :\n%<''',
131 '''--new-line-format=\t\t%dn\t%L''',
132 '''--new-group-format=\t\tnew lines : \n%>''',
133 '''--old-line-format=\t\t%dn\t%L''',
134 '''--unchanged-group-format=%=''',
135 '''--changed-group-format=\t\texpected :\n%<\t\tgot :\n%>''',
136 '''--unchanged-line-format=''',
137 lhs,
138 rhs,
139 output=subprocess.PIPE
140 )
141
142# call make
143def make(target, *, flags = '', output = None, error = None, error_file = None, silent = False):
144 test_param = """test="%s" """ % (error_file) if error_file else None
145 cmd = [
146 *settings.make,
147 '-s' if silent else None,
148 test_param,
149 settings.arch.flags,
150 settings.debug.flags,
151 settings.install.flags,
152 flags,
153 target
154 ]
155 cmd = [s for s in cmd if s]
156 return sh(*cmd, output=output, error=error)
157
158def which(program):
159 fpath, fname = os.path.split(program)
160 if fpath:
161 if is_exe(program):
162 return program
163 else:
164 for path in os.environ["PATH"].split(os.pathsep):
165 exe_file = os.path.join(path, program)
166 if is_exe(exe_file):
167 return exe_file
168
169 return None
170
171@contextlib.contextmanager
172def tempdir():
173 cwd = os.getcwd()
174 with tempfile.TemporaryDirectory() as temp:
175 os.chdir(temp)
176 try:
177 yield temp
178 finally:
179 os.chdir(cwd)
180
181def killgroup():
182 try:
183 os.killpg(os.getpgrp(), signal.SIGINT)
184 except KeyboardInterrupt:
185 pass # expected
186 except Exception as exc:
187 print("Unexpected exception", file=sys.stderr)
188 print(exc, file=sys.stderr)
189 sys.stderr.flush()
190 sys.exit(2)
191
192################################################################################
193# file handling
194################################################################################
195# move a file
196def mv(source, dest):
197 ret, _ = sh("mv", source, dest)
198 return ret
199
200# cat one file into the other
201def cat(source, dest):
202 ret, _ = sh("cat", source, output=dest)
203 return ret
204
205# helper function to replace patterns in a file
206def file_replace(fname, pat, s_after):
207 if settings.dry_run:
208 print("replacing '%s' with '%s' in %s" % (pat, s_after, fname))
209 return
210
211 file = fileinput.FileInput(fname, inplace=True, backup='.bak')
212 for line in file:
213 print(line.replace(pat, s_after), end='')
214 file.close()
215
216# helper function to check if a files contains only a specific string
217def file_contains_only(file, text) :
218 with open(file) as f:
219 ff = f.read().strip()
220 result = ff == text.strip()
221
222 return result
223
224# transform path to canonical form
225def canonical_path(path):
226 abspath = os.path.abspath(__main__.__file__)
227 dname = os.path.dirname(abspath)
228 return os.path.join(dname, os.path.normpath(path) )
229
230# compare path even if form is different
231def path_cmp(lhs, rhs):
232 return canonical_path( lhs ) == canonical_path( rhs )
233
234# walk all files in a path
235def path_walk( op ):
236 dname = settings.SRCDIR
237 for dirname, _, names in os.walk(dname):
238 for name in names:
239 path = os.path.join(dirname, name)
240 op( path )
241
242################################################################################
243# system
244################################################################################
245# count number of jobs to create
246def job_count( options, tests ):
247 # check if the user already passed in a number of jobs for multi-threading
248 if not options.jobs:
249 make_flags = os.environ.get('MAKEFLAGS')
250 force = bool(make_flags)
251 make_jobs_fds = re.search("--jobserver-(auth|fds)=\s*([0-9]+),([0-9]+)", make_flags) if make_flags else None
252 if make_jobs_fds :
253 tokens = os.read(int(make_jobs_fds.group(2)), 1024)
254 options.jobs = len(tokens)
255 os.write(int(make_jobs_fds.group(3)), tokens)
256 else :
257 options.jobs = multiprocessing.cpu_count()
258 else :
259 force = True
260
261 # make sure we have a valid number of jobs that corresponds to user input
262 if options.jobs <= 0 :
263 print('ERROR: Invalid number of jobs', file=sys.stderr)
264 sys.exit(1)
265
266 return min( options.jobs, len(tests) ), force
267
268# enable core dumps for all the test children
269resource.setrlimit(resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY))
270
271################################################################################
272# misc
273################################################################################
274
275# check if arguments is yes or no
276def yes_no(string):
277 if string == "yes" :
278 return True
279 if string == "no" :
280 return False
281 raise argparse.ArgumentTypeError(msg)
282
283def fancy_print(text):
284 column = which('column')
285 if column:
286 subprocess.run(column, input=bytes(text + "\n", "UTF-8"))
287 else:
288 print(text)
289
290
291def core_info(path):
292 if not os.path.isfile(path):
293 return 1, "ERR Executable path is wrong"
294
295 cmd = os.path.join(settings.SRCDIR, "pybin/print-core.gdb")
296 if not os.path.isfile(cmd):
297 return 1, "ERR Printing format for core dumps not found"
298
299 core = os.path.join(os.getcwd(), "core" )
300
301 if not os.path.isfile(core):
302 return 1, "ERR No core dump"
303
304 return sh('gdb', '-n', path, core, '-batch', '-x', cmd, output=subprocess.PIPE)
305
306class Timed:
307 def __enter__(self):
308 self.start = time.time()
309 return self
310
311 def __exit__(self, *args):
312 self.end = time.time()
313 self.duration = self.end - self.start
314
315def timed(src, timeout):
316 expire = time.time() + timeout
317 i = iter(src)
318 while True:
319 yield i.next(max(expire - time.time(), 0))
Note: See TracBrowser for help on using the repository browser.