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