1111#include < string>
1212#include < utility>
1313
14+ #include " common/check.h"
1415#include " common/ostream.h"
1516#include " llvm/ADT/IntrusiveRefCntPtr.h"
1617#include " llvm/ADT/SmallVector.h"
@@ -62,43 +63,79 @@ MATCHER_P(TextSymbolNamed, name_matcher, "") {
6263
6364class ClangRuntimesTest : public ::testing::Test {
6465 public:
66+ // Helper to get the `llvm-nm` listing of defined symbols for an archive.
67+ //
68+ // TODO: It would be nice to use a library API and matchers instead of
69+ // `llvm-nm` and matching text on the output.
70+ auto NmListDefinedSymbols (const std::filesystem::path& archive)
71+ -> std::string {
72+ LLVMRunner llvm_runner (&install_paths_, &llvm::errs ());
73+ std::string out;
74+ std::string err;
75+ bool result = Testing::CallWithCapturedOutput (out, err, [&] {
76+ return llvm_runner.Run (
77+ LLVMTool::Nm, {" --format=just-symbols" , " --defined-only" , " --quiet" ,
78+ archive.native ()});
79+ });
80+ CARBON_CHECK (result, " Unable to run `llvm-nm`:\n {1}" , err);
81+
82+ return out;
83+ }
84+
85+ // Helper to expect a specific symbol in the `llvm-nm` list.
86+ //
87+ // This handles platform-specific formatting of symbols.
88+ auto ExpectSymbol (llvm::StringRef nm_list, llvm::StringRef symbol) -> void {
89+ std::string symbol_substr = llvm::formatv (
90+ target_triple_.isMacOSX () ? " \n _{0}\n " : " \n {0}\n " , symbol);
91+
92+ // Do the actual match with `HasSubstr` so it can explain failures.
93+ EXPECT_THAT (nm_list, HasSubstr (symbol_substr));
94+ }
95+
6596 InstallPaths install_paths_ =
6697 InstallPaths::MakeForBazelRunfiles (Testing::GetExePath());
67- Runtimes::Cache runtimes_cache_ =
68- *Runtimes::Cache::MakeSystem (install_paths_);
6998 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs_ =
7099 llvm::vfs::getRealFileSystem ();
71- };
72-
73- TEST_F (ClangRuntimesTest, ResourceDir) {
74- ClangRunner runner ( &install_paths_, vfs_, & llvm::errs ()) ;
100+ // Note that for debugging, you can pass `llvm::errs()` as the vlog stream,
101+ // but this makes the output both very verbose and hard to use with multiple
102+ // threads.
103+ ClangRunner runner_{ &install_paths_, vfs_} ;
75104
76105 // Note that we can't test arbitrary targets here as we need to be able to
77106 // compile the builtin functions for the target. We use the default target as
78107 // the most likely to pass.
79- std::string target = llvm::sys::getDefaultTargetTriple ();
80- llvm::Triple target_triple (target);
81- Runtimes::Cache::Features features = {.target = target};
82- auto runtimes = *runtimes_cache_.Lookup (features);
83- llvm::DefaultThreadPool threads (llvm::optimal_concurrency ());
84-
85- ClangResourceDirBuilder resource_dir_builder (&runner, &threads, target_triple,
86- &runtimes);
108+ std::string target_ = llvm::sys::getDefaultTargetTriple();
109+ llvm::Triple target_triple_{target_};
110+
111+ Runtimes::Cache runtimes_cache_ =
112+ *Runtimes::Cache::MakeSystem (install_paths_);
113+ Runtimes::Cache::Features features = {.target = target_};
114+ Runtimes runtimes_ = *runtimes_cache_.Lookup(features);
115+
116+ // Note that for debugging it may be useful to replace this with a
117+ // single-threaded thread pool. However the test will be _much_ slower.
118+ llvm::DefaultThreadPool threads_{llvm::optimal_concurrency ()};
119+ };
120+
121+ TEST_F (ClangRuntimesTest, ResourceDir) {
122+ ClangResourceDirBuilder resource_dir_builder (&runner_, &threads_,
123+ target_triple_, &runtimes_);
87124 auto build_result = std::move (resource_dir_builder).Wait ();
88125 ASSERT_TRUE (build_result.ok ()) << build_result.error ();
89126 std::filesystem::path resource_dir_path = std::move (*build_result);
90127
91128 // For Linux we can directly check the CRT begin/end object files.
92- if (target_triple .isOSLinux ()) {
129+ if (target_triple_ .isOSLinux ()) {
93130 std::filesystem::path crt_begin_path =
94- resource_dir_path / " lib" / target / " clang_rt.crtbegin.o" ;
131+ resource_dir_path / " lib" / target_ / " clang_rt.crtbegin.o" ;
95132 ASSERT_TRUE (std::filesystem::is_regular_file (crt_begin_path));
96133 auto begin_result =
97134 llvm::object::ObjectFile::createObjectFile (crt_begin_path.native ());
98135 llvm::object::ObjectFile& crtbegin = *begin_result->getBinary ();
99136 EXPECT_TRUE (crtbegin.isELF ());
100137 EXPECT_TRUE (crtbegin.isObject ());
101- EXPECT_THAT (crtbegin.getArch (), Eq (target_triple .getArch ()));
138+ EXPECT_THAT (crtbegin.getArch (), Eq (target_triple_ .getArch ()));
102139
103140 llvm::SmallVector<llvm::object::SymbolRef> symbols (crtbegin.symbols ());
104141 // The first symbol should come from the source file.
@@ -110,14 +147,14 @@ TEST_F(ClangRuntimesTest, ResourceDir) {
110147 TextSymbolNamed (" __do_fini" )}));
111148
112149 std::filesystem::path crt_end_path =
113- resource_dir_path / " lib" / target / " clang_rt.crtend.o" ;
150+ resource_dir_path / " lib" / target_ / " clang_rt.crtend.o" ;
114151 ASSERT_TRUE (std::filesystem::is_regular_file (crt_end_path));
115152 auto end_result =
116153 llvm::object::ObjectFile::createObjectFile (crt_end_path.native ());
117154 llvm::object::ObjectFile& crtend = *end_result->getBinary ();
118155 EXPECT_TRUE (crtend.isELF ());
119156 EXPECT_TRUE (crtend.isObject ());
120- EXPECT_THAT (crtend.getArch (), Eq (target_triple .getArch ()));
157+ EXPECT_THAT (crtend.getArch (), Eq (target_triple_ .getArch ()));
121158
122159 // Just check the source file symbol, not much of interest in the end.
123160 llvm::object::SymbolRef crtend_front_symbol = *crtend.symbol_begin ();
@@ -129,25 +166,37 @@ TEST_F(ClangRuntimesTest, ResourceDir) {
129166 // than directly inspecting the objects is a bit awkward, but lets us easily
130167 // ignore the wrapping in an archive file.
131168 std::filesystem::path builtins_path =
132- resource_dir_path / " lib" / target / " libclang_rt.builtins.a" ;
133- LLVMRunner llvm_runner (&install_paths_, &llvm::errs ());
134- std::string out;
135- std::string err;
136- EXPECT_TRUE (Testing::CallWithCapturedOutput (out, err, [&] {
137- return llvm_runner.Run (LLVMTool::Nm, {builtins_path.native ()});
138- }));
169+ resource_dir_path / " lib" / target_ / " libclang_rt.builtins.a" ;
170+ std::string builtins_symbols = NmListDefinedSymbols (builtins_path);
139171
140172 // Check that we found a definition of `__mulodi4`, a builtin function
141- // provided by Compiler-RT, but not `libgcc` historically. Note that on macOS
142- // there is a leading `_` due to mangling.
143- EXPECT_THAT (out, HasSubstr (target_triple.isMacOSX () ? " T ___mulodi4\n "
144- : " T __mulodi4\n " ));
173+ // provided by Compiler-RT.
174+ ExpectSymbol (builtins_symbols, " __mulodi4" );
145175
146176 // Check that we don't include the `chkstk` builtins outside of Windows.
147- if (!target_triple .isOSWindows ()) {
148- EXPECT_THAT (out , Not (HasSubstr (" chkstk" )));
177+ if (!target_triple_ .isOSWindows ()) {
178+ EXPECT_THAT (builtins_symbols , Not (HasSubstr (" chkstk" )));
149179 }
150180}
151181
182+ TEST_F (ClangRuntimesTest, Libunwind) {
183+ LibunwindBuilder libunwind_builder (&runner_, &threads_, target_triple_,
184+ &runtimes_);
185+ auto build_result = std::move (libunwind_builder).Wait ();
186+ ASSERT_TRUE (build_result.ok ()) << build_result.error ();
187+ std::filesystem::path runtimes_path = std::move (*build_result);
188+
189+ std::filesystem::path libunwind_path = runtimes_path / " lib/libunwind.a" ;
190+ std::string libunwind_symbols = NmListDefinedSymbols (libunwind_path);
191+
192+ // Check a few of the main exported symbols here. The set here is somewhat
193+ // arbitrary, but chosen to be among the more stable names and have at least
194+ // one from most of the object files that should be linked into the archive.
195+ ExpectSymbol (libunwind_symbols, " _Unwind_Resume" );
196+ ExpectSymbol (libunwind_symbols, " _Unwind_Backtrace" );
197+ ExpectSymbol (libunwind_symbols, " __unw_getcontext" );
198+ ExpectSymbol (libunwind_symbols, " __unw_get_proc_info" );
199+ }
200+
152201} // namespace
153202} // namespace Carbon
0 commit comments