source: tests/pybin/tools.py @ 541d8219

ADTarm-ehast-experimentalenumforall-pointer-decayjacob/cs343-translationjenkins-sandboxnew-astnew-ast-unique-exprpthread-emulationqualifiedEnum
Last change on this file since 541d8219 was 0f5da65, checked in by Peter A. Buhr <pabuhr@…>, 4 years ago

formatting, fix python 3.7 StopIteration? problem

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