| # -*- 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'] |
| |
| # 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', |
| # 'IOS_SIMULATOR_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: |
| 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((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) |
| |
| # 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) |
| 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')) |
| |
| config.substitutions.append( ('%clang_cc1', '%s -cc1 -internal-isystem %s' |
| % (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 ') ) |
| |
| # 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'. ***""") ) |
| |
| ### |
| |
| # Set available features we allow tests to conditionalize on. |
| # |
| # 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') |
| |
| # Exclude MSYS due to transforming '/' to 'X:/mingwroot/'. |
| if not platform.system() in ['Windows'] or not execute_external: |
| config.available_features.add('shell-preserves-root') |
| |
| # ANSI escape sequences in non-dumb terminal |
| if platform.system() not in ['Windows']: |
| config.available_features.add('ansi-escape-sequences') |
| |
| # Native compilation: host arch == target arch |
| if config.host_arch in 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') |
| |
| # [PR8833] LLP64-incompatible tests |
| if not re.match(r'^x86_64.*-(win32|mingw32)$', config.target_triple): |
| config.available_features.add('LP64') |
| |
| # [PR12920] "clang-driver" -- set if gcc driver is not used. |
| if not re.match(r'.*-(cygwin|mingw32)$', config.target_triple): |
| config.available_features.add('clang-driver') |
| |
| # Registered Targets |
| def get_llc_props(tool): |
| set_of_targets = set() |
| enable_assertions = False |
| |
| cmd = subprocess.Popen([tool, '-version'], stdout=subprocess.PIPE) |
| |
| # Parse the stdout to get the list of registered targets. |
| parse_targets = False |
| for line in cmd.stdout: |
| line = line.decode('ascii') |
| if parse_targets: |
| m = re.match( r'(.*) - ', line) |
| if m is not None: |
| set_of_targets.add(m.group(1).strip() + '-registered-target') |
| else: |
| break |
| elif "Registered Targets:" in line: |
| parse_targets = True |
| |
| if re.search(r'with assertions', line): |
| enable_assertions = True |
| |
| return {"set_of_targets": set_of_targets, |
| "enable_assertions": enable_assertions} |
| |
| llc_props = get_llc_props(os.path.join(llvm_tools_dir, 'llc')) |
| if len(llc_props['set_of_targets']) > 0: |
| config.available_features.update(llc_props['set_of_targets']) |
| else: |
| lit_config.fatal('No Targets Registered with the LLVM Tools!') |
| |
| if llc_props['enable_assertions']: |
| config.available_features.add('asserts') |
| |
| if lit.util.which('xmllint'): |
| config.available_features.add('xmllint') |
| |
| # Sanitizers. |
| if config.llvm_use_sanitizer == "Address": |
| config.available_features.add("asan") |
| if (config.llvm_use_sanitizer == "Memory" or |
| config.llvm_use_sanitizer == "MemoryWithOrigins"): |
| config.available_features.add("msan") |
| |
| # 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}) |
| |
| # On Darwin, support relocatable SDKs by providing Clang with a |
| # default system root path. |
| if 'darwin' in config.target_triple: |
| cmd = subprocess.Popen(['xcrun', '--show-sdk-path'], |
| stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| out, err = cmd.communicate() |
| out = out.strip() |
| res = cmd.wait() |
| if res == 0 and out: |
| sdk_path = out |
| lit_config.note('using SDKROOT: %r' % sdk_path) |
| config.environment['SDKROOT'] = sdk_path |