1from __future__ import absolute_import
2import os
3
4import lit.Test
5import lit.TestFormats
6import lit.TestingConfig
7import lit.Util
8
9class LitConfig:
10    """LitConfig - Configuration data for a 'lit' test runner instance, shared
11    across all tests.
12
13    The LitConfig object is also used to communicate with client configuration
14    files, it is always passed in as the global variable 'lit' so that
15    configuration files can access common functionality and internal components
16    easily.
17    """
18
19    # Provide access to Test module.
20    Test = lit.Test
21
22    # Provide access to built-in formats.
23    formats = lit.TestFormats
24
25    # Provide access to built-in utility functions.
26    util = lit.Util
27
28    def __init__(self, progname, path, quiet,
29                 useValgrind, valgrindLeakCheck, valgrindArgs,
30                 noExecute, debug, isWindows,
31                 params, config_prefix = None):
32        # The name of the test runner.
33        self.progname = progname
34        # The items to add to the PATH environment variable.
35        self.path = list(map(str, path))
36        self.quiet = bool(quiet)
37        self.useValgrind = bool(useValgrind)
38        self.valgrindLeakCheck = bool(valgrindLeakCheck)
39        self.valgrindUserArgs = list(valgrindArgs)
40        self.noExecute = noExecute
41        self.debug = debug
42        self.isWindows = bool(isWindows)
43        self.params = dict(params)
44        self.bashPath = None
45
46        # Configuration files to look for when discovering test suites.
47        self.config_prefix = config_prefix or 'lit'
48        self.config_name = '%s.cfg' % (self.config_prefix,)
49        self.site_config_name = '%s.site.cfg' % (self.config_prefix,)
50        self.local_config_name = '%s.local.cfg' % (self.config_prefix,)
51
52        self.numErrors = 0
53        self.numWarnings = 0
54
55        self.valgrindArgs = []
56        if self.useValgrind:
57            self.valgrindArgs = ['valgrind', '-q', '--run-libc-freeres=no',
58                                 '--tool=memcheck', '--trace-children=yes',
59                                 '--error-exitcode=123']
60            if self.valgrindLeakCheck:
61                self.valgrindArgs.append('--leak-check=full')
62            else:
63                # The default is 'summary'.
64                self.valgrindArgs.append('--leak-check=no')
65            self.valgrindArgs.extend(self.valgrindUserArgs)
66
67
68    def load_config(self, config, path):
69        """load_config(config, path) - Load a config object from an alternate
70        path."""
71        if self.debug:
72            self.note('load_config from %r' % path)
73        return lit.TestingConfig.TestingConfig.frompath(
74            path, config.parent, self, mustExist = True, config = config)
75
76    def getBashPath(self):
77        """getBashPath - Get the path to 'bash'"""
78        import os
79
80        if self.bashPath is not None:
81            return self.bashPath
82
83        self.bashPath = lit.Util.which('bash', os.pathsep.join(self.path))
84        if self.bashPath is None:
85            # Check some known paths.
86            for path in ('/bin/bash', '/usr/bin/bash', '/usr/local/bin/bash'):
87                if os.path.exists(path):
88                    self.bashPath = path
89                    break
90
91        if self.bashPath is None:
92            self.warning("Unable to find 'bash'.")
93            self.bashPath = ''
94
95        return self.bashPath
96
97    def getToolsPath(self, dir, paths, tools):
98        if dir is not None and os.path.isabs(dir) and os.path.isdir(dir):
99            if not lit.Util.checkToolsPath(dir, tools):
100                return None
101        else:
102            dir = lit.Util.whichTools(tools, paths)
103
104        # bash
105        self.bashPath = lit.Util.which('bash', dir)
106        if self.bashPath is None:
107            self.note("Unable to find 'bash.exe'.")
108            self.bashPath = ''
109
110        return dir
111
112    def _write_message(self, kind, message):
113        import inspect, os, sys
114
115        # Get the file/line where this message was generated.
116        f = inspect.currentframe()
117        # Step out of _write_message, and then out of wrapper.
118        f = f.f_back.f_back
119        file,line,_,_,_ = inspect.getframeinfo(f)
120        location = '%s:%d' % (os.path.basename(file), line)
121
122        sys.stderr.write('%s: %s: %s: %s\n' % (self.progname, location,
123                                               kind, message))
124
125    def note(self, message):
126        self._write_message('note', message)
127
128    def warning(self, message):
129        self._write_message('warning', message)
130        self.numWarnings += 1
131
132    def error(self, message):
133        self._write_message('error', message)
134        self.numErrors += 1
135
136    def fatal(self, message):
137        import sys
138        self._write_message('fatal', message)
139        sys.exit(2)
140