]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - utils/lit/lit/llvm/config.py
Vendor import of llvm trunk r351319 (just before the release_80 branch
[FreeBSD/FreeBSD.git] / utils / lit / lit / llvm / config.py
1 import os
2 import platform
3 import re
4 import subprocess
5 import sys
6
7 import lit.util
8 from lit.llvm.subst import FindTool
9 from lit.llvm.subst import ToolSubst
10
11
12 def binary_feature(on, feature, off_prefix):
13     return feature if on else off_prefix + feature
14
15
16 class LLVMConfig(object):
17
18     def __init__(self, lit_config, config):
19         self.lit_config = lit_config
20         self.config = config
21
22         features = config.available_features
23
24         self.use_lit_shell = False
25         # Tweak PATH for Win32 to decide to use bash.exe or not.
26         if sys.platform == 'win32':
27             # For tests that require Windows to run.
28             features.add('system-windows')
29
30             # Seek sane tools in directories and set to $PATH.
31             path = self.lit_config.getToolsPath(config.lit_tools_dir,
32                                                 config.environment['PATH'],
33                                                 ['cmp.exe', 'grep.exe', 'sed.exe'])
34             if path is not None:
35                 self.with_environment('PATH', path, append_path=True)
36             # Many tools behave strangely if these environment variables aren't set.
37             self.with_system_environment(['SystemDrive', 'SystemRoot', 'TEMP', 'TMP'])
38             self.use_lit_shell = True
39
40         # Choose between lit's internal shell pipeline runner and a real shell.  If
41         # LIT_USE_INTERNAL_SHELL is in the environment, we use that as an override.
42         lit_shell_env = os.environ.get('LIT_USE_INTERNAL_SHELL')
43         if lit_shell_env:
44             self.use_lit_shell = lit.util.pythonize_bool(lit_shell_env)
45
46         if not self.use_lit_shell:
47             features.add('shell')
48
49         # Running on Darwin OS
50         if platform.system() == 'Darwin':
51             # FIXME: lld uses the first, other projects use the second.
52             # We should standardize on the former.
53             features.add('system-linker-mach-o')
54             features.add('system-darwin')
55         elif platform.system() == 'Windows':
56             # For tests that require Windows to run.
57             features.add('system-windows')
58         elif platform.system() == "Linux":
59             features.add('system-linux')
60         elif platform.system() in ['FreeBSD']:
61             config.available_features.add('system-freebsd')
62         elif platform.system() == "NetBSD":
63             features.add('system-netbsd')
64
65         # Native compilation: host arch == default triple arch
66         # Both of these values should probably be in every site config (e.g. as
67         # part of the standard header.  But currently they aren't)
68         host_triple = getattr(config, 'host_triple', None)
69         target_triple = getattr(config, 'target_triple', None)
70         if host_triple and host_triple == target_triple:
71             features.add('native')
72
73         # Sanitizers.
74         sanitizers = getattr(config, 'llvm_use_sanitizer', '')
75         sanitizers = frozenset(x.lower() for x in sanitizers.split(';'))
76         features.add(binary_feature('address' in sanitizers, 'asan', 'not_'))
77         features.add(binary_feature('memory' in sanitizers, 'msan', 'not_'))
78         features.add(binary_feature(
79             'undefined' in sanitizers, 'ubsan', 'not_'))
80
81         have_zlib = getattr(config, 'have_zlib', None)
82         features.add(binary_feature(have_zlib, 'zlib', 'no'))
83
84         # Check if we should run long running tests.
85         long_tests = lit_config.params.get('run_long_tests', None)
86         if lit.util.pythonize_bool(long_tests):
87             features.add('long_tests')
88
89         if target_triple:
90             if re.match(r'^x86_64.*-apple', target_triple):
91                 host_cxx = getattr(config, 'host_cxx', None)
92                 if 'address' in sanitizers and self.get_clang_has_lsan(host_cxx, target_triple):
93                     self.with_environment(
94                         'ASAN_OPTIONS', 'detect_leaks=1', append_path=True)
95             if re.match(r'^x86_64.*-linux', target_triple):
96                 features.add('x86_64-linux')
97             if re.match(r'.*-windows-msvc$', target_triple):
98                 features.add('target-windows')
99
100         use_gmalloc = lit_config.params.get('use_gmalloc', None)
101         if lit.util.pythonize_bool(use_gmalloc):
102             # Allow use of an explicit path for gmalloc library.
103             # Will default to '/usr/lib/libgmalloc.dylib' if not set.
104             gmalloc_path_str = lit_config.params.get('gmalloc_path',
105                                                      '/usr/lib/libgmalloc.dylib')
106             if gmalloc_path_str is not None:
107                 self.with_environment(
108                     'DYLD_INSERT_LIBRARIES', gmalloc_path_str)
109
110     def with_environment(self, variable, value, append_path=False):
111         if append_path:
112             # For paths, we should be able to take a list of them and process all
113             # of them.
114             paths_to_add = value
115             if lit.util.is_string(paths_to_add):
116                 paths_to_add = [paths_to_add]
117
118             def norm(x):
119                 return os.path.normcase(os.path.normpath(x))
120
121             current_paths = self.config.environment.get(variable, None)
122             if current_paths:
123                 current_paths = current_paths.split(os.path.pathsep)
124                 paths = [norm(p) for p in current_paths]
125             else:
126                 paths = []
127
128             # If we are passed a list [a b c], then iterating this list forwards
129             # and adding each to the beginning would result in b c a.  So we
130             # need to iterate in reverse to end up with the original ordering.
131             for p in reversed(paths_to_add):
132                 # Move it to the front if it already exists, otherwise insert it at the
133                 # beginning.
134                 p = norm(p)
135                 try:
136                     paths.remove(p)
137                 except ValueError:
138                     pass
139                 paths = [p] + paths
140             value = os.pathsep.join(paths)
141         self.config.environment[variable] = value
142
143     def with_system_environment(self, variables, append_path=False):
144         if lit.util.is_string(variables):
145             variables = [variables]
146         for v in variables:
147             value = os.environ.get(v)
148             if value:
149                 self.with_environment(v, value, append_path)
150
151     def clear_environment(self, variables):
152         for name in variables:
153             if name in self.config.environment:
154                 del self.config.environment[name]
155
156     def get_process_output(self, command):
157         try:
158             cmd = subprocess.Popen(
159                 command, stdout=subprocess.PIPE,
160                 stderr=subprocess.PIPE, env=self.config.environment)
161             stdout, stderr = cmd.communicate()
162             stdout = lit.util.to_string(stdout)
163             stderr = lit.util.to_string(stderr)
164             return (stdout, stderr)
165         except OSError:
166             self.lit_config.fatal('Could not run process %s' % command)
167
168     def feature_config(self, features):
169         # Ask llvm-config about the specified feature.
170         arguments = [x for (x, _) in features]
171         config_path = os.path.join(self.config.llvm_tools_dir, 'llvm-config')
172
173         output, _ = self.get_process_output([config_path] + arguments)
174         lines = output.split('\n')
175
176         for (feature_line, (_, patterns)) in zip(lines, features):
177             # We should have either a callable or a dictionary.  If it's a
178             # dictionary, grep each key against the output and use the value if
179             # it matches.  If it's a callable, it does the entire translation.
180             if callable(patterns):
181                 features_to_add = patterns(feature_line)
182                 self.config.available_features.update(features_to_add)
183             else:
184                 for (re_pattern, feature) in patterns.items():
185                     if re.search(re_pattern, feature_line):
186                         self.config.available_features.add(feature)
187
188     # Note that when substituting %clang_cc1 also fill in the include directory of
189     # the builtin headers. Those are part of even a freestanding environment, but
190     # Clang relies on the driver to locate them.
191     def get_clang_builtin_include_dir(self, clang):
192         # FIXME: Rather than just getting the version, we should have clang print
193         # out its resource dir here in an easy to scrape form.
194         clang_dir, _ = self.get_process_output(
195             [clang, '-print-file-name=include'])
196
197         if not clang_dir:
198             self.lit_config.fatal(
199                 "Couldn't find the include dir for Clang ('%s')" % clang)
200
201         clang_dir = clang_dir.strip()
202         if sys.platform in ['win32'] and not self.use_lit_shell:
203             # Don't pass dosish path separator to msys bash.exe.
204             clang_dir = clang_dir.replace('\\', '/')
205         # Ensure the result is an ascii string, across Python2.5+ - Python3.
206         return clang_dir
207
208     # On macOS, LSan is only supported on clang versions 5 and higher
209     def get_clang_has_lsan(self, clang, triple):
210         if not clang:
211             self.lit_config.warning(
212                 'config.host_cxx is unset but test suite is configured to use sanitizers.')
213             return False
214
215         clang_binary = clang.split()[0]
216         version_string, _ = self.get_process_output(
217             [clang_binary, '--version'])
218         if not 'clang' in version_string:
219             self.lit_config.warning(
220                 "compiler '%s' does not appear to be clang, " % clang_binary +
221                 'but test suite is configured to use sanitizers.')
222             return False
223
224         if re.match(r'.*-linux', triple):
225             return True
226
227         if re.match(r'^x86_64.*-apple', triple):
228             version_regex = re.search(r'version ([0-9]+)\.([0-9]+).([0-9]+)', version_string)
229             major_version_number = int(version_regex.group(1))
230             minor_version_number = int(version_regex.group(2))
231             patch_version_number = int(version_regex.group(3))
232             if 'Apple LLVM' in version_string:
233                 # Apple LLVM doesn't yet support LSan
234                 return False
235             else:
236                 return major_version_number >= 5
237
238         return False
239
240     def make_itanium_abi_triple(self, triple):
241         m = re.match(r'(\w+)-(\w+)-(\w+)', triple)
242         if not m:
243             self.lit_config.fatal(
244                 "Could not turn '%s' into Itanium ABI triple" % triple)
245         if m.group(3).lower() != 'windows':
246             # All non-windows triples use the Itanium ABI.
247             return triple
248         return m.group(1) + '-' + m.group(2) + '-' + m.group(3) + '-gnu'
249
250     def make_msabi_triple(self, triple):
251         m = re.match(r'(\w+)-(\w+)-(\w+)', triple)
252         if not m:
253             self.lit_config.fatal(
254                 "Could not turn '%s' into MS ABI triple" % triple)
255         isa = m.group(1).lower()
256         vendor = m.group(2).lower()
257         os = m.group(3).lower()
258         if os == 'windows' and re.match(r'.*-msvc$', triple):
259             # If the OS is windows and environment is msvc, we're done.
260             return triple
261         if isa.startswith('x86') or isa == 'amd64' or re.match(r'i\d86', isa):
262             # For x86 ISAs, adjust the OS.
263             return isa + '-' + vendor + '-windows-msvc'
264         # -msvc is not supported for non-x86 targets; use a default.
265         return 'i686-pc-windows-msvc'
266
267     def add_tool_substitutions(self, tools, search_dirs=None):
268         if not search_dirs:
269             search_dirs = [self.config.llvm_tools_dir]
270
271         if lit.util.is_string(search_dirs):
272             search_dirs = [search_dirs]
273
274         tools = [x if isinstance(x, ToolSubst) else ToolSubst(x)
275                  for x in tools]
276
277         search_dirs = os.pathsep.join(search_dirs)
278         substitutions = []
279
280         for tool in tools:
281             match = tool.resolve(self, search_dirs)
282
283             # Either no match occurred, or there was an unresolved match that
284             # is ignored.
285             if not match:
286                 continue
287
288             subst_key, tool_pipe, command = match
289
290             # An unresolved match occurred that can't be ignored.  Fail without
291             # adding any of the previously-discovered substitutions.
292             if not command:
293                 return False
294
295             substitutions.append((subst_key, tool_pipe + command))
296
297         self.config.substitutions.extend(substitutions)
298         return True
299
300     def use_default_substitutions(self):
301         tool_patterns = [
302             ToolSubst('FileCheck', unresolved='fatal'),
303             # Handle these specially as they are strings searched for during testing.
304             ToolSubst(r'\| \bcount\b', command=FindTool(
305                 'count'), verbatim=True, unresolved='fatal'),
306             ToolSubst(r'\| \bnot\b', command=FindTool('not'), verbatim=True, unresolved='fatal')]
307
308         self.config.substitutions.append(('%python', '"%s"' % (sys.executable)))
309
310         self.add_tool_substitutions(
311             tool_patterns, [self.config.llvm_tools_dir])
312
313     def use_llvm_tool(self, name, search_env=None, required=False, quiet=False):
314         """Find the executable program 'name', optionally using the specified
315         environment variable as an override before searching the
316         configuration's PATH."""
317         # If the override is specified in the environment, use it without
318         # validation.
319         if search_env:
320             tool = self.config.environment.get(search_env)
321             if tool:
322                 return tool
323
324         # Otherwise look in the path.
325         tool = lit.util.which(name, self.config.environment['PATH'])
326
327         if required and not tool:
328             message = "couldn't find '{}' program".format(name)
329             if search_env:
330                 message = message + \
331                     ', try setting {} in your environment'.format(search_env)
332             self.lit_config.fatal(message)
333
334         if tool:
335             tool = os.path.normpath(tool)
336             if not self.lit_config.quiet and not quiet:
337                 self.lit_config.note('using {}: {}'.format(name, tool))
338         return tool
339
340     def use_clang(self, additional_tool_dirs=[], additional_flags=[], required=True):
341         """Configure the test suite to be able to invoke clang.
342
343         Sets up some environment variables important to clang, locates a
344         just-built or installed clang, and add a set of standard
345         substitutions useful to any test suite that makes use of clang.
346
347         """
348         # Clear some environment variables that might affect Clang.
349         #
350         # This first set of vars are read by Clang, but shouldn't affect tests
351         # that aren't specifically looking for these features, or are required
352         # simply to run the tests at all.
353         #
354         # FIXME: Should we have a tool that enforces this?
355
356         # safe_env_vars = ('TMPDIR', 'TEMP', 'TMP', 'USERPROFILE', 'PWD',
357         #                  'MACOSX_DEPLOYMENT_TARGET', 'IPHONEOS_DEPLOYMENT_TARGET',
358         #                  'VCINSTALLDIR', 'VC100COMNTOOLS', 'VC90COMNTOOLS',
359         #                  'VC80COMNTOOLS')
360         possibly_dangerous_env_vars = ['COMPILER_PATH', 'RC_DEBUG_OPTIONS',
361                                        'CINDEXTEST_PREAMBLE_FILE', 'LIBRARY_PATH',
362                                        'CPATH', 'C_INCLUDE_PATH', 'CPLUS_INCLUDE_PATH',
363                                        'OBJC_INCLUDE_PATH', 'OBJCPLUS_INCLUDE_PATH',
364                                        'LIBCLANG_TIMING', 'LIBCLANG_OBJTRACKING',
365                                        'LIBCLANG_LOGGING', 'LIBCLANG_BGPRIO_INDEX',
366                                        'LIBCLANG_BGPRIO_EDIT', 'LIBCLANG_NOTHREADS',
367                                        'LIBCLANG_RESOURCE_USAGE',
368                                        'LIBCLANG_CODE_COMPLETION_LOGGING']
369         # Clang/Win32 may refer to %INCLUDE%. vsvarsall.bat sets it.
370         if platform.system() != 'Windows':
371             possibly_dangerous_env_vars.append('INCLUDE')
372
373         self.clear_environment(possibly_dangerous_env_vars)
374
375         # Tweak the PATH to include the tools dir and the scripts dir.
376         # Put Clang first to avoid LLVM from overriding out-of-tree clang builds.
377         exe_dir_props = [self.config.name.lower() + '_tools_dir', 'clang_tools_dir', 'llvm_tools_dir']
378         paths = [getattr(self.config, pp) for pp in exe_dir_props
379                  if getattr(self.config, pp, None)]
380         paths = additional_tool_dirs + paths
381         self.with_environment('PATH', paths, append_path=True)
382
383         lib_dir_props = [self.config.name.lower() + '_libs_dir', 'clang_libs_dir', 'llvm_shlib_dir', 'llvm_libs_dir']
384         paths = [getattr(self.config, pp) for pp in lib_dir_props
385                  if getattr(self.config, pp, None)]
386
387         self.with_environment('LD_LIBRARY_PATH', paths, append_path=True)
388
389         # Discover the 'clang' and 'clangcc' to use.
390
391         self.config.clang = self.use_llvm_tool(
392             'clang', search_env='CLANG', required=required)
393
394         shl = getattr(self.config, 'llvm_shlib_dir', None)
395         pext = getattr(self.config, 'llvm_plugin_ext', None)
396         if shl:
397             self.config.substitutions.append(('%llvmshlibdir', shl))
398         if pext:
399             self.config.substitutions.append(('%pluginext', pext))
400
401         builtin_include_dir = self.get_clang_builtin_include_dir(self.config.clang)
402         tool_substitutions = [
403             ToolSubst('%clang', command=self.config.clang, extra_args=additional_flags),
404             ToolSubst('%clang_analyze_cc1', command='%clang_cc1', extra_args=['-analyze', '%analyze']+additional_flags),
405             ToolSubst('%clang_cc1', command=self.config.clang, extra_args=['-cc1', '-internal-isystem', builtin_include_dir, '-nostdsysteminc']+additional_flags),
406             ToolSubst('%clang_cpp', command=self.config.clang, extra_args=['--driver-mode=cpp']+additional_flags),
407             ToolSubst('%clang_cl', command=self.config.clang, extra_args=['--driver-mode=cl']+additional_flags),
408             ToolSubst('%clangxx', command=self.config.clang, extra_args=['--driver-mode=g++']+additional_flags),
409             ]
410         self.add_tool_substitutions(tool_substitutions)
411
412         self.config.substitutions.append(('%itanium_abi_triple',
413                                           self.make_itanium_abi_triple(self.config.target_triple)))
414         self.config.substitutions.append(('%ms_abi_triple',
415                                           self.make_msabi_triple(self.config.target_triple)))
416         self.config.substitutions.append(
417             ('%resource_dir', builtin_include_dir))
418
419         # The host triple might not be set, at least if we're compiling clang from
420         # an already installed llvm.
421         if self.config.host_triple and self.config.host_triple != '@LLVM_HOST_TRIPLE@':
422             self.config.substitutions.append(('%target_itanium_abi_host_triple',
423                                               '--target=%s' % self.make_itanium_abi_triple(self.config.host_triple)))
424         else:
425             self.config.substitutions.append(
426                 ('%target_itanium_abi_host_triple', ''))
427
428         # FIXME: Find nicer way to prohibit this.
429         self.config.substitutions.append(
430             (' clang ', """\"*** Do not use 'clang' in tests, use '%clang'. ***\""""))
431         self.config.substitutions.append(
432             (' clang\+\+ ', """\"*** Do not use 'clang++' in tests, use '%clangxx'. ***\""""))
433         self.config.substitutions.append(
434             (' clang-cc ',
435              """\"*** Do not use 'clang-cc' in tests, use '%clang_cc1'. ***\""""))
436         self.config.substitutions.append(
437             (' clang-cl ',
438              """\"*** Do not use 'clang-cl' in tests, use '%clang_cl'. ***\""""))
439         self.config.substitutions.append(
440             (' clang -cc1 -analyze ',
441              """\"*** Do not use 'clang -cc1 -analyze' in tests, use '%clang_analyze_cc1'. ***\""""))
442         self.config.substitutions.append(
443             (' clang -cc1 ',
444              """\"*** Do not use 'clang -cc1' in tests, use '%clang_cc1'. ***\""""))
445         self.config.substitutions.append(
446             (' %clang-cc1 ',
447              """\"*** invalid substitution, use '%clang_cc1'. ***\""""))
448         self.config.substitutions.append(
449             (' %clang-cpp ',
450              """\"*** invalid substitution, use '%clang_cpp'. ***\""""))
451         self.config.substitutions.append(
452             (' %clang-cl ',
453              """\"*** invalid substitution, use '%clang_cl'. ***\""""))
454
455     def use_lld(self, additional_tool_dirs=[], required=True):
456         """Configure the test suite to be able to invoke lld.
457
458         Sets up some environment variables important to lld, locates a
459         just-built or installed lld, and add a set of standard
460         substitutions useful to any test suite that makes use of lld.
461
462         """
463
464         # Tweak the PATH to include the tools dir and the scripts dir.
465         exe_dir_props = [self.config.name.lower() + '_tools_dir', 'lld_tools_dir', 'llvm_tools_dir']
466         paths = [getattr(self.config, pp) for pp in exe_dir_props
467                  if getattr(self.config, pp, None)]
468         paths = additional_tool_dirs + paths
469         self.with_environment('PATH', paths, append_path=True)
470
471         lib_dir_props = [self.config.name.lower() + '_libs_dir', 'lld_libs_dir', 'llvm_libs_dir']
472         paths = [getattr(self.config, pp) for pp in lib_dir_props
473                  if getattr(self.config, pp, None)]
474
475         self.with_environment('LD_LIBRARY_PATH', paths, append_path=True)
476
477         # Discover the 'clang' and 'clangcc' to use.
478
479         ld_lld = self.use_llvm_tool('ld.lld', required=required)
480         lld_link = self.use_llvm_tool('lld-link', required=required)
481         ld64_lld = self.use_llvm_tool('ld64.lld', required=required)
482         wasm_ld = self.use_llvm_tool('wasm-ld', required=required)
483
484         was_found = ld_lld and lld_link and ld64_lld and wasm_ld
485         tool_substitutions = []
486         if ld_lld:
487             tool_substitutions.append(ToolSubst('ld.lld', command=ld_lld))
488         if lld_link:
489             tool_substitutions.append(ToolSubst('lld-link', command=lld_link))
490         if ld64_lld:
491             tool_substitutions.append(ToolSubst('ld64.lld', command=ld64_lld))
492         if wasm_ld:
493             tool_substitutions.append(ToolSubst('wasm-ld', command=wasm_ld))
494         self.add_tool_substitutions(tool_substitutions)
495         return was_found