source: tests/pybin/tools.py @ d65f92c

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

Tests almost work, the only issue left is using -E and -CFA together

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