source: src/tests/pybin/tools.py@ 5c14030

new-env
Last change on this file since 5c14030 was ed45af6, checked in by Thierry Delisle <tdelisle@…>, 7 years ago

Improve test list printing

  • Property mode set to 100644
File size: 6.8 KB
Line 
1from __future__ import print_function
2
3import __main__
4import argparse
5import multiprocessing
6import os
7import re
8import signal
9import stat
10import sys
11
12from pybin import settings
13from subprocess import Popen, PIPE, STDOUT
14
15################################################################################
16# shell helpers
17################################################################################
18
19# helper functions to run terminal commands
20def sh(cmd, print2stdout = True, input = None):
21 # add input redirection if needed
22 if input and os.path.isfile(input):
23 cmd += " < %s" % input
24
25 # if this is a dry_run, only print the commands that would be ran
26 if settings.dry_run :
27 print("cmd: %s" % cmd)
28 return 0, None
29
30 # otherwise create a pipe and run the desired command
31 else :
32 proc = Popen(cmd, stdout=None if print2stdout else PIPE, stderr=STDOUT, shell=True)
33 out, err = proc.communicate()
34 return proc.returncode, out
35
36# Remove 1 or more files silently
37def rm( files ):
38 try:
39 for file in files:
40 sh("rm -f %s > /dev/null 2>&1" % file )
41 except TypeError:
42 sh("rm -f %s > /dev/null 2>&1" % files )
43
44def chdir( dest = __main__.__file__ ):
45 abspath = os.path.abspath(dest)
46 dname = os.path.dirname(abspath)
47 os.chdir(dname)
48
49# diff two files
50def diff( lhs, rhs ):
51 # diff the output of the files
52 diff_cmd = ("diff --ignore-all-space "
53 "--ignore-blank-lines "
54 "--old-group-format='\t\tmissing lines :\n"
55 "%%<' \\\n"
56 "--new-group-format='\t\tnew lines :\n"
57 "%%>' \\\n"
58 "--unchanged-group-format='%%=' \\"
59 "--changed-group-format='\t\texpected :\n"
60 "%%<"
61 "\t\tgot :\n"
62 "%%>\n' \\\n"
63 "--new-line-format='\t\t%%dn\t%%L' \\\n"
64 "--old-line-format='\t\t%%dn\t%%L' \\\n"
65 "--unchanged-line-format='' \\\n"
66 "%s %s")
67
68 # fetch return code and error from the diff command
69 return sh(diff_cmd % (lhs, rhs), False)
70
71# call make
72def make(target, flags = '', redirects = '', error_file = None, silent = False):
73 test_param = """test="%s" """ % (error_file) if error_file else ''
74 cmd = ' '.join([
75 settings.make,
76 '-s' if silent else '',
77 test_param,
78 settings.debug.flags,
79 flags,
80 target,
81 redirects
82 ])
83 return sh(cmd)
84
85def which(program):
86 import os
87 def is_exe(fpath):
88 return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
89
90 fpath, fname = os.path.split(program)
91 if fpath:
92 if is_exe(program):
93 return program
94 else:
95 for path in os.environ["PATH"].split(os.pathsep):
96 exe_file = os.path.join(path, program)
97 if is_exe(exe_file):
98 return exe_file
99
100 return None
101################################################################################
102# file handling
103################################################################################
104
105# helper function to replace patterns in a file
106def file_replace(fname, pat, s_after):
107 # first, see if the pattern is even in the file.
108 with open(fname) as f:
109 if not any(re.search(pat, line) for line in f):
110 return # pattern does not occur in file so we are done.
111
112 # pattern is in the file, so perform replace operation.
113 with open(fname) as f:
114 out_fname = fname + ".tmp"
115 out = open(out_fname, "w")
116 for line in f:
117 out.write(re.sub(pat, s_after, line))
118 out.close()
119 os.rename(out_fname, fname)
120
121# helper function to check if a files contains only a specific string
122def fileContainsOnly(file, text) :
123 with open(file) as f:
124 ff = f.read().strip()
125 result = ff == text.strip()
126
127 return result;
128
129# check whether or not a file is executable
130def fileIsExecutable(file) :
131 try :
132 fileinfo = os.stat(file)
133 return bool(fileinfo.st_mode & stat.S_IXUSR)
134 except Exception as inst:
135 print(type(inst)) # the exception instance
136 print(inst.args) # arguments stored in .args
137 print(inst)
138 return False
139
140# transform path to canonical form
141def canonicalPath(path):
142 return os.path.join('.', os.path.normpath(path) )
143
144# compare path even if form is different
145def pathCmp(lhs, rhs):
146 return canonicalPath( lhs ) == canonicalPath( rhs )
147
148# walk all files in a path
149def pathWalk( op ):
150 def step(_, dirname, names):
151 for name in names:
152 path = os.path.join(dirname, name)
153
154 op( path )
155
156 # Start the walk
157 os.path.walk('.', step, '')
158
159################################################################################
160# system
161################################################################################
162
163# parses the Makefile to find the machine type (32-bit / 64-bit)
164def getMachineType():
165 sh('echo "void ?{}(int&a,int b){}int main(){return 0;}" > .dummy.c')
166 ret, out = make('.dummy', silent = True)
167
168 if ret != 0:
169 print("Failed to identify architecture:")
170 print(out)
171 print("Stopping")
172 rm( (".dummy.c",".dummy") )
173 sys.exit(1)
174
175 _, out = sh("file .dummy", print2stdout=False)
176 rm( (".dummy.c",".dummy") )
177
178 if settings.dry_run :
179 return 'x64'
180
181 return re.search(r"[^,]+,([^,]+),", out).group(1).strip()
182
183# count number of jobs to create
184def jobCount( options, tests ):
185 # check if the user already passed in a number of jobs for multi-threading
186 if not options.jobs:
187 make_flags = os.environ.get('MAKEFLAGS')
188 force = bool(make_flags)
189 make_jobs_fds = re.search("--jobserver-(auth|fds)=\s*([0-9]+),([0-9]+)", make_flags) if make_flags else None
190 if make_jobs_fds :
191 tokens = os.read(int(make_jobs_fds.group(2)), 1024)
192 options.jobs = len(tokens)
193 os.write(int(make_jobs_fds.group(3)), tokens)
194 else :
195 options.jobs = multiprocessing.cpu_count()
196 else :
197 force = True
198
199 # make sure we have a valid number of jobs that corresponds to user input
200 if options.jobs <= 0 :
201 print('ERROR: Invalid number of jobs', file=sys.stderr)
202 sys.exit(1)
203
204 return min( options.jobs, len(tests) ), force
205
206# setup a proper processor pool with correct signal handling
207def setupPool(jobs):
208 original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
209 pool = multiprocessing.Pool(jobs)
210 signal.signal(signal.SIGINT, original_sigint_handler)
211
212 return pool
213
214# handle signals in scope
215class SignalHandling():
216 def __enter__(self):
217 # enable signal handling
218 signal.signal(signal.SIGINT, signal.SIG_DFL)
219
220 def __exit__(self, type, value, traceback):
221 # disable signal handling
222 signal.signal(signal.SIGINT, signal.SIG_IGN)
223
224################################################################################
225# misc
226################################################################################
227
228# check if arguments is yes or no
229def yes_no(string):
230 if string == "yes" :
231 return True
232 if string == "no" :
233 return False
234 raise argparse.ArgumentTypeError(msg)
235 return False
236
237def fancy_print(text):
238 column = which('column')
239 if column:
240 cmd = "%s 2> /dev/null" % column
241 print(cmd)
242 proc = Popen(cmd, stdin=PIPE, stderr=None, shell=True)
243 proc.communicate(input=text)
244 else:
245 print(text)
246
247settings.set_machine_default( getMachineType )
Note: See TracBrowser for help on using the repository browser.