xref: /openbsd/gnu/llvm/llvm/utils/lit/lit/LitConfig.py (revision d415bd75)
109467b48Spatrickfrom __future__ import absolute_import
209467b48Spatrickimport inspect
309467b48Spatrickimport os
409467b48Spatrickimport platform
509467b48Spatrickimport sys
609467b48Spatrick
709467b48Spatrickimport lit.Test
809467b48Spatrickimport lit.formats
909467b48Spatrickimport lit.TestingConfig
1009467b48Spatrickimport lit.util
1109467b48Spatrick
1209467b48Spatrick# LitConfig must be a new style class for properties to work
1309467b48Spatrickclass LitConfig(object):
1409467b48Spatrick    """LitConfig - Configuration data for a 'lit' test runner instance, shared
1509467b48Spatrick    across all tests.
1609467b48Spatrick
1709467b48Spatrick    The LitConfig object is also used to communicate with client configuration
1809467b48Spatrick    files, it is always passed in as the global variable 'lit' so that
1909467b48Spatrick    configuration files can access common functionality and internal components
2009467b48Spatrick    easily.
2109467b48Spatrick    """
2209467b48Spatrick
2309467b48Spatrick    def __init__(self, progname, path, quiet,
2409467b48Spatrick                 useValgrind, valgrindLeakCheck, valgrindArgs,
25*d415bd75Srobert                 noExecute, debug, isWindows, order,
2609467b48Spatrick                 params, config_prefix = None,
2709467b48Spatrick                 maxIndividualTestTime = 0,
2809467b48Spatrick                 parallelism_groups = {},
2909467b48Spatrick                 echo_all_commands = False):
3009467b48Spatrick        # The name of the test runner.
3109467b48Spatrick        self.progname = progname
3209467b48Spatrick        # The items to add to the PATH environment variable.
3309467b48Spatrick        self.path = [str(p) for p in path]
3409467b48Spatrick        self.quiet = bool(quiet)
3509467b48Spatrick        self.useValgrind = bool(useValgrind)
3609467b48Spatrick        self.valgrindLeakCheck = bool(valgrindLeakCheck)
3709467b48Spatrick        self.valgrindUserArgs = list(valgrindArgs)
3809467b48Spatrick        self.noExecute = noExecute
3909467b48Spatrick        self.debug = debug
4009467b48Spatrick        self.isWindows = bool(isWindows)
41*d415bd75Srobert        self.order = order
4209467b48Spatrick        self.params = dict(params)
4309467b48Spatrick        self.bashPath = None
4409467b48Spatrick
4509467b48Spatrick        # Configuration files to look for when discovering test suites.
4609467b48Spatrick        self.config_prefix = config_prefix or 'lit'
4709467b48Spatrick        self.suffixes = ['cfg.py', 'cfg']
4809467b48Spatrick        self.config_names = ['%s.%s' % (self.config_prefix,x) for x in self.suffixes]
4909467b48Spatrick        self.site_config_names = ['%s.site.%s' % (self.config_prefix,x) for x in self.suffixes]
5009467b48Spatrick        self.local_config_names = ['%s.local.%s' % (self.config_prefix,x) for x in self.suffixes]
5109467b48Spatrick
5209467b48Spatrick        self.numErrors = 0
5309467b48Spatrick        self.numWarnings = 0
5409467b48Spatrick
5509467b48Spatrick        self.valgrindArgs = []
5609467b48Spatrick        if self.useValgrind:
5709467b48Spatrick            self.valgrindArgs = ['valgrind', '-q', '--run-libc-freeres=no',
5809467b48Spatrick                                 '--tool=memcheck', '--trace-children=yes',
5909467b48Spatrick                                 '--error-exitcode=123']
6009467b48Spatrick            if self.valgrindLeakCheck:
6109467b48Spatrick                self.valgrindArgs.append('--leak-check=full')
6209467b48Spatrick            else:
6309467b48Spatrick                # The default is 'summary'.
6409467b48Spatrick                self.valgrindArgs.append('--leak-check=no')
6509467b48Spatrick            self.valgrindArgs.extend(self.valgrindUserArgs)
6609467b48Spatrick
6709467b48Spatrick        self.maxIndividualTestTime = maxIndividualTestTime
6809467b48Spatrick        self.parallelism_groups = parallelism_groups
6909467b48Spatrick        self.echo_all_commands = echo_all_commands
7009467b48Spatrick
7109467b48Spatrick    @property
7209467b48Spatrick    def maxIndividualTestTime(self):
7309467b48Spatrick        """
7409467b48Spatrick            Interface for getting maximum time to spend executing
7509467b48Spatrick            a single test
7609467b48Spatrick        """
7709467b48Spatrick        return self._maxIndividualTestTime
7809467b48Spatrick
7909467b48Spatrick    @property
8009467b48Spatrick    def maxIndividualTestTimeIsSupported(self):
8109467b48Spatrick        """
8209467b48Spatrick            Returns a tuple (<supported> , <error message>)
8309467b48Spatrick            where
8409467b48Spatrick            `<supported>` is True if setting maxIndividualTestTime is supported
8509467b48Spatrick                on the current host, returns False otherwise.
8609467b48Spatrick            `<error message>` is an empty string if `<supported>` is True,
8709467b48Spatrick                otherwise is contains a string describing why setting
8809467b48Spatrick                maxIndividualTestTime is not supported.
8909467b48Spatrick        """
9009467b48Spatrick        return lit.util.killProcessAndChildrenIsSupported()
9109467b48Spatrick
9209467b48Spatrick    @maxIndividualTestTime.setter
9309467b48Spatrick    def maxIndividualTestTime(self, value):
9409467b48Spatrick        """
9509467b48Spatrick            Interface for setting maximum time to spend executing
9609467b48Spatrick            a single test
9709467b48Spatrick        """
9809467b48Spatrick        if not isinstance(value, int):
9909467b48Spatrick            self.fatal('maxIndividualTestTime must set to a value of type int.')
10009467b48Spatrick        self._maxIndividualTestTime = value
10109467b48Spatrick        if self.maxIndividualTestTime > 0:
10209467b48Spatrick            # The current implementation needs psutil on some platforms to set
10309467b48Spatrick            # a timeout per test. Check it's available.
10409467b48Spatrick            # See lit.util.killProcessAndChildren()
10509467b48Spatrick            supported, errormsg = self.maxIndividualTestTimeIsSupported
10609467b48Spatrick            if not supported:
10709467b48Spatrick                self.fatal('Setting a timeout per test not supported. ' +
10809467b48Spatrick                           errormsg)
10909467b48Spatrick        elif self.maxIndividualTestTime < 0:
11009467b48Spatrick            self.fatal('The timeout per test must be >= 0 seconds')
11109467b48Spatrick
11209467b48Spatrick    def load_config(self, config, path):
11309467b48Spatrick        """load_config(config, path) - Load a config object from an alternate
11409467b48Spatrick        path."""
11509467b48Spatrick        if self.debug:
11609467b48Spatrick            self.note('load_config from %r' % path)
11709467b48Spatrick        config.load_from_path(path, self)
11809467b48Spatrick        return config
11909467b48Spatrick
12009467b48Spatrick    def getBashPath(self):
12109467b48Spatrick        """getBashPath - Get the path to 'bash'"""
12209467b48Spatrick        if self.bashPath is not None:
12309467b48Spatrick            return self.bashPath
12409467b48Spatrick
12509467b48Spatrick        self.bashPath = lit.util.which('bash', os.pathsep.join(self.path))
12609467b48Spatrick        if self.bashPath is None:
12709467b48Spatrick            self.bashPath = lit.util.which('bash')
12809467b48Spatrick
12909467b48Spatrick        if self.bashPath is None:
13009467b48Spatrick            self.bashPath = ''
13109467b48Spatrick
13209467b48Spatrick        # Check whether the found version of bash is able to cope with paths in
13309467b48Spatrick        # the host path format. If not, don't return it as it can't be used to
13409467b48Spatrick        # run scripts. For example, WSL's bash.exe requires '/mnt/c/foo' rather
13509467b48Spatrick        # than 'C:\\foo' or 'C:/foo'.
13609467b48Spatrick        if self.isWindows and self.bashPath:
13709467b48Spatrick            command = [self.bashPath, '-c',
13809467b48Spatrick                       '[[ -f "%s" ]]' % self.bashPath.replace('\\', '\\\\')]
13909467b48Spatrick            _, _, exitCode = lit.util.executeCommand(command)
14009467b48Spatrick            if exitCode:
14109467b48Spatrick                self.note('bash command failed: %s' % (
14209467b48Spatrick                    ' '.join('"%s"' % c for c in command)))
14309467b48Spatrick                self.bashPath = ''
14409467b48Spatrick
14509467b48Spatrick        if not self.bashPath:
14609467b48Spatrick            self.warning('Unable to find a usable version of bash.')
14709467b48Spatrick
14809467b48Spatrick        return self.bashPath
14909467b48Spatrick
15009467b48Spatrick    def getToolsPath(self, dir, paths, tools):
15109467b48Spatrick        if dir is not None and os.path.isabs(dir) and os.path.isdir(dir):
15209467b48Spatrick            if not lit.util.checkToolsPath(dir, tools):
15309467b48Spatrick                return None
15409467b48Spatrick        else:
15509467b48Spatrick            dir = lit.util.whichTools(tools, paths)
15609467b48Spatrick
15709467b48Spatrick        # bash
15809467b48Spatrick        self.bashPath = lit.util.which('bash', dir)
15909467b48Spatrick        if self.bashPath is None:
16009467b48Spatrick            self.bashPath = ''
16109467b48Spatrick
16209467b48Spatrick        return dir
16309467b48Spatrick
16409467b48Spatrick    def _write_message(self, kind, message):
16509467b48Spatrick        # Get the file/line where this message was generated.
16609467b48Spatrick        f = inspect.currentframe()
16709467b48Spatrick        # Step out of _write_message, and then out of wrapper.
16809467b48Spatrick        f = f.f_back.f_back
16973471bf0Spatrick        file = os.path.abspath(inspect.getsourcefile(f))
17073471bf0Spatrick        line = inspect.getlineno(f)
17173471bf0Spatrick        sys.stderr.write('%s: %s:%d: %s: %s\n' % (self.progname, file, line,
17209467b48Spatrick                                                  kind, message))
173*d415bd75Srobert        if self.isWindows:
174*d415bd75Srobert            # In a git bash terminal, the writes to sys.stderr aren't visible
175*d415bd75Srobert            # on screen immediately. Flush them here to avoid broken/misoredered
176*d415bd75Srobert            # output.
177*d415bd75Srobert            sys.stderr.flush()
178*d415bd75Srobert
179*d415bd75Srobert    def substitute(self, string):
180*d415bd75Srobert        """substitute - Interpolate params into a string"""
181*d415bd75Srobert        try:
182*d415bd75Srobert          return string % self.params
183*d415bd75Srobert        except KeyError as e:
184*d415bd75Srobert          key, = e.args
185*d415bd75Srobert          self.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (
186*d415bd75Srobert              key,key))
18709467b48Spatrick
18809467b48Spatrick    def note(self, message):
18909467b48Spatrick        if not self.quiet:
19009467b48Spatrick            self._write_message('note', message)
19109467b48Spatrick
19209467b48Spatrick    def warning(self, message):
19309467b48Spatrick        if not self.quiet:
19409467b48Spatrick            self._write_message('warning', message)
19509467b48Spatrick        self.numWarnings += 1
19609467b48Spatrick
19709467b48Spatrick    def error(self, message):
19809467b48Spatrick        self._write_message('error', message)
19909467b48Spatrick        self.numErrors += 1
20009467b48Spatrick
20109467b48Spatrick    def fatal(self, message):
20209467b48Spatrick        self._write_message('fatal', message)
20309467b48Spatrick        sys.exit(2)
204