source: tests/pybin/tools.py@ a45fc7b

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 a45fc7b was a45fc7b, checked in by tdelisle <tdelisle@…>, 7 years ago

Tests no longer use subprocess.run(shell=True), which solves leak process problems

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