source: tests/pybin/tools.py @ e8fe5e0

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

Tests no longer use subprocess.run(shell=True), which solves leak process problems

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