From 93619335dfd47bad08b82d0615e2a8e8a8ddd985 Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Sat, 7 Jan 2017 00:01:55 +0000 Subject: [PATCH] Fix linking of DLL's on Windows On Windows the runtime search path for DLL's is the same as PATH. This patch changes the test suite to add the libc++ build directory to the runtime PATH. llvm-svn: 291309 --- libcxx/test/libcxx/test/config.py | 56 ++++++++++++++++++++--------- libcxx/test/libcxx/test/executor.py | 25 ++++++++++++- libcxx/test/libcxx/test/format.py | 5 --- 3 files changed, 63 insertions(+), 23 deletions(-) diff --git a/libcxx/test/libcxx/test/config.py b/libcxx/test/libcxx/test/config.py index 7043b8a01c4a..c8771999dd76 100644 --- a/libcxx/test/libcxx/test/config.py +++ b/libcxx/test/libcxx/test/config.py @@ -67,7 +67,8 @@ class Configuration(object): self.cxx_library_root = None self.cxx_runtime_root = None self.abi_library_root = None - self.env = {} + self.link_shared = self.get_lit_bool('enable_shared', default=True) + self.exec_env = {} self.use_target = False self.use_system_cxx_lib = False self.use_clang_verify = False @@ -146,7 +147,7 @@ class Configuration(object): # Print as list to prevent "set([...])" from being printed. self.lit_config.note('Using available_features: %s' % list(self.config.available_features)) - self.lit_config.note('Using environment: %r' % self.env) + self.lit_config.note('Using environment: %r' % self.exec_env) def get_test_format(self): return LibcxxTestFormat( @@ -154,7 +155,7 @@ class Configuration(object): self.use_clang_verify, self.execute_external, self.executor, - exec_env=self.env) + exec_env=self.exec_env) def configure_executor(self): exec_str = self.get_lit_conf('executor', "None") @@ -207,6 +208,11 @@ class Configuration(object): self.config.available_features.add('%s-%s' % (cxx_type, maj_v)) self.config.available_features.add('%s-%s.%s' % ( cxx_type, maj_v, min_v)) + self.cxx.compile_env = dict(os.environ) + # 'CCACHE_CPP2' prevents ccache from stripping comments while + # preprocessing. This is required to prevent stripping of '-verify' + # comments. + self.cxx.compile_env['CCACHE_CPP2'] = '1' def _configure_clang_cl(self, clang_path): assert self.cxx_is_clang_cl @@ -555,7 +561,7 @@ class Configuration(object): if not os.path.isdir(dynamic_env): os.makedirs(dynamic_env) self.cxx.compile_flags += ['-DLIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT="%s"' % dynamic_env] - self.env['LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT'] = ("%s" % dynamic_env) + self.exec_env['LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT'] = ("%s" % dynamic_env) dynamic_helper = os.path.join(self.libcxx_src_root, 'test', 'support', 'filesystem_dynamic_test_helper.py') @@ -577,7 +583,7 @@ class Configuration(object): self.cxx.link_flags += ['-nodefaultlibs'] # FIXME: Handle MSVCRT as part of the ABI library handling. if self.is_windows: - self.cxx.link_flags += ['-nostdlib', '-lmsvcrtd'] + self.cxx.link_flags += ['-nostdlib'] self.configure_link_flags_cxx_library() self.configure_link_flags_abi_library() self.configure_extra_library_flags() @@ -602,8 +608,14 @@ class Configuration(object): if not self.use_system_cxx_lib: if self.cxx_library_root: self.cxx.link_flags += ['-L' + self.cxx_library_root] - if self.cxx_runtime_root and not self.is_windows: - self.cxx.link_flags += ['-Wl,-rpath,' + self.cxx_runtime_root] + if self.is_windows and self.link_shared: + self.add_path(self.cxx.compile_env, self.cxx_library_root) + if self.cxx_runtime_root: + if not self.is_windows: + self.cxx.link_flags += ['-Wl,-rpath,' + + self.cxx_runtime_root] + elif self.is_windows and self.link_shared: + self.add_path(self.exec_env, self.cxx_runtime_root) def configure_link_flags_abi_library_path(self): # Configure ABI library paths. @@ -612,14 +624,15 @@ class Configuration(object): self.cxx.link_flags += ['-L' + self.abi_library_root] if not self.is_windows: self.cxx.link_flags += ['-Wl,-rpath,' + self.abi_library_root] + else: + self.add_path(self.exec_env, self.abi_library_root) def configure_link_flags_cxx_library(self): libcxx_experimental = self.get_lit_bool('enable_experimental', default=False) if libcxx_experimental: self.config.available_features.add('c++experimental') self.cxx.link_flags += ['-lc++experimental'] - libcxx_shared = self.get_lit_bool('enable_shared', default=True) - if libcxx_shared: + if self.link_shared: self.cxx.link_flags += ['-lc++'] else: cxx_library_root = self.get_lit_conf('cxx_library_root') @@ -654,7 +667,8 @@ class Configuration(object): elif cxx_abi == 'libcxxrt': self.cxx.link_flags += ['-lcxxrt'] elif cxx_abi == 'none' or cxx_abi == 'default': - pass + if self.is_windows: + self.cxx.link_flags += ['-lmsvcrtd'] else: self.lit_config.fatal( 'C++ ABI setting %s unsupported for tests' % cxx_abi) @@ -746,7 +760,7 @@ class Configuration(object): self.cxx.flags += ['-fsanitize=undefined', '-fno-sanitize=vptr,function,float-divide-by-zero', '-fno-sanitize-recover=all'] - self.env['UBSAN_OPTIONS'] = 'print_stacktrace=1' + self.exec_env['UBSAN_OPTIONS'] = 'print_stacktrace=1' self.config.available_features.add('ubsan') # Setup the sanitizer compile flags @@ -754,10 +768,10 @@ class Configuration(object): if san == 'Address' or san == 'Address;Undefined' or san == 'Undefined;Address': self.cxx.flags += ['-fsanitize=address'] if llvm_symbolizer is not None: - self.env['ASAN_SYMBOLIZER_PATH'] = llvm_symbolizer + self.exec_env['ASAN_SYMBOLIZER_PATH'] = llvm_symbolizer # FIXME: Turn ODR violation back on after PR28391 is resolved # https://llvm.org/bugs/show_bug.cgi?id=28391 - self.env['ASAN_OPTIONS'] = 'detect_odr_violation=0' + self.exec_env['ASAN_OPTIONS'] = 'detect_odr_violation=0' self.config.available_features.add('asan') self.config.available_features.add('sanitizer-new-delete') self.cxx.compile_flags += ['-O1'] @@ -769,7 +783,7 @@ class Configuration(object): self.cxx.compile_flags += [ '-fsanitize-memory-track-origins'] if llvm_symbolizer is not None: - self.env['MSAN_SYMBOLIZER_PATH'] = llvm_symbolizer + self.exec_env['MSAN_SYMBOLIZER_PATH'] = llvm_symbolizer self.config.available_features.add('msan') self.config.available_features.add('sanitizer-new-delete') self.cxx.compile_flags += ['-O1'] @@ -855,8 +869,8 @@ class Configuration(object): sub.append(('%link', link_str)) sub.append(('%build', build_str)) # Configure exec prefix substitutions. - exec_env_str = 'env ' if len(self.env) != 0 else '' - for k, v in self.env.items(): + exec_env_str = 'env ' if len(self.exec_env) != 0 else '' + for k, v in self.exec_env.items(): exec_env_str += ' %s=%s' % (k, v) # Configure run env substitution. exec_str = exec_env_str @@ -898,4 +912,12 @@ class Configuration(object): "inferred target_triple as: %r" % self.config.target_triple) def configure_env(self): - self.target_info.configure_env(self.env) + self.target_info.configure_env(self.exec_env) + + def add_path(self, dest_env, new_path): + if 'PATH' not in dest_env: + dest_env['PATH'] = new_path + else: + split_char = ';' if self.is_windows else ':' + dest_env['PATH'] = '%s%s%s' % (new_path, split_char, + dest_env['PATH']) diff --git a/libcxx/test/libcxx/test/executor.py b/libcxx/test/libcxx/test/executor.py index ee4288fb0083..250186cecb84 100644 --- a/libcxx/test/libcxx/test/executor.py +++ b/libcxx/test/libcxx/test/executor.py @@ -7,6 +7,7 @@ # #===----------------------------------------------------------------------===## +import platform import os from libcxx.test import tracing @@ -34,6 +35,7 @@ class Executor(object): class LocalExecutor(Executor): def __init__(self): super(LocalExecutor, self).__init__() + self.is_windows = platform.system() == 'Windows' def run(self, exe_path, cmd=None, work_dir='.', file_deps=None, env=None): cmd = cmd or [exe_path] @@ -43,9 +45,30 @@ class LocalExecutor(Executor): env_cmd += ['%s=%s' % (k, v) for k, v in env.items()] if work_dir == '.': work_dir = os.getcwd() - out, err, rc = executeCommand(env_cmd + cmd, cwd=work_dir) + if not self.is_windows: + out, err, rc = executeCommand(env_cmd + cmd, cwd=work_dir) + else: + out, err, rc = executeCommand(cmd, cwd=work_dir, + env=self._build_windows_env(env)) return (env_cmd + cmd, out, err, rc) + def _build_windows_env(self, exec_env): + # FIXME: Finding Windows DLL's at runtime requires modifying the + # PATH environment variables. However we don't want to print out + # the entire PATH as part of the diagnostic for every failing test. + # Therefore this hack builds a new executable environment that + # merges the current environment and the supplied environment while + # still only printing the supplied environment in diagnostics. + if not self.is_windows or exec_env is None: + return None + new_env = dict(os.environ) + for key, value in exec_env.items(): + if key == 'PATH': + assert value.strip() != '' and "expected non-empty path" + new_env['PATH'] = "%s;%s" % (value, os.environ['PATH']) + else: + new_env[key] = value + return new_env class PrefixExecutor(Executor): """Prefix an executor with some other command wrapper. diff --git a/libcxx/test/libcxx/test/format.py b/libcxx/test/libcxx/test/format.py index ee6ab82c6e3a..cbd96f340bbc 100644 --- a/libcxx/test/libcxx/test/format.py +++ b/libcxx/test/libcxx/test/format.py @@ -41,11 +41,6 @@ class LibcxxTestFormat(object): self.execute_external = execute_external self.executor = executor self.exec_env = dict(exec_env) - self.cxx.compile_env = dict(os.environ) - # 'CCACHE_CPP2' prevents ccache from stripping comments while - # preprocessing. This is required to prevent stripping of '-verify' - # comments. - self.cxx.compile_env['CCACHE_CPP2'] = '1' @staticmethod def _make_custom_parsers():