source: tests/pybin/tools.py@ fd8f88f

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 fd8f88f was 143e6f3, checked in by Thierry Delisle <tdelisle@…>, 6 years ago

Trying out archive build failures

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