source: tests/pybin/tools.py @ 7726839

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

No longer need to use popen and signal handling in test.py

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