1import os
2import sys
3
4
5class TestingConfig(object):
6    """"
7    TestingConfig - Information on the tests inside a suite.
8    """
9
10    @staticmethod
11    def fromdefaults(litConfig):
12        """
13        fromdefaults(litConfig) -> TestingConfig
14
15        Create a TestingConfig object with default values.
16        """
17        # Set the environment based on the command line arguments.
18        environment = {
19            'PATH' : os.pathsep.join(litConfig.path +
20                                     [os.environ.get('PATH','')]),
21            'LLVM_DISABLE_CRASH_REPORT' : '1',
22            }
23
24        pass_vars = [
25            'LIBRARY_PATH',
26            'LD_LIBRARY_PATH',
27            'SYSTEMROOT',
28            'TERM',
29            'CLANG',
30            'LLDB',
31            'LD_PRELOAD',
32            'LLVM_SYMBOLIZER_PATH',
33            'ASAN_SYMBOLIZER_PATH',
34            'LSAN_SYMBOLIZER_PATH',
35            'MSAN_SYMBOLIZER_PATH',
36            'TSAN_SYMBOLIZER_PATH',
37            'UBSAN_SYMBOLIZER_PATH',
38            'ASAN_OPTIONS',
39            'LSAN_OPTIONS',
40            'MSAN_OPTIONS',
41            'TSAN_OPTIONS',
42            'UBSAN_OPTIONS',
43            'ADB',
44            'ANDROID_SERIAL',
45            'SSH_AUTH_SOCK',
46            'SANITIZER_IGNORE_CVE_2016_2143',
47            'TMPDIR',
48            'TMP',
49            'TEMP',
50            'TEMPDIR',
51            'AVRLIT_BOARD',
52            'AVRLIT_PORT',
53            'FILECHECK_OPTS',
54            'VCINSTALLDIR',
55            'VCToolsinstallDir',
56            'VSINSTALLDIR',
57            'WindowsSdkDir',
58            'WindowsSDKLibVersion',
59            'SOURCE_DATE_EPOCH',
60            'GTEST_FILTER',
61            'DFLTCC',
62        ]
63
64        if sys.platform == 'win32':
65            pass_vars += [
66                'COMSPEC',
67                'INCLUDE',
68                'LIB',
69                'PATHEXT',
70                'USERPROFILE',
71            ]
72            environment['PYTHONBUFFERED'] = '1'
73
74        for var in pass_vars:
75            val = os.environ.get(var, '')
76            # Check for empty string as some variables such as LD_PRELOAD cannot be empty
77            # ('') for OS's such as OpenBSD.
78            if val:
79                environment[var] = val
80
81        # Set the default available features based on the LitConfig.
82        available_features = []
83        if litConfig.useValgrind:
84            available_features.append('valgrind')
85            if litConfig.valgrindLeakCheck:
86                available_features.append('vg_leak')
87
88        return TestingConfig(None,
89                             name = '<unnamed>',
90                             suffixes = set(),
91                             test_format = None,
92                             environment = environment,
93                             substitutions = [],
94                             unsupported = False,
95                             test_exec_root = None,
96                             test_source_root = None,
97                             excludes = [],
98                             available_features = available_features,
99                             pipefail = True,
100                             standalone_tests = False)
101
102    def load_from_path(self, path, litConfig):
103        """
104        load_from_path(path, litConfig)
105
106        Load the configuration module at the provided path into the given config
107        object.
108        """
109
110        # Load the config script data.
111        data = None
112        f = open(path)
113        try:
114            data = f.read()
115        except:
116            litConfig.fatal('unable to load config file: %r' % (path,))
117        f.close()
118
119        # Execute the config script to initialize the object.
120        cfg_globals = dict(globals())
121        cfg_globals['config'] = self
122        cfg_globals['lit_config'] = litConfig
123        cfg_globals['__file__'] = path
124        try:
125            exec(compile(data, path, 'exec'), cfg_globals, None)
126            if litConfig.debug:
127                litConfig.note('... loaded config %r' % path)
128        except SystemExit:
129            e = sys.exc_info()[1]
130            # We allow normal system exit inside a config file to just
131            # return control without error.
132            if e.args:
133                raise
134        except:
135            import traceback
136            litConfig.fatal(
137                'unable to parse config file %r, traceback: %s' % (
138                    path, traceback.format_exc()))
139        self.finish(litConfig)
140
141    def __init__(self, parent, name, suffixes, test_format,
142                 environment, substitutions, unsupported,
143                 test_exec_root, test_source_root, excludes,
144                 available_features, pipefail, limit_to_features = [],
145                 is_early = False, parallelism_group = None,
146                 standalone_tests = False):
147        self.parent = parent
148        self.name = str(name)
149        self.suffixes = set(suffixes)
150        self.test_format = test_format
151        self.environment = dict(environment)
152        self.substitutions = list(substitutions)
153        self.unsupported = unsupported
154        self.test_exec_root = test_exec_root
155        self.test_source_root = test_source_root
156        self.excludes = set(excludes)
157        self.available_features = set(available_features)
158        self.pipefail = pipefail
159        self.standalone_tests = standalone_tests
160        # This list is used by TestRunner.py to restrict running only tests that
161        # require one of the features in this list if this list is non-empty.
162        # Configurations can set this list to restrict the set of tests to run.
163        self.limit_to_features = set(limit_to_features)
164        self.parallelism_group = parallelism_group
165        self._recursiveExpansionLimit = None
166
167    @property
168    def recursiveExpansionLimit(self):
169        return self._recursiveExpansionLimit
170
171    @recursiveExpansionLimit.setter
172    def recursiveExpansionLimit(self, value):
173        if value is not None and not isinstance(value, int):
174            raise ValueError('recursiveExpansionLimit must be either None or an integer (got <{}>)'.format(value))
175        if isinstance(value, int) and value < 0:
176            raise ValueError('recursiveExpansionLimit must be a non-negative integer (got <{}>)'.format(value))
177        self._recursiveExpansionLimit = value
178
179    def finish(self, litConfig):
180        """finish() - Finish this config object, after loading is complete."""
181
182        self.name = str(self.name)
183        self.suffixes = set(self.suffixes)
184        self.environment = dict(self.environment)
185        self.substitutions = list(self.substitutions)
186        if self.test_exec_root is not None:
187            # FIXME: This should really only be suite in test suite config
188            # files. Should we distinguish them?
189            self.test_exec_root = str(self.test_exec_root)
190        if self.test_source_root is not None:
191            # FIXME: This should really only be suite in test suite config
192            # files. Should we distinguish them?
193            self.test_source_root = str(self.test_source_root)
194        self.excludes = set(self.excludes)
195
196    @property
197    def root(self):
198        """root attribute - The root configuration for the test suite."""
199        if self.parent is None:
200            return self
201        else:
202            return self.parent.root
203
204class SubstituteCaptures:
205    """
206    Helper class to indicate that the substitutions contains backreferences.
207
208    This can be used as the following in lit.cfg to mark subsitutions as having
209    back-references::
210
211        config.substutions.append(('\b[^ ]*.cpp', SubstituteCaptures('\0.txt')))
212
213    """
214    def __init__(self, substitution):
215        self.substitution = substitution
216
217    def replace(self, pattern, replacement):
218        return self.substitution
219
220    def __str__(self):
221        return self.substitution
222
223    def __len__(self):
224        return len(self.substitution)
225
226    def __getitem__(self, item):
227        return self.substitution.__getitem__(item)
228
229