source: tests/pybin/tools.py @ 8209e70

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

Merge branch 'master' into distcc

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