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

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 8abca06 was 0f5da65, checked in by Peter A. Buhr <pabuhr@…>, 6 years ago

formatting, fix python 3.7 StopIteration problem

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