source: tests/pybin/tools.py @ 2b10f95

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

Improved printing, added support for cpp tests and fix byte string concatenation error

  • Property mode set to 100644
File size: 7.0 KB
Line 
1import __main__
2import argparse
3import fileinput
4import multiprocessing
5import os
6import re
7import resource
8import signal
9import stat
10import subprocess
11import sys
12import time
13import types
14
15from pybin import settings
16from subprocess import Popen, PIPE, STDOUT
17
18################################################################################
19#               shell helpers
20################################################################################
21
22# helper functions to run terminal commands
23def sh(cmd, print2stdout = True, timeout = False, output = None, input = None):
24        # add input redirection if needed
25        if input and os.path.isfile(input):
26                cmd += " < %s" % input
27
28        # add output redirection if needed
29        if output:
30                cmd += " > %s" % output
31
32        # if this is a dry_run, only print the commands that would be ran
33        if settings.dry_run :
34                print("cmd: %s" % cmd)
35                return 0, None
36
37        # otherwise create a pipe and run the desired command
38        else :
39                try:
40                        proc = subprocess.run(
41                                cmd,
42                                stdout=None if print2stdout else PIPE,
43                                stderr=STDOUT,
44                                shell=True,
45                                timeout=settings.timeout.single if timeout else None
46                        )
47                        return proc.returncode, proc.stdout.decode("utf-8") if proc.stdout else None
48                except subprocess.TimeoutExpired:
49                        return 124, str(None)
50
51def is_ascii(fname):
52        if settings.dry_run:
53                print("is_ascii: %s" % fname)
54                return True
55
56        if not os.path.isfile(fname):
57                return False
58
59        code, out = sh("file %s" % fname, print2stdout = False)
60        if code != 0:
61                return False
62
63        match = re.search(".*: (.*)", out)
64
65        if not match:
66                return False
67
68        return match.group(1).startswith("ASCII text")
69
70def is_exe(fname):
71        return os.path.isfile(fname) and os.access(fname, os.X_OK)
72
73# Remove 1 or more files silently
74def rm( files ):
75        if isinstance(files, str ): files = [ files ]
76        for file in files:
77                sh("rm -f %s > /dev/null 2>&1" % file )
78
79# Create 1 or more directory
80def mkdir( files ):
81        if isinstance(files, str ): files = [ files ]
82        for file in files:
83                p = os.path.normpath( file )
84                d = os.path.dirname ( p )
85                sh( "mkdir -p {}".format(d) )
86
87
88def chdir( dest = __main__.__file__ ):
89        abspath = os.path.abspath(dest)
90        dname = os.path.dirname(abspath)
91        os.chdir(dname)
92
93# diff two files
94def diff( lhs, rhs ):
95        # diff the output of the files
96        diff_cmd = ("diff --text "
97                        "--old-group-format='\t\tmissing lines :\n"
98                        "%%<' \\\n"
99                        "--new-group-format='\t\tnew lines :\n"
100                        "%%>' \\\n"
101                        "--unchanged-group-format='%%=' \\"
102                        "--changed-group-format='\t\texpected :\n"
103                        "%%<"
104                        "\t\tgot :\n"
105                        "%%>\n' \\\n"
106                        "--new-line-format='\t\t%%dn\t%%L' \\\n"
107                        "--old-line-format='\t\t%%dn\t%%L' \\\n"
108                        "--unchanged-line-format='' \\\n"
109                        "%s %s")
110
111        # fetch return code and error from the diff command
112        return sh(diff_cmd % (lhs, rhs), False)
113
114# call make
115def make(target, flags = '', redirects = '', error_file = None, silent = False):
116        test_param = """test="%s" """ % (error_file) if error_file else ''
117        cmd = ' '.join([
118                settings.make,
119                '-s' if silent else '',
120                test_param,
121                settings.arch.flags,
122                settings.debug.flags,
123                settings.install.flags,
124                flags,
125                target,
126                redirects
127        ])
128        return sh(cmd)
129
130def which(program):
131    fpath, fname = os.path.split(program)
132    if fpath:
133        if is_exe(program):
134            return program
135    else:
136        for path in os.environ["PATH"].split(os.pathsep):
137            exe_file = os.path.join(path, program)
138            if is_exe(exe_file):
139                return exe_file
140
141    return None
142
143################################################################################
144#               file handling
145################################################################################
146# move a file
147def mv(source, dest):
148        ret, _ = sh("mv %s %s" % (source, dest))
149        return ret
150
151# cat one file into the other
152def cat(source, dest):
153        ret, _ = sh("cat %s > %s" % (source, dest))
154        return ret
155
156# helper function to replace patterns in a file
157def file_replace(fname, pat, s_after):
158        if settings.dry_run:
159                print("replacing '%s' with '%s' in %s" % (pat, s_after, fname))
160                return
161
162        file = fileinput.FileInput(fname, inplace=True, backup='.bak')
163        for line in file:
164                print(line.replace(pat, s_after), end='')
165        file.close()
166
167# helper function to check if a files contains only a specific string
168def file_contains_only(file, text) :
169        with open(file) as f:
170                ff = f.read().strip()
171                result = ff == text.strip()
172
173                return result
174
175# transform path to canonical form
176def canonical_path(path):
177        abspath = os.path.abspath(__main__.__file__)
178        dname = os.path.dirname(abspath)
179        return os.path.join(dname, os.path.normpath(path) )
180
181# compare path even if form is different
182def path_cmp(lhs, rhs):
183        return canonical_path( lhs ) == canonical_path( rhs )
184
185# walk all files in a path
186def path_walk( op ):
187        dname = settings.SRCDIR
188        for dirname, _, names in os.walk(dname):
189                for name in names:
190                        path = os.path.join(dirname, name)
191                        op( path )
192
193################################################################################
194#               system
195################################################################################
196# count number of jobs to create
197def job_count( options, tests ):
198        # check if the user already passed in a number of jobs for multi-threading
199        if not options.jobs:
200                make_flags = os.environ.get('MAKEFLAGS')
201                force = bool(make_flags)
202                make_jobs_fds = re.search("--jobserver-(auth|fds)=\s*([0-9]+),([0-9]+)", make_flags) if make_flags else None
203                if make_jobs_fds :
204                        tokens = os.read(int(make_jobs_fds.group(2)), 1024)
205                        options.jobs = len(tokens)
206                        os.write(int(make_jobs_fds.group(3)), tokens)
207                else :
208                        options.jobs = multiprocessing.cpu_count()
209        else :
210                force = True
211
212        # make sure we have a valid number of jobs that corresponds to user input
213        if options.jobs <= 0 :
214                print('ERROR: Invalid number of jobs', file=sys.stderr)
215                sys.exit(1)
216
217        return min( options.jobs, len(tests) ), force
218
219# enable core dumps for all the test children
220resource.setrlimit(resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY))
221
222################################################################################
223#               misc
224################################################################################
225
226# check if arguments is yes or no
227def yes_no(string):
228        if string == "yes" :
229                return True
230        if string == "no" :
231                return False
232        raise argparse.ArgumentTypeError(msg)
233
234def fancy_print(text):
235        column = which('column')
236        if column:
237                subprocess.run(column, input=bytes(text + "\n", "UTF-8"))
238        else:
239                print(text)
240
241
242def core_info(path):
243        cmd   = os.path.join(settings.SRCDIR, "pybin/print-core.gdb")
244        if not os.path.isfile(cmd):
245                return 1, "ERR Printing format for core dumps not found"
246
247        dname = os.path.dirname(path)
248        core  = os.path.join(dname, "core" )
249        if not os.path.isfile(path):
250                return 1, "ERR Executable path is wrong"
251
252        if not os.path.isfile(core):
253                return 1, "ERR No core dump"
254
255        return sh("gdb -n %s %s -batch -x %s" % (path, core, cmd), print2stdout=False)
256
257class Timed:
258    def __enter__(self):
259        self.start = time.time()
260        return self
261
262    def __exit__(self, *args):
263        self.end = time.time()
264        self.duration = self.end - self.start
Note: See TracBrowser for help on using the repository browser.