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
RevLine 
[c07d724]1import __main__
2import argparse
[a45fc7b]3import contextlib
[dcfedca]4import datetime
[0c13238]5import fileinput
[bacc36c]6import multiprocessing
[c07d724]7import os
8import re
[0c13238]9import resource
[bacc36c]10import signal
[c07d724]11import stat
[1bb2488]12import subprocess
[bacc36c]13import sys
[f806b61]14import tempfile
[0c13238]15import time
[5b993e0]16import types
[c07d724]17
[bacc36c]18from pybin import settings
[c07d724]19
[bacc36c]20################################################################################
21# shell helpers
22################################################################################
23
[c07d724]24# helper functions to run terminal commands
[eea20cd]25def sh(*cmd, timeout = False, output_file = None, input_file = None, input_text = None, error = subprocess.STDOUT, ignore_dry_run = False):
[8364209]26 try:
27 cmd = list(cmd)
[1bb2488]28
[8364209]29 if input_file and input_text:
30 return 401, "Cannot use both text and file inputs"
[d65f92c]31
[8364209]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
[ea62265]38
[8364209]39 if error and not isinstance(error, int):
40 cmd += " 2> "
41 cmd += error
[ea62265]42
[8364209]43 if input_file and not isinstance(input_file, int) and os.path.isfile(input_file):
44 cmd += " < "
45 cmd += input_file
[ea62265]46
[8364209]47 print(cmd)
48 return 0, None
[bacc36c]49
[8364209]50 with contextlib.ExitStack() as onexit:
51 # add input redirection if needed
52 input_file = openfd(input_file, 'r', onexit, True)
[a45fc7b]53
[8364209]54 # add output redirection if needed
55 output_file = openfd(output_file, 'w', onexit, False)
[f806b61]56
[8364209]57 # add error redirection if needed
58 error = openfd(error, 'w', onexit, False)
[a45fc7b]59
[8364209]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(
[2b10f95]64 cmd,
[d65f92c]65 **({'input' : bytes(input_text, encoding='utf-8')} if input_text else {'stdin' : input_file}),
66 stdout = output_file,
[8364209]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
[c07d724]84
[f85bc15]85def is_ascii(fname):
[202ad72]86 if settings.dry_run:
87 print("is_ascii: %s" % fname)
88 return True
89
[f85bc15]90 if not os.path.isfile(fname):
91 return False
92
[d65f92c]93 code, out = sh("file %s" % fname, output_file=subprocess.PIPE)
[f85bc15]94 if code != 0:
95 return False
96
97 match = re.search(".*: (.*)", out)
98
99 if not match:
100 return False
101
[e1bdccb]102 return match.group(1).startswith("ASCII text")
[f85bc15]103
[5bf1f3e]104def is_exe(fname):
105 return os.path.isfile(fname) and os.access(fname, os.X_OK)
106
[f806b61]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
[c07d724]121# Remove 1 or more files silently
[bacc36c]122def rm( files ):
[5b993e0]123 if isinstance(files, str ): files = [ files ]
124 for file in files:
[d65f92c]125 sh( 'rm', '-f', file, output_file=subprocess.DEVNULL, error=subprocess.DEVNULL )
[c07d724]126
[a95c117]127# Create 1 or more directory
128def mkdir( files ):
[5b993e0]129 if isinstance(files, str ): files = [ files ]
130 for file in files:
131 p = os.path.normpath( file )
132 d = os.path.dirname ( p )
[d65f92c]133 sh( 'mkdir', '-p', d, output_file=subprocess.DEVNULL, error=subprocess.DEVNULL )
[28582b2]134
[a95c117]135
[c07d724]136def chdir( dest = __main__.__file__ ):
137 abspath = os.path.abspath(dest)
138 dname = os.path.dirname(abspath)
139 os.chdir(dname)
140
[bacc36c]141# diff two files
142def diff( lhs, rhs ):
143 # fetch return code and error from the diff command
[a45fc7b]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,
[d65f92c]156 output_file=subprocess.PIPE
[a45fc7b]157 )
[bacc36c]158
159# call make
[d65f92c]160def make(target, *, flags = '', output_file = None, error = None, error_file = None, silent = False):
[a45fc7b]161 test_param = """test="%s" """ % (error_file) if error_file else None
162 cmd = [
163 *settings.make,
164 '-s' if silent else None,
[bacc36c]165 test_param,
[575a6e5]166 settings.arch.flags,
[f3b9efc]167 settings.debug.flags,
[a5121bf]168 settings.install.flags,
[d65f92c]169 settings.distcc if settings.distribute else None,
[bacc36c]170 flags,
[a45fc7b]171 target
172 ]
173 cmd = [s for s in cmd if s]
[d65f92c]174 return sh(*cmd, output_file=output_file, error=error)
[bacc36c]175
[ed45af6]176def which(program):
[0f5da65]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
[0c13238]187
[f806b61]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
[35a408b7]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
[bacc36c]209################################################################################
210# file handling
211################################################################################
[0c13238]212# move a file
213def mv(source, dest):
[a45fc7b]214 ret, _ = sh("mv", source, dest)
[0c13238]215 return ret
216
217# cat one file into the other
218def cat(source, dest):
[d65f92c]219 ret, _ = sh("cat", source, output_file=dest)
[0c13238]220 return ret
[bacc36c]221
[c07d724]222# helper function to replace patterns in a file
223def file_replace(fname, pat, s_after):
[202ad72]224 if settings.dry_run:
225 print("replacing '%s' with '%s' in %s" % (pat, s_after, fname))
226 return
227
[f85bc15]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()
[c07d724]232
[0ad0c55]233# helper function to check if a files contains only a specific string
[5bf1f3e]234def file_contains_only(file, text) :
[c07d724]235 with open(file) as f:
236 ff = f.read().strip()
237 result = ff == text.strip()
238
[5bf1f3e]239 return result
[c07d724]240
[bacc36c]241# transform path to canonical form
[5bf1f3e]242def canonical_path(path):
[f85bc15]243 abspath = os.path.abspath(__main__.__file__)
244 dname = os.path.dirname(abspath)
245 return os.path.join(dname, os.path.normpath(path) )
[c07d724]246
[bacc36c]247# compare path even if form is different
[5bf1f3e]248def path_cmp(lhs, rhs):
249 return canonical_path( lhs ) == canonical_path( rhs )
[c07d724]250
[bacc36c]251# walk all files in a path
[5bf1f3e]252def path_walk( op ):
[56de5932]253 dname = settings.SRCDIR
[5b993e0]254 for dirname, _, names in os.walk(dname):
255 for name in names:
256 path = os.path.join(dirname, name)
257 op( path )
[bacc36c]258
259################################################################################
260# system
261################################################################################
262# count number of jobs to create
[5bf1f3e]263def job_count( options, tests ):
[bacc36c]264 # check if the user already passed in a number of jobs for multi-threading
[d142ec5]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 :
[34e1494]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()
[bacc36c]282 else :
[d142ec5]283 force = True
[bacc36c]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
[d142ec5]290 return min( options.jobs, len(tests) ), force
[bacc36c]291
[0c13238]292# enable core dumps for all the test children
293resource.setrlimit(resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY))
294
[bacc36c]295################################################################################
296# misc
297################################################################################
298
[d65f92c]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)
[eea20cd]307 _, out = sh(distcc_hash, config, output_file=subprocess.PIPE, ignore_dry_run=True)
[d65f92c]308 return out.strip()
309
[5c4a473]310# get pretty string for time of day
[dcfedca]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
[bacc36c]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)
[f3b9efc]323
[ed45af6]324def fancy_print(text):
325 column = which('column')
326 if column:
[1bb2488]327 subprocess.run(column, input=bytes(text + "\n", "UTF-8"))
[ed45af6]328 else:
329 print(text)
[0c13238]330
331
[5bf1f3e]332def core_info(path):
[f806b61]333 if not os.path.isfile(path):
334 return 1, "ERR Executable path is wrong"
335
[0c13238]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
[f806b61]340 core = os.path.join(os.getcwd(), "core" )
[0c13238]341
342 if not os.path.isfile(core):
343 return 1, "ERR No core dump"
344
[d65f92c]345 return sh('gdb', '-n', path, core, '-batch', '-x', cmd, output_file=subprocess.PIPE)
[0c13238]346
[dcfedca]347def core_archive(dst, name, exe):
[143e6f3]348 # Get the core dump
[dcfedca]349 core = os.path.join(os.getcwd(), "core" )
350
[143e6f3]351 # update the path for this test
352 dst = os.path.join(dst, name)
[dcfedca]353
354 # make a directory for this test
[143e6f3]355 # mkdir makes the parent directory only so add a dummy
[ea2074e]356 mkdir(os.path.join(dst, name ))
[dcfedca]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
[0c13238]365class Timed:
[0f5da65]366 def __enter__(self):
367 self.start = time.time()
368 return self
[0c13238]369
[0f5da65]370 def __exit__(self, *args):
371 self.end = time.time()
372 self.duration = self.end - self.start
[35a408b7]373
374def timed(src, timeout):
375 expire = time.time() + timeout
376 i = iter(src)
[0f5da65]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.