xref: /openbsd-src/gnu/llvm/llvm/utils/lit/lit/llvm/config.py (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
1import itertools
2import os
3import platform
4import re
5import subprocess
6import sys
7import errno
8
9import lit.util
10from lit.llvm.subst import FindTool
11from lit.llvm.subst import ToolSubst
12
13lit_path_displayed = False
14
15class LLVMConfig(object):
16
17    def __init__(self, lit_config, config):
18        self.lit_config = lit_config
19        self.config = config
20
21        features = config.available_features
22
23        self.use_lit_shell = False
24        # Tweak PATH for Win32 to decide to use bash.exe or not.
25        if sys.platform == 'win32':
26            # Seek necessary tools in directories and set to $PATH.
27            path = None
28            lit_tools_dir = getattr(config, 'lit_tools_dir', None)
29            required_tools = [
30                'cmp.exe', 'grep.exe', 'sed.exe', 'diff.exe', 'echo.exe']
31            path = self.lit_config.getToolsPath(lit_tools_dir,
32                                                config.environment['PATH'],
33                                                required_tools)
34            if path is None:
35                path = self._find_git_windows_unix_tools(required_tools)
36            if path is not None:
37                self.with_environment('PATH', path, append_path=True)
38            # Many tools behave strangely if these environment variables aren't
39            # set.
40            self.with_system_environment(
41                ['SystemDrive', 'SystemRoot', 'TEMP', 'TMP', 'PLATFORM'])
42            self.use_lit_shell = True
43
44            global lit_path_displayed
45            if not self.lit_config.quiet and lit_path_displayed is False:
46                self.lit_config.note("using lit tools: {}".format(path))
47                lit_path_displayed = True
48
49        # Choose between lit's internal shell pipeline runner and a real shell.
50        # If LIT_USE_INTERNAL_SHELL is in the environment, we use that as an
51        # override.
52        lit_shell_env = os.environ.get('LIT_USE_INTERNAL_SHELL')
53        if lit_shell_env:
54            self.use_lit_shell = lit.util.pythonize_bool(lit_shell_env)
55
56        if not self.use_lit_shell:
57            features.add('shell')
58
59        self.with_system_environment([
60            'ASAN_SYMBOLIZER_PATH',
61            'HWASAN_SYMBOLIZER_PATH',
62            'MSAN_SYMBOLIZER_PATH',
63            'TSAN_SYMBOLIZER_PATH',
64            'UBSAN_SYMBOLIZER_PATH'
65            'ASAN_OPTIONS',
66            'HWASAN_OPTIONS',
67            'MSAN_OPTIONS',
68            'TSAN_OPTIONS',
69            'UBSAN_OPTIONS',
70        ])
71
72        # Running on Darwin OS
73        if platform.system() == 'Darwin':
74            # FIXME: lld uses the first, other projects use the second.
75            # We should standardize on the former.
76            features.add('system-linker-mach-o')
77            features.add('system-darwin')
78        elif platform.system() == 'Windows':
79            # For tests that require Windows to run.
80            features.add('system-windows')
81        elif platform.system() == 'Linux':
82            features.add('system-linux')
83        elif platform.system() in ['FreeBSD']:
84            features.add('system-freebsd')
85        elif platform.system() == 'NetBSD':
86            features.add('system-netbsd')
87        elif platform.system() == 'AIX':
88            features.add('system-aix')
89        elif platform.system() == 'SunOS':
90            features.add('system-solaris')
91        elif platform.system() == 'OS/390':
92            features.add('system-zos')
93
94        # Native compilation: host arch == default triple arch
95        # Both of these values should probably be in every site config (e.g. as
96        # part of the standard header.  But currently they aren't)
97        host_triple = getattr(config, 'host_triple', None)
98        target_triple = getattr(config, 'target_triple', None)
99        features.add('target=%s' % target_triple)
100        if host_triple and host_triple == target_triple:
101            features.add('native')
102
103        # Sanitizers.
104        sanitizers = getattr(config, 'llvm_use_sanitizer', '')
105        sanitizers = frozenset(x.lower() for x in sanitizers.split(';'))
106        if 'address' in sanitizers:
107            features.add('asan')
108        if 'hwaddress' in sanitizers:
109            features.add('hwasan')
110        if 'memory' in sanitizers or 'memorywithorigins' in sanitizers:
111            features.add('msan')
112        if 'undefined' in sanitizers:
113            features.add('ubsan')
114
115        have_zlib = getattr(config, 'have_zlib', None)
116        if have_zlib:
117            features.add('zlib')
118        have_zstd = getattr(config, 'have_zstd', None)
119        if have_zstd:
120            features.add('zstd')
121
122        # Check if we should run long running tests.
123        long_tests = lit_config.params.get('run_long_tests', None)
124        if lit.util.pythonize_bool(long_tests):
125            features.add('long_tests')
126
127        if target_triple:
128            if re.match(r'^x86_64.*-apple', target_triple):
129                features.add('x86_64-apple')
130                host_cxx = getattr(config, 'host_cxx', None)
131                if ('address' in sanitizers and
132                        self.get_clang_has_lsan(host_cxx, target_triple)):
133                    self.with_environment(
134                        'ASAN_OPTIONS', 'detect_leaks=1', append_path=True)
135            if re.match(r'^x86_64.*-linux', target_triple):
136                features.add('x86_64-linux')
137            if re.match(r'^i.86.*', target_triple):
138                features.add('target-x86')
139            elif re.match(r'^x86_64.*', target_triple):
140                features.add('target-x86_64')
141            elif re.match(r'^aarch64.*', target_triple):
142                features.add('target-aarch64')
143            elif re.match(r'^arm64.*', target_triple):
144                features.add('target-aarch64')
145            elif re.match(r'^arm.*', target_triple):
146                features.add('target-arm')
147
148        use_gmalloc = lit_config.params.get('use_gmalloc', None)
149        if lit.util.pythonize_bool(use_gmalloc):
150            # Allow use of an explicit path for gmalloc library.
151            # Will default to '/usr/lib/libgmalloc.dylib' if not set.
152            gmalloc_path_str = lit_config.params.get(
153                'gmalloc_path', '/usr/lib/libgmalloc.dylib')
154            if gmalloc_path_str is not None:
155                self.with_environment(
156                    'DYLD_INSERT_LIBRARIES', gmalloc_path_str)
157
158    def _find_git_windows_unix_tools(self, tools_needed):
159        assert(sys.platform == 'win32')
160        if sys.version_info.major >= 3:
161            import winreg
162        else:
163            import _winreg as winreg
164
165        # Search both the 64 and 32-bit hives, as well as HKLM + HKCU
166        masks = [0, winreg.KEY_WOW64_64KEY]
167        hives = [winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER]
168        for mask, hive in itertools.product(masks, hives):
169            try:
170                with winreg.OpenKey(hive, r"SOFTWARE\GitForWindows", 0,
171                                    winreg.KEY_READ | mask) as key:
172                    install_root, _ = winreg.QueryValueEx(key, 'InstallPath')
173
174                    if not install_root:
175                        continue
176                    candidate_path = os.path.join(install_root, 'usr', 'bin')
177                    if not lit.util.checkToolsPath(
178                               candidate_path, tools_needed):
179                        continue
180
181                    # We found it, stop enumerating.
182                    return lit.util.to_string(candidate_path)
183            except:
184                continue
185
186        return None
187
188    def with_environment(self, variable, value, append_path=False):
189        if append_path:
190            # For paths, we should be able to take a list of them and process
191            # all of them.
192            paths_to_add = value
193            if lit.util.is_string(paths_to_add):
194                paths_to_add = [paths_to_add]
195
196            def norm(x):
197                return os.path.normcase(os.path.normpath(x))
198
199            current_paths = self.config.environment.get(variable, None)
200            if current_paths:
201                current_paths = current_paths.split(os.path.pathsep)
202                paths = [norm(p) for p in current_paths]
203            else:
204                paths = []
205
206            # If we are passed a list [a b c], then iterating this list forwards
207            # and adding each to the beginning would result in c b a.  So we
208            # need to iterate in reverse to end up with the original ordering.
209            for p in reversed(paths_to_add):
210                # Move it to the front if it already exists, otherwise insert
211                # it at the beginning.
212                p = norm(p)
213                try:
214                    paths.remove(p)
215                except ValueError:
216                    pass
217                paths = [p] + paths
218            value = os.pathsep.join(paths)
219        self.config.environment[variable] = value
220
221    def with_system_environment(self, variables, append_path=False):
222        if lit.util.is_string(variables):
223            variables = [variables]
224        for v in variables:
225            value = os.environ.get(v)
226            if value:
227                self.with_environment(v, value, append_path)
228
229    def clear_environment(self, variables):
230        for name in variables:
231            if name in self.config.environment:
232                del self.config.environment[name]
233
234    def get_process_output(self, command):
235        try:
236            cmd = subprocess.Popen(
237                command, stdout=subprocess.PIPE,
238                stderr=subprocess.PIPE, env=self.config.environment)
239            stdout, stderr = cmd.communicate()
240            stdout = lit.util.to_string(stdout)
241            stderr = lit.util.to_string(stderr)
242            return (stdout, stderr)
243        except OSError:
244            self.lit_config.fatal('Could not run process %s' % command)
245
246    def feature_config(self, features):
247        # Ask llvm-config about the specified feature.
248        arguments = [x for (x, _) in features]
249        config_path = os.path.join(self.config.llvm_tools_dir, 'llvm-config')
250
251        output, _ = self.get_process_output([config_path] + arguments)
252        lines = output.split('\n')
253
254        for (feature_line, (_, patterns)) in zip(lines, features):
255            # We should have either a callable or a dictionary.  If it's a
256            # dictionary, grep each key against the output and use the value if
257            # it matches.  If it's a callable, it does the entire translation.
258            if callable(patterns):
259                features_to_add = patterns(feature_line)
260                self.config.available_features.update(features_to_add)
261            else:
262                for (re_pattern, feature) in patterns.items():
263                    if re.search(re_pattern, feature_line):
264                        self.config.available_features.add(feature)
265
266    # Note that when substituting %clang_cc1 also fill in the include directory
267    # of the builtin headers. Those are part of even a freestanding
268    # environment, but Clang relies on the driver to locate them.
269    def get_clang_builtin_include_dir(self, clang):
270        # FIXME: Rather than just getting the version, we should have clang
271        # print out its resource dir here in an easy to scrape form.
272        clang_dir, _ = self.get_process_output(
273            [clang, '-print-file-name=include'])
274
275        if not clang_dir:
276            print(clang)
277            self.lit_config.fatal(
278                "Couldn't find the include dir for Clang ('%s')" % clang)
279
280        clang_dir = clang_dir.strip()
281        if sys.platform in ['win32'] and not self.use_lit_shell:
282            # Don't pass dosish path separator to msys bash.exe.
283            clang_dir = clang_dir.replace('\\', '/')
284        # Ensure the result is an ascii string, across Python2.5+ - Python3.
285        return clang_dir
286
287    # On macOS, LSan is only supported on clang versions 5 and higher
288    def get_clang_has_lsan(self, clang, triple):
289        if not clang:
290            self.lit_config.warning(
291                'config.host_cxx is unset but test suite is configured '
292                'to use sanitizers.')
293            return False
294
295        clang_binary = clang.split()[0]
296        version_string, _ = self.get_process_output(
297            [clang_binary, '--version'])
298        if not 'clang' in version_string:
299            self.lit_config.warning(
300                "compiler '%s' does not appear to be clang, " % clang_binary +
301                'but test suite is configured to use sanitizers.')
302            return False
303
304        if re.match(r'.*-linux', triple):
305            return True
306
307        if re.match(r'^x86_64.*-apple', triple):
308            version_regex = re.search(r'version ([0-9]+)\.([0-9]+).([0-9]+)',
309                                      version_string)
310            major_version_number = int(version_regex.group(1))
311            minor_version_number = int(version_regex.group(2))
312            patch_version_number = int(version_regex.group(3))
313            if ('Apple LLVM' in version_string or
314                'Apple clang' in version_string):
315                # Apple clang doesn't yet support LSan
316                return False
317            return major_version_number >= 5
318
319        return False
320
321    def make_itanium_abi_triple(self, triple):
322        m = re.match(r'(\w+)-(\w+)-(\w+)', triple)
323        if not m:
324            self.lit_config.fatal(
325                "Could not turn '%s' into Itanium ABI triple" % triple)
326        if m.group(3).lower() != 'windows':
327            # All non-windows triples use the Itanium ABI.
328            return triple
329        return m.group(1) + '-' + m.group(2) + '-' + m.group(3) + '-gnu'
330
331    def make_msabi_triple(self, triple):
332        m = re.match(r'(\w+)-(\w+)-(\w+)', triple)
333        if not m:
334            self.lit_config.fatal(
335                "Could not turn '%s' into MS ABI triple" % triple)
336        isa = m.group(1).lower()
337        vendor = m.group(2).lower()
338        os = m.group(3).lower()
339        if os == 'windows' and re.match(r'.*-msvc$', triple):
340            # If the OS is windows and environment is msvc, we're done.
341            return triple
342        if isa.startswith('x86') or isa == 'amd64' or re.match(r'i\d86', isa):
343            # For x86 ISAs, adjust the OS.
344            return isa + '-' + vendor + '-windows-msvc'
345        # -msvc is not supported for non-x86 targets; use a default.
346        return 'i686-pc-windows-msvc'
347
348    def add_tool_substitutions(self, tools, search_dirs=None):
349        if not search_dirs:
350            search_dirs = [self.config.llvm_tools_dir]
351
352        if lit.util.is_string(search_dirs):
353            search_dirs = [search_dirs]
354
355        tools = [x if isinstance(x, ToolSubst) else ToolSubst(x)
356                 for x in tools]
357
358        search_dirs = os.pathsep.join(search_dirs)
359        substitutions = []
360
361        for tool in tools:
362            match = tool.resolve(self, search_dirs)
363
364            # Either no match occurred, or there was an unresolved match that
365            # is ignored.
366            if not match:
367                continue
368
369            subst_key, tool_pipe, command = match
370
371            # An unresolved match occurred that can't be ignored.  Fail without
372            # adding any of the previously-discovered substitutions.
373            if not command:
374                return False
375
376            substitutions.append((subst_key, tool_pipe + command))
377
378        self.config.substitutions.extend(substitutions)
379        return True
380
381    def add_err_msg_substitutions(self):
382        # Python's strerror may not supply the same message
383        # as C++ std::error_code. One example of such a platform is
384        # Visual Studio. errc_messages may be supplied which contains the error
385        # messages for ENOENT, EISDIR, EINVAL and EACCES as a semi colon
386        # separated string. LLVM testsuites can use get_errc_messages in cmake
387        # to automatically get the messages and pass them into lit.
388        errc_messages = getattr(self.config, 'errc_messages', '')
389        if len(errc_messages) != 0:
390            (errc_enoent, errc_eisdir,
391             errc_einval, errc_eacces) = errc_messages.split(';')
392            self.config.substitutions.append(
393                ('%errc_ENOENT', '\'' + errc_enoent + '\''))
394            self.config.substitutions.append(
395                ('%errc_EISDIR', '\'' + errc_eisdir + '\''))
396            self.config.substitutions.append(
397                ('%errc_EINVAL', '\'' + errc_einval + '\''))
398            self.config.substitutions.append(
399                ('%errc_EACCES', '\'' + errc_eacces + '\''))
400        else:
401            self.config.substitutions.append(
402                ('%errc_ENOENT', '\'' + os.strerror(errno.ENOENT) + '\''))
403            self.config.substitutions.append(
404                ('%errc_EISDIR', '\'' + os.strerror(errno.EISDIR) + '\''))
405            self.config.substitutions.append(
406                ('%errc_EINVAL', '\'' + os.strerror(errno.EINVAL) + '\''))
407            self.config.substitutions.append(
408                ('%errc_EACCES', '\'' + os.strerror(errno.EACCES) + '\''))
409
410    def use_default_substitutions(self):
411        tool_patterns = [
412            ToolSubst('FileCheck', unresolved='fatal'),
413            # Handle these specially as they are strings searched for during
414            # testing.
415            ToolSubst(r'\| \bcount\b', command=FindTool('count'),
416                verbatim=True, unresolved='fatal'),
417            ToolSubst(r'\| \bnot\b', command=FindTool('not'),
418                verbatim=True, unresolved='fatal')]
419
420        self.config.substitutions.append(('%python', '"%s"' % (sys.executable)))
421
422        self.add_tool_substitutions(
423            tool_patterns, [self.config.llvm_tools_dir])
424
425        self.add_err_msg_substitutions()
426
427    def use_llvm_tool(self, name, search_env=None, required=False, quiet=False,
428                      search_paths=None, use_installed=False):
429        """Find the executable program 'name', optionally using the specified
430        environment variable as an override before searching the build directory
431        and then optionally the configuration's PATH."""
432        # If the override is specified in the environment, use it without
433        # validation.
434        tool = None
435        if search_env:
436            tool = self.config.environment.get(search_env)
437
438        if not tool:
439            if search_paths is None:
440                search_paths = [self.config.llvm_tools_dir]
441            # Use the specified search paths.
442            path = os.pathsep.join(search_paths)
443            tool = lit.util.which(name, path)
444
445        if not tool and use_installed:
446            # Otherwise look in the path, if enabled.
447            tool = lit.util.which(name, self.config.environment['PATH'])
448
449        if required and not tool:
450            message = "couldn't find '{}' program".format(name)
451            if search_env:
452                message = message + \
453                    ', try setting {} in your environment'.format(search_env)
454            self.lit_config.fatal(message)
455
456        if tool:
457            tool = os.path.normpath(tool)
458            if not self.lit_config.quiet and not quiet:
459                self.lit_config.note('using {}: {}'.format(name, tool))
460        return tool
461
462    def use_clang(self, additional_tool_dirs=[], additional_flags=[],
463                  required=True, use_installed=False):
464        """Configure the test suite to be able to invoke clang.
465
466        Sets up some environment variables important to clang, locates a
467        just-built or optionally an installed clang, and add a set of standard
468        substitutions useful to any test suite that makes use of clang.
469
470        """
471        # Clear some environment variables that might affect Clang.
472        #
473        # This first set of vars are read by Clang, but shouldn't affect tests
474        # that aren't specifically looking for these features, or are required
475        # simply to run the tests at all.
476        #
477        # FIXME: Should we have a tool that enforces this?
478
479        # safe_env_vars = (
480        #     'TMPDIR', 'TEMP', 'TMP', 'USERPROFILE', 'PWD',
481        #     'MACOSX_DEPLOYMENT_TARGET', 'IPHONEOS_DEPLOYMENT_TARGET',
482        #     'VCINSTALLDIR', 'VC100COMNTOOLS', 'VC90COMNTOOLS',
483        #     'VC80COMNTOOLS')
484        possibly_dangerous_env_vars = [
485            'COMPILER_PATH', 'RC_DEBUG_OPTIONS',
486            'CINDEXTEST_PREAMBLE_FILE', 'LIBRARY_PATH',
487            'CPATH', 'C_INCLUDE_PATH', 'CPLUS_INCLUDE_PATH',
488            'OBJC_INCLUDE_PATH', 'OBJCPLUS_INCLUDE_PATH',
489            'LIBCLANG_TIMING', 'LIBCLANG_OBJTRACKING',
490            'LIBCLANG_LOGGING', 'LIBCLANG_BGPRIO_INDEX',
491            'LIBCLANG_BGPRIO_EDIT', 'LIBCLANG_NOTHREADS',
492            'LIBCLANG_RESOURCE_USAGE',
493            'LIBCLANG_CODE_COMPLETION_LOGGING',
494            ]
495        # Clang/Win32 may refer to %INCLUDE%. vsvarsall.bat sets it.
496        if platform.system() != 'Windows':
497            possibly_dangerous_env_vars.append('INCLUDE')
498
499        self.clear_environment(possibly_dangerous_env_vars)
500
501        # Tweak the PATH to include the tools dir and the scripts dir.
502        # Put Clang first to avoid LLVM from overriding out-of-tree clang
503        # builds.
504        exe_dir_props = [self.config.name.lower() + '_tools_dir',
505                         'clang_tools_dir', 'llvm_tools_dir']
506        paths = [getattr(self.config, pp) for pp in exe_dir_props
507                 if getattr(self.config, pp, None)]
508        paths = additional_tool_dirs + paths
509        self.with_environment('PATH', paths, append_path=True)
510
511        lib_dir_props = [
512            self.config.name.lower() + '_libs_dir',
513            'llvm_shlib_dir',
514            'llvm_libs_dir',
515            ]
516        lib_paths = [getattr(self.config, pp) for pp in lib_dir_props
517                     if getattr(self.config, pp, None)]
518
519        self.with_environment('LD_LIBRARY_PATH', lib_paths, append_path=True)
520
521        shl = getattr(self.config, 'llvm_shlib_dir', None)
522        pext = getattr(self.config, 'llvm_plugin_ext', None)
523        if shl:
524            self.config.substitutions.append(('%llvmshlibdir', shl))
525        if pext:
526            self.config.substitutions.append(('%pluginext', pext))
527
528        # Discover the 'clang' and 'clangcc' to use.
529        self.config.clang = self.use_llvm_tool(
530            'clang', search_env='CLANG', required=required,
531            search_paths=paths, use_installed=use_installed)
532        if self.config.clang:
533          self.config.available_features.add('clang')
534          builtin_include_dir = self.get_clang_builtin_include_dir(
535              self.config.clang)
536          tool_substitutions = [
537              ToolSubst('%clang', command=self.config.clang,
538                        extra_args=additional_flags),
539              ToolSubst('%clang_analyze_cc1', command='%clang_cc1',
540                        extra_args=['-analyze', '%analyze',
541                                    '-setup-static-analyzer']+additional_flags),
542              ToolSubst('%clang_cc1', command=self.config.clang,
543                        extra_args=['-cc1', '-internal-isystem',
544                                    builtin_include_dir, '-nostdsysteminc'] +
545                                   additional_flags),
546              ToolSubst('%clang_cpp', command=self.config.clang,
547                        extra_args=['--driver-mode=cpp']+additional_flags),
548              ToolSubst('%clang_cl', command=self.config.clang,
549                        extra_args=['--driver-mode=cl']+additional_flags),
550              ToolSubst('%clang_dxc', command=self.config.clang,
551                        extra_args=['--driver-mode=dxc']+additional_flags),
552              ToolSubst('%clangxx', command=self.config.clang,
553                        extra_args=['--driver-mode=g++']+additional_flags),
554              ]
555          self.add_tool_substitutions(tool_substitutions)
556          self.config.substitutions.append(
557              ('%resource_dir', builtin_include_dir))
558
559        self.config.substitutions.append(
560            ('%itanium_abi_triple',
561             self.make_itanium_abi_triple(self.config.target_triple)))
562        self.config.substitutions.append(
563            ('%ms_abi_triple',
564             self.make_msabi_triple(self.config.target_triple)))
565
566        # The host triple might not be set, at least if we're compiling clang
567        # from an already installed llvm.
568        if (self.config.host_triple and
569                self.config.host_triple != '@LLVM_HOST_TRIPLE@'):
570            self.config.substitutions.append(
571                ('%target_itanium_abi_host_triple',
572                 '--target=' + self.make_itanium_abi_triple(
573                                   self.config.host_triple)))
574        else:
575            self.config.substitutions.append(
576                ('%target_itanium_abi_host_triple', ''))
577
578        # TODO: Many tests work across many language standards. Before
579        # https://discourse.llvm.org/t/lit-run-a-run-line-multiple-times-with-different-replacements/64932
580        # has a solution, provide substitutions to conveniently try every standard with LIT_CLANG_STD_GROUP.
581        clang_std_group = int(os.environ.get('LIT_CLANG_STD_GROUP', '0'))
582        clang_std_values = ('98', '11', '14', '17', '20', '2b')
583        def add_std_cxx(s):
584            t = s[8:]
585            if t.endswith('-'):
586                t += clang_std_values[-1]
587            l = clang_std_values.index(t[0:2] if t[0:2] != '23' else '2b')
588            h = clang_std_values.index(t[3:5])
589            # Let LIT_CLANG_STD_GROUP=0 pick the highest value (likely the most relevant
590            # standard).
591            l = h - clang_std_group % (h-l+1)
592            self.config.substitutions.append((s, '-std=c++' + clang_std_values[l]))
593
594        add_std_cxx('%std_cxx98-14')
595        add_std_cxx('%std_cxx98-')
596        add_std_cxx('%std_cxx11-14')
597        add_std_cxx('%std_cxx11-')
598        add_std_cxx('%std_cxx14-')
599        add_std_cxx('%std_cxx17-20')
600        add_std_cxx('%std_cxx17-')
601        add_std_cxx('%std_cxx20-')
602        add_std_cxx('%std_cxx23-')
603
604        # FIXME: Find nicer way to prohibit this.
605        def prefer(this, to):
606            return '''\"*** Do not use '%s' in tests, use '%s'. ***\"''' % (
607                to, this)
608        self.config.substitutions.append(
609            (' clang ', prefer('%clang', 'clang')))
610        self.config.substitutions.append(
611            (r' clang\+\+ ', prefer('%clangxx', 'clang++')))
612        self.config.substitutions.append(
613            (' clang-cc ', prefer('%clang_cc1', 'clang-cc')))
614        self.config.substitutions.append(
615            (' clang-cl ', prefer('%clang_cl', 'clang-cl')))
616        self.config.substitutions.append(
617            (' clang -cc1 -analyze ',
618             prefer('%clang_analyze_cc1', 'clang -cc1 -analyze')))
619        self.config.substitutions.append(
620            (' clang -cc1 ', prefer('%clang_cc1', 'clang -cc1')))
621        self.config.substitutions.append(
622            (' %clang-cc1 ',
623             '''\"*** invalid substitution, use '%clang_cc1'. ***\"'''))
624        self.config.substitutions.append(
625            (' %clang-cpp ',
626             '''\"*** invalid substitution, use '%clang_cpp'. ***\"'''))
627        self.config.substitutions.append(
628            (' %clang-cl ',
629             '''\"*** invalid substitution, use '%clang_cl'. ***\"'''))
630
631    def use_lld(self, additional_tool_dirs=[], required=True,
632                use_installed=False):
633        """Configure the test suite to be able to invoke lld.
634
635        Sets up some environment variables important to lld, locates a
636        just-built or optionally an installed lld, and add a set of standard
637        substitutions useful to any test suite that makes use of lld.
638
639        """
640
641        # Tweak the PATH to include the tools dir and the scripts dir.
642        exe_dir_props = [self.config.name.lower() + '_tools_dir',
643                         'lld_tools_dir', 'llvm_tools_dir']
644        paths = [getattr(self.config, pp) for pp in exe_dir_props
645                 if getattr(self.config, pp, None)]
646        paths = additional_tool_dirs + paths
647        self.with_environment('PATH', paths, append_path=True)
648
649        lib_dir_props = [self.config.name.lower() + '_libs_dir',
650                         'lld_libs_dir', 'llvm_shlib_dir', 'llvm_libs_dir']
651        lib_paths = [getattr(self.config, pp) for pp in lib_dir_props
652                     if getattr(self.config, pp, None)]
653
654        self.with_environment('LD_LIBRARY_PATH', lib_paths, append_path=True)
655
656        # Discover the LLD executables to use.
657
658        ld_lld = self.use_llvm_tool('ld.lld', required=required,
659                                    search_paths=paths,
660                                    use_installed=use_installed)
661        lld_link = self.use_llvm_tool('lld-link', required=required,
662                                      search_paths=paths,
663                                      use_installed=use_installed)
664        ld64_lld = self.use_llvm_tool('ld64.lld', required=required,
665                                      search_paths=paths,
666                                      use_installed=use_installed)
667        wasm_ld = self.use_llvm_tool('wasm-ld', required=required,
668                                     search_paths=paths,
669                                     use_installed=use_installed)
670
671        was_found = ld_lld and lld_link and ld64_lld and wasm_ld
672        tool_substitutions = []
673        if ld_lld:
674            tool_substitutions.append(ToolSubst(r'ld\.lld', command=ld_lld))
675            self.config.available_features.add('ld.lld')
676        if lld_link:
677            tool_substitutions.append(ToolSubst('lld-link', command=lld_link))
678            self.config.available_features.add('lld-link')
679        if ld64_lld:
680            tool_substitutions.append(ToolSubst(r'ld64\.lld', command=ld64_lld))
681            self.config.available_features.add('ld64.lld')
682        if wasm_ld:
683            tool_substitutions.append(ToolSubst('wasm-ld', command=wasm_ld))
684            self.config.available_features.add('wasm-ld')
685        self.add_tool_substitutions(tool_substitutions)
686
687        return was_found
688