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