| # -*- Python -*- |
| |
| import os |
| import platform |
| import re |
| import subprocess |
| import tempfile |
| |
| import lit.formats |
| import lit.util |
| |
| # Configuration file for the 'lit' test runner. |
| |
| # name: The name of this test suite. |
| config.name = 'Clang' |
| |
| # Tweak PATH for Win32 |
| if platform.system() == 'Windows': |
| # Seek sane tools in directories and set to $PATH. |
| path = getattr(config, 'lit_tools_dir', None) |
| path = lit_config.getToolsPath(path, |
| config.environment['PATH'], |
| ['cmp.exe', 'grep.exe', 'sed.exe']) |
| if path is not None: |
| path = os.path.pathsep.join((path, |
| config.environment['PATH'])) |
| config.environment['PATH'] = path |
| |
| # Choose between lit's internal shell pipeline runner and a real shell. If |
| # LIT_USE_INTERNAL_SHELL is in the environment, we use that as an override. |
| use_lit_shell = os.environ.get("LIT_USE_INTERNAL_SHELL") |
| if use_lit_shell: |
| # 0 is external, "" is default, and everything else is internal. |
| execute_external = (use_lit_shell == "0") |
| else: |
| # Otherwise we default to internal on Windows and external elsewhere, as |
| # bash on Windows is usually very slow. |
| execute_external = (not sys.platform in ['win32']) |
| |
| # testFormat: The test format to use to interpret tests. |
| # |
| # For now we require '&&' between commands, until they get globally killed and |
| # the test runner updated. |
| config.test_format = lit.formats.ShTest(execute_external) |
| |
| # suffixes: A list of file extensions to treat as test files. |
| config.suffixes = ['.c', '.cpp', '.m', '.mm', '.cu', '.ll', '.cl', '.s', '.S', '.modulemap', '.test', '.rs'] |
| |
| # excludes: A list of directories to exclude from the testsuite. The 'Inputs' |
| # subdirectories contain auxiliary inputs for various tests in their parent |
| # directories. |
| config.excludes = ['Inputs', 'CMakeLists.txt', 'README.txt', 'LICENSE.txt'] |
| |
| # test_source_root: The root path where tests are located. |
| config.test_source_root = os.path.dirname(__file__) |
| |
| # test_exec_root: The root path where tests should be run. |
| clang_obj_root = getattr(config, 'clang_obj_root', None) |
| if clang_obj_root is not None: |
| config.test_exec_root = os.path.join(clang_obj_root, 'test') |
| |
| # Set llvm_{src,obj}_root for use by others. |
| config.llvm_src_root = getattr(config, 'llvm_src_root', None) |
| config.llvm_obj_root = getattr(config, 'llvm_obj_root', None) |
| |
| # Clear some environment variables that might affect Clang. |
| # |
| # This first set of vars are read by Clang, but shouldn't affect tests |
| # that aren't specifically looking for these features, or are required |
| # simply to run the tests at all. |
| # |
| # FIXME: Should we have a tool that enforces this? |
| |
| # safe_env_vars = ('TMPDIR', 'TEMP', 'TMP', 'USERPROFILE', 'PWD', |
| # 'MACOSX_DEPLOYMENT_TARGET', 'IPHONEOS_DEPLOYMENT_TARGET', |
| # 'VCINSTALLDIR', 'VC100COMNTOOLS', 'VC90COMNTOOLS', |
| # 'VC80COMNTOOLS') |
| possibly_dangerous_env_vars = ['COMPILER_PATH', 'RC_DEBUG_OPTIONS', |
| 'CINDEXTEST_PREAMBLE_FILE', 'LIBRARY_PATH', |
| 'CPATH', 'C_INCLUDE_PATH', 'CPLUS_INCLUDE_PATH', |
| 'OBJC_INCLUDE_PATH', 'OBJCPLUS_INCLUDE_PATH', |
| 'LIBCLANG_TIMING', 'LIBCLANG_OBJTRACKING', |
| 'LIBCLANG_LOGGING', 'LIBCLANG_BGPRIO_INDEX', |
| 'LIBCLANG_BGPRIO_EDIT', 'LIBCLANG_NOTHREADS', |
| 'LIBCLANG_RESOURCE_USAGE', |
| 'LIBCLANG_CODE_COMPLETION_LOGGING'] |
| # Clang/Win32 may refer to %INCLUDE%. vsvarsall.bat sets it. |
| if platform.system() != 'Windows': |
| possibly_dangerous_env_vars.append('INCLUDE') |
| for name in possibly_dangerous_env_vars: |
| if name in config.environment: |
| del config.environment[name] |
| |
| # Tweak the PATH to include the tools dir and the scripts dir. |
| if clang_obj_root is not None: |
| clang_tools_dir = getattr(config, 'clang_tools_dir', None) |
| if not clang_tools_dir: |
| lit_config.fatal('No Clang tools dir set!') |
| llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) |
| if not llvm_tools_dir: |
| lit_config.fatal('No LLVM tools dir set!') |
| path = os.path.pathsep.join(( |
| clang_tools_dir, llvm_tools_dir, config.environment['PATH'])) |
| config.environment['PATH'] = path |
| llvm_libs_dir = getattr(config, 'llvm_libs_dir', None) |
| if not llvm_libs_dir: |
| lit_config.fatal('No LLVM libs dir set!') |
| path = os.path.pathsep.join((llvm_libs_dir, |
| config.environment.get('LD_LIBRARY_PATH',''))) |
| config.environment['LD_LIBRARY_PATH'] = path |
| |
| # Propagate path to symbolizer for ASan/MSan. |
| for symbolizer in ['ASAN_SYMBOLIZER_PATH', 'MSAN_SYMBOLIZER_PATH']: |
| if symbolizer in os.environ: |
| config.environment[symbolizer] = os.environ[symbolizer] |
| |
| ### |
| |
| # Check that the object root is known. |
| if config.test_exec_root is None: |
| # Otherwise, we haven't loaded the site specific configuration (the user is |
| # probably trying to run on a test file directly, and either the site |
| # configuration hasn't been created by the build system, or we are in an |
| # out-of-tree build situation). |
| |
| # Check for 'clang_site_config' user parameter, and use that if available. |
| site_cfg = lit_config.params.get('clang_site_config', None) |
| if site_cfg and os.path.exists(site_cfg): |
| lit_config.load_config(config, site_cfg) |
| raise SystemExit |
| |
| # Try to detect the situation where we are using an out-of-tree build by |
| # looking for 'llvm-config'. |
| # |
| # FIXME: I debated (i.e., wrote and threw away) adding logic to |
| # automagically generate the lit.site.cfg if we are in some kind of fresh |
| # build situation. This means knowing how to invoke the build system though, |
| # and I decided it was too much magic. We should solve this by just having |
| # the .cfg files generated during the configuration step. |
| |
| llvm_config = lit.util.which('llvm-config', config.environment['PATH']) |
| if not llvm_config: |
| lit_config.fatal('No site specific configuration available!') |
| |
| # Get the source and object roots. |
| llvm_src_root = lit.util.capture(['llvm-config', '--src-root']).strip() |
| llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip() |
| clang_src_root = os.path.join(llvm_src_root, "tools", "clang") |
| clang_obj_root = os.path.join(llvm_obj_root, "tools", "clang") |
| |
| # Validate that we got a tree which points to here, using the standard |
| # tools/clang layout. |
| this_src_root = os.path.dirname(config.test_source_root) |
| if os.path.realpath(clang_src_root) != os.path.realpath(this_src_root): |
| lit_config.fatal('No site specific configuration available!') |
| |
| # Check that the site specific configuration exists. |
| site_cfg = os.path.join(clang_obj_root, 'test', 'lit.site.cfg') |
| if not os.path.exists(site_cfg): |
| lit_config.fatal( |
| 'No site specific configuration available! You may need to ' |
| 'run "make test" in your Clang build directory.') |
| |
| # Okay, that worked. Notify the user of the automagic, and reconfigure. |
| lit_config.note('using out-of-tree build at %r' % clang_obj_root) |
| lit_config.load_config(config, site_cfg) |
| raise SystemExit |
| |
| ### |
| |
| # Discover the 'clang' and 'clangcc' to use. |
| |
| import os |
| |
| def inferClang(PATH): |
| # Determine which clang to use. |
| clang = os.getenv('CLANG') |
| |
| # If the user set clang in the environment, definitely use that and don't |
| # try to validate. |
| if clang: |
| return clang |
| |
| # Otherwise look in the path. |
| clang = lit.util.which('clang', PATH) |
| |
| if not clang: |
| lit_config.fatal("couldn't find 'clang' program, try setting " |
| "CLANG in your environment") |
| |
| return clang |
| |
| config.clang = inferClang(config.environment['PATH']).replace('\\', '/') |
| if not lit_config.quiet: |
| lit_config.note('using clang: %r' % config.clang) |
| |
| # Plugins (loadable modules) |
| # TODO: This should be supplied by Makefile or autoconf. |
| if sys.platform in ['win32', 'cygwin']: |
| has_plugins = (config.enable_shared == 1) |
| else: |
| has_plugins = True |
| |
| if has_plugins and config.llvm_plugin_ext: |
| config.available_features.add('plugins') |
| |
| config.substitutions.append( ('%llvmshlibdir', config.llvm_shlib_dir) ) |
| config.substitutions.append( ('%pluginext', config.llvm_plugin_ext) ) |
| config.substitutions.append( ('%PATH%', config.environment['PATH']) ) |
| |
| if config.clang_examples: |
| config.available_features.add('examples') |
| |
| # Note that when substituting %clang_cc1 also fill in the include directory of |
| # the builtin headers. Those are part of even a freestanding environment, but |
| # Clang relies on the driver to locate them. |
| def getClangBuiltinIncludeDir(clang): |
| # FIXME: Rather than just getting the version, we should have clang print |
| # out its resource dir here in an easy to scrape form. |
| cmd = subprocess.Popen([clang, '-print-file-name=include'], |
| stdout=subprocess.PIPE, |
| env=config.environment) |
| if not cmd.stdout: |
| lit_config.fatal("Couldn't find the include dir for Clang ('%s')" % clang) |
| dir = cmd.stdout.read().strip() |
| if sys.platform in ['win32'] and execute_external: |
| # Don't pass dosish path separator to msys bash.exe. |
| dir = dir.replace('\\', '/') |
| # Ensure the result is an ascii string, across Python2.5+ - Python3. |
| return str(dir.decode('ascii')) |
| |
| def makeItaniumABITriple(triple): |
| m = re.match(r'(\w+)-(\w+)-(\w+)', triple) |
| if not m: |
| lit_config.fatal("Could not turn '%s' into Itanium ABI triple" % triple) |
| if m.group(3).lower() != 'win32': |
| # All non-win32 triples use the Itanium ABI. |
| return triple |
| return m.group(1) + '-' + m.group(2) + '-mingw32' |
| |
| def makeMSABITriple(triple): |
| m = re.match(r'(\w+)-(\w+)-(\w+)', triple) |
| if not m: |
| lit_config.fatal("Could not turn '%s' into MS ABI triple" % triple) |
| isa = m.group(1).lower() |
| vendor = m.group(2).lower() |
| os = m.group(3).lower() |
| if os == 'win32': |
| # If the OS is win32, we're done. |
| return triple |
| if isa.startswith('x86') or isa == 'amd64' or re.match(r'i\d86', isa): |
| # For x86 ISAs, adjust the OS. |
| return isa + '-' + vendor + '-win32' |
| # -win32 is not supported for non-x86 targets; use a default. |
| return 'i686-pc-win32' |
| |
| config.substitutions.append( ('%clang_cc1', |
| '%s -cc1 -internal-isystem %s -nostdsysteminc' |
| % (config.clang, |
| getClangBuiltinIncludeDir(config.clang))) ) |
| config.substitutions.append( ('%clang_cpp', ' ' + config.clang + |
| ' --driver-mode=cpp ')) |
| config.substitutions.append( ('%clang_cl', ' ' + config.clang + |
| ' --driver-mode=cl ')) |
| config.substitutions.append( ('%clangxx', ' ' + config.clang + |
| ' --driver-mode=g++ ')) |
| config.substitutions.append( ('%clang', ' ' + config.clang + ' ') ) |
| config.substitutions.append( ('%test_debuginfo', ' ' + config.llvm_src_root + '/utils/test_debuginfo.pl ') ) |
| config.substitutions.append( ('%itanium_abi_triple', makeItaniumABITriple(config.target_triple)) ) |
| config.substitutions.append( ('%ms_abi_triple', makeMSABITriple(config.target_triple)) ) |
| |
| # The host triple might not be set, at least if we're compiling clang from |
| # an already installed llvm. |
| if config.host_triple and config.host_triple != '@LLVM_HOST_TRIPLE@': |
| config.substitutions.append( ('%target_itanium_abi_host_triple', '--target=%s' % makeItaniumABITriple(config.host_triple)) ) |
| else: |
| config.substitutions.append( ('%target_itanium_abi_host_triple', '') ) |
| |
| # FIXME: Find nicer way to prohibit this. |
| config.substitutions.append( |
| (' clang ', """*** Do not use 'clang' in tests, use '%clang'. ***""") ) |
| config.substitutions.append( |
| (' clang\+\+ ', """*** Do not use 'clang++' in tests, use '%clangxx'. ***""")) |
| config.substitutions.append( |
| (' clang-cc ', |
| """*** Do not use 'clang-cc' in tests, use '%clang_cc1'. ***""") ) |
| config.substitutions.append( |
| (' clang -cc1 ', |
| """*** Do not use 'clang -cc1' in tests, use '%clang_cc1'. ***""") ) |
| config.substitutions.append( |
| (' %clang-cc1 ', |
| """*** invalid substitution, use '%clang_cc1'. ***""") ) |
| config.substitutions.append( |
| (' %clang-cpp ', |
| """*** invalid substitution, use '%clang_cpp'. ***""") ) |
| config.substitutions.append( |
| (' %clang-cl ', |
| """*** invalid substitution, use '%clang_cl'. ***""") ) |
| |
| # For each occurrence of a clang tool name as its own word, replace it |
| # with the full path to the build directory holding that tool. This |
| # ensures that we are testing the tools just built and not some random |
| # tools that might happen to be in the user's PATH. |
| tool_dirs = os.path.pathsep.join((clang_tools_dir, llvm_tools_dir)) |
| |
| # Regex assertions to reject neighbor hyphens/dots (seen in some tests). |
| # For example, don't match 'clang-check-' or '.clang-format'. |
| NoPreHyphenDot = r"(?<!(-|\.))" |
| NoPostHyphenDot = r"(?!(-|\.))" |
| NoPostBar = r"(?!(/|\\))" |
| |
| tool_patterns = [r"\bFileCheck\b", |
| r"\bc-index-test\b", |
| NoPreHyphenDot + r"\bclang-check\b" + NoPostHyphenDot, |
| NoPreHyphenDot + r"\bclang-format\b" + NoPostHyphenDot, |
| # FIXME: Some clang test uses opt? |
| NoPreHyphenDot + r"\bopt\b" + NoPostBar + NoPostHyphenDot, |
| # Handle these specially as they are strings searched |
| # for during testing. |
| r"\| \bcount\b", |
| r"\| \bnot\b"] |
| |
| if config.clang_examples: |
| tool_patterns.append(NoPreHyphenDot + r"\bclang-interpreter\b" + NoPostHyphenDot) |
| |
| for pattern in tool_patterns: |
| # Extract the tool name from the pattern. This relies on the tool |
| # name being surrounded by \b word match operators. If the |
| # pattern starts with "| ", include it in the string to be |
| # substituted. |
| tool_match = re.match(r"^(\\)?((\| )?)\W+b([0-9A-Za-z-_]+)\\b\W*$", |
| pattern) |
| tool_pipe = tool_match.group(2) |
| tool_name = tool_match.group(4) |
| tool_path = lit.util.which(tool_name, tool_dirs) |
| if not tool_path: |
| # Warn, but still provide a substitution. |
| lit_config.note('Did not find ' + tool_name + ' in ' + tool_dirs) |
| tool_path = clang_tools_dir + '/' + tool_name |
| config.substitutions.append((pattern, tool_pipe + tool_path)) |
| |
| ### |
| |
| # Set available features we allow tests to conditionalize on. |
| # |
| # Enabled/disabled features |
| if config.clang_staticanalyzer != 0: |
| config.available_features.add("staticanalyzer") |
| |
| # As of 2011.08, crash-recovery tests still do not pass on FreeBSD. |
| if platform.system() not in ['FreeBSD']: |
| config.available_features.add('crash-recovery') |
| |
| # Shell execution |
| if execute_external: |
| config.available_features.add('shell') |
| |
| # For tests that require Darwin to run. |
| # This is used by debuginfo-tests/*block*.m and debuginfo-tests/foreach.m. |
| if platform.system() in ['Darwin']: |
| config.available_features.add('system-darwin') |
| elif platform.system() in ['Windows']: |
| # For tests that require Windows to run. |
| config.available_features.add('system-windows') |
| |
| # ANSI escape sequences in non-dumb terminal |
| if platform.system() not in ['Windows']: |
| config.available_features.add('ansi-escape-sequences') |
| |
| # Capability to print utf8 to the terminal. |
| # Windows expects codepage, unless Wide API. |
| if platform.system() not in ['Windows']: |
| config.available_features.add('utf8-capable-terminal') |
| |
| # Native compilation: Check if triples match. |
| # FIXME: Consider cases that target can be executed |
| # even if host_triple were different from target_triple. |
| if config.host_triple == config.target_triple: |
| config.available_features.add("native") |
| |
| # Case-insensitive file system |
| def is_filesystem_case_insensitive(): |
| handle, path = tempfile.mkstemp(prefix='case-test', dir=config.test_exec_root) |
| isInsensitive = os.path.exists( |
| os.path.join( |
| os.path.dirname(path), |
| os.path.basename(path).upper() |
| )) |
| os.close(handle) |
| os.remove(path) |
| return isInsensitive |
| |
| if is_filesystem_case_insensitive(): |
| config.available_features.add('case-insensitive-filesystem') |
| |
| # Tests that require the /dev/fd filesystem. |
| if os.path.exists("/dev/fd/0") and sys.platform not in ['cygwin']: |
| config.available_features.add('dev-fd-fs') |
| |
| # Not set on native MS environment. |
| if not re.match(r'.*-win32$', config.target_triple): |
| config.available_features.add('non-ms-sdk') |
| |
| # Not set on native PS4 environment. |
| if not re.match(r'.*-scei-ps4', config.target_triple): |
| config.available_features.add('non-ps4-sdk') |
| |
| # [PR8833] LLP64-incompatible tests |
| if not re.match(r'^x86_64.*-(win32|mingw32|windows-gnu)$', config.target_triple): |
| config.available_features.add('LP64') |
| |
| # [PR12920] "clang-driver" -- set if gcc driver is not used. |
| if not re.match(r'.*-(cygwin)$', config.target_triple): |
| config.available_features.add('clang-driver') |
| |
| # [PR18856] Depends to remove opened file. On win32, a file could be removed |
| # only if all handles were closed. |
| if platform.system() not in ['Windows']: |
| config.available_features.add('can-remove-opened-file') |
| |
| # Returns set of available features, registered-target(s) and asserts. |
| def get_llvm_config_props(): |
| set_of_features = set() |
| |
| cmd = subprocess.Popen( |
| [ |
| os.path.join(llvm_tools_dir, 'llvm-config'), |
| '--assertion-mode', |
| '--targets-built', |
| ], |
| stdout=subprocess.PIPE, |
| env=config.environment |
| ) |
| # 1st line corresponds to --assertion-mode, "ON" or "OFF". |
| line = cmd.stdout.readline().strip().decode('ascii') |
| if line == "ON": |
| set_of_features.add('asserts') |
| |
| # 2nd line corresponds to --targets-built, like; |
| # AArch64 ARM CppBackend X86 |
| for arch in cmd.stdout.readline().decode('ascii').split(): |
| set_of_features.add(arch.lower() + '-registered-target') |
| |
| return set_of_features |
| |
| config.available_features.update(get_llvm_config_props()) |
| |
| if lit.util.which('xmllint'): |
| config.available_features.add('xmllint') |
| |
| # Sanitizers. |
| if 'Address' in config.llvm_use_sanitizer: |
| config.available_features.add("asan") |
| else: |
| config.available_features.add("not_asan") |
| if 'Memory' in config.llvm_use_sanitizer: |
| config.available_features.add("msan") |
| if 'Undefined' in config.llvm_use_sanitizer: |
| config.available_features.add("ubsan") |
| else: |
| config.available_features.add("not_ubsan") |
| |
| if config.enable_backtrace == "1": |
| config.available_features.add("backtrace") |
| |
| if config.have_zlib == "1": |
| config.available_features.add("zlib") |
| else: |
| config.available_features.add("nozlib") |
| |
| # Check if we should run long running tests. |
| if lit_config.params.get("run_long_tests", None) == "true": |
| config.available_features.add("long_tests") |
| |
| # Check if we should use gmalloc. |
| use_gmalloc_str = lit_config.params.get('use_gmalloc', None) |
| if use_gmalloc_str is not None: |
| if use_gmalloc_str.lower() in ('1', 'true'): |
| use_gmalloc = True |
| elif use_gmalloc_str.lower() in ('', '0', 'false'): |
| use_gmalloc = False |
| else: |
| lit_config.fatal('user parameter use_gmalloc should be 0 or 1') |
| else: |
| # Default to not using gmalloc |
| use_gmalloc = False |
| |
| # Allow use of an explicit path for gmalloc library. |
| # Will default to '/usr/lib/libgmalloc.dylib' if not set. |
| gmalloc_path_str = lit_config.params.get('gmalloc_path', |
| '/usr/lib/libgmalloc.dylib') |
| if use_gmalloc: |
| config.environment.update({'DYLD_INSERT_LIBRARIES' : gmalloc_path_str}) |
| |
| # Check if we should allow outputs to console. |
| run_console_tests = int(lit_config.params.get('enable_console', '0')) |
| if run_console_tests != 0: |
| config.available_features.add('console') |
| |
| lit.util.usePlatformSdkOnDarwin(config, lit_config) |