source: tests/pybin/tools.py@ a539fc3

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

Merge branch 'master' into distcc

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