source: tests/pybin/tools.py @ d82892a

ADTarm-ehast-experimentalenumforall-pointer-decayjacob/cs343-translationjenkins-sandboxnew-astnew-ast-unique-exprpthread-emulationqualifiedEnum
Last change on this file since d82892a was 35a408b7, checked in by Thierry Delisle <tdelisle@…>, 5 years ago

Fixed global timeout

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