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

ADT arm-eh ast-experimental cleanup-dtors enum forall-pointer-decay jacob/cs343-translation jenkins-sandbox new-ast new-ast-unique-expr pthread-emulation qualifiedEnum
Last change on this file since 8dbfb7e was 1bb2488, checked in by tdelisle <tdelisle@…>, 7 years ago

No longer need to use popen and signal handling in test.py

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