|
|
@@ -8,6 +8,7 @@
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
|
#include <filesystem>
|
|
|
+#include <memory>
|
|
|
#include <string>
|
|
|
#include <utility>
|
|
|
|
|
|
@@ -29,10 +30,12 @@
|
|
|
#include "toolchain/driver/clang_runner.h"
|
|
|
#include "toolchain/driver/llvm_runner.h"
|
|
|
#include "toolchain/driver/runtimes_cache.h"
|
|
|
+#include "tools/cpp/runfiles/runfiles.h"
|
|
|
|
|
|
namespace Carbon {
|
|
|
namespace {
|
|
|
|
|
|
+using ::bazel::tools::cpp::runfiles::Runfiles;
|
|
|
using ::testing::Each;
|
|
|
using ::testing::Eq;
|
|
|
using ::testing::HasSubstr;
|
|
|
@@ -70,6 +73,12 @@ MATCHER(IsBasename, "") {
|
|
|
|
|
|
class ClangRuntimesTest : public ::testing::Test {
|
|
|
public:
|
|
|
+ ClangRuntimesTest() {
|
|
|
+ std::string error;
|
|
|
+ test_runfiles_.reset(Runfiles::Create(exe_path_, &error));
|
|
|
+ CARBON_CHECK(test_runfiles_ != nullptr, "{0}", error);
|
|
|
+ }
|
|
|
+
|
|
|
// Helper to get the `llvm-nm` listing of defined symbols for an archive.
|
|
|
//
|
|
|
// TODO: It would be nice to use a library API and matchers instead of
|
|
|
@@ -84,7 +93,7 @@ class ClangRuntimesTest : public ::testing::Test {
|
|
|
LLVMTool::Nm, {"--format=just-symbols", "--defined-only", "--quiet",
|
|
|
archive.native()});
|
|
|
});
|
|
|
- CARBON_CHECK(result, "Unable to run `llvm-nm`:\n{1}", err);
|
|
|
+ CARBON_CHECK(result, "Unable to run `llvm-nm`:\n{0}", err);
|
|
|
|
|
|
return out;
|
|
|
}
|
|
|
@@ -122,8 +131,110 @@ class ClangRuntimesTest : public ::testing::Test {
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- InstallPaths install_paths_ =
|
|
|
- InstallPaths::MakeForBazelRunfiles(Testing::GetExePath());
|
|
|
+ auto TestResourceDir(std::filesystem::path resource_dir_path) -> void {
|
|
|
+ // For Linux we can directly check the CRT begin/end object files.
|
|
|
+ if (target_triple_.isOSLinux()) {
|
|
|
+ std::filesystem::path crt_begin_path =
|
|
|
+ resource_dir_path / "lib" / target_ / "clang_rt.crtbegin.o";
|
|
|
+ ASSERT_TRUE(std::filesystem::is_regular_file(crt_begin_path));
|
|
|
+ auto begin_result =
|
|
|
+ llvm::object::ObjectFile::createObjectFile(crt_begin_path.native());
|
|
|
+ llvm::object::ObjectFile& crtbegin = *begin_result->getBinary();
|
|
|
+ EXPECT_TRUE(crtbegin.isELF());
|
|
|
+ EXPECT_TRUE(crtbegin.isObject());
|
|
|
+ EXPECT_THAT(crtbegin.getArch(), Eq(target_triple_.getArch()));
|
|
|
+
|
|
|
+ llvm::SmallVector<llvm::object::SymbolRef> symbols(crtbegin.symbols());
|
|
|
+ // The first symbol should come from the source file.
|
|
|
+ EXPECT_THAT(*symbols.front().getName(), Eq("crtbegin.c"));
|
|
|
+
|
|
|
+ // Check for representative symbols of `crtbegin.o` -- we always use
|
|
|
+ // `.init_array` in our runtimes build so we have predictable functions.
|
|
|
+ EXPECT_THAT(symbols, IsSupersetOf({TextSymbolNamed("__do_init"),
|
|
|
+ TextSymbolNamed("__do_fini")}));
|
|
|
+
|
|
|
+ std::filesystem::path crt_end_path =
|
|
|
+ resource_dir_path / "lib" / target_ / "clang_rt.crtend.o";
|
|
|
+ ASSERT_TRUE(std::filesystem::is_regular_file(crt_end_path));
|
|
|
+ auto end_result =
|
|
|
+ llvm::object::ObjectFile::createObjectFile(crt_end_path.native());
|
|
|
+ llvm::object::ObjectFile& crtend = *end_result->getBinary();
|
|
|
+ EXPECT_TRUE(crtend.isELF());
|
|
|
+ EXPECT_TRUE(crtend.isObject());
|
|
|
+ EXPECT_THAT(crtend.getArch(), Eq(target_triple_.getArch()));
|
|
|
+
|
|
|
+ // Just check the source file symbol, not much of interest in the end.
|
|
|
+ llvm::object::SymbolRef crtend_front_symbol = *crtend.symbol_begin();
|
|
|
+ EXPECT_THAT(*crtend_front_symbol.getName(), Eq("crtend.c"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Across all targets, check that the builtins archive exists, and contains
|
|
|
+ // a relevant symbol by running the `llvm-nm` tool over it. Using `nm`
|
|
|
+ // rather than directly inspecting the objects is a bit awkward, but lets us
|
|
|
+ // easily ignore the wrapping in an archive file.
|
|
|
+ std::filesystem::path builtins_path =
|
|
|
+ resource_dir_path / "lib" / target_ / "libclang_rt.builtins.a";
|
|
|
+ std::string builtins_symbols = NmListDefinedSymbols(builtins_path);
|
|
|
+
|
|
|
+ // Check that we found a definition of `__mulodi4`, a builtin function
|
|
|
+ // provided by Compiler-RT.
|
|
|
+ ExpectSymbol(builtins_symbols, "__mulodi4");
|
|
|
+
|
|
|
+ // Check that we don't include the `chkstk` builtins outside of Windows.
|
|
|
+ if (!target_triple_.isOSWindows()) {
|
|
|
+ EXPECT_THAT(builtins_symbols, Not(HasSubstr("chkstk")));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check that member names don't contain full paths, as that is the
|
|
|
+ // canonical format produced by `ar`.
|
|
|
+ auto member_names = ListArchiveMemberNames(builtins_path);
|
|
|
+ EXPECT_THAT(member_names, Each(IsBasename()));
|
|
|
+ }
|
|
|
+
|
|
|
+ auto TestLibunwind(std::filesystem::path libunwind_path) -> void {
|
|
|
+ std::string libunwind_symbols = NmListDefinedSymbols(libunwind_path);
|
|
|
+
|
|
|
+ // Check a few of the main exported symbols here. The set here is somewhat
|
|
|
+ // arbitrary, but chosen to be among the more stable names and have at least
|
|
|
+ // one from most of the object files that should be linked into the archive.
|
|
|
+ ExpectSymbol(libunwind_symbols, "_Unwind_Resume");
|
|
|
+ ExpectSymbol(libunwind_symbols, "_Unwind_Backtrace");
|
|
|
+ ExpectSymbol(libunwind_symbols, "__unw_getcontext");
|
|
|
+ ExpectSymbol(libunwind_symbols, "__unw_get_proc_info");
|
|
|
+
|
|
|
+ // Check that member names don't contain full paths, as that is the
|
|
|
+ // canonical format produced by `ar`.
|
|
|
+ auto member_names = ListArchiveMemberNames(libunwind_path);
|
|
|
+ EXPECT_THAT(member_names, Each(IsBasename()));
|
|
|
+ }
|
|
|
+
|
|
|
+ auto TestLibcxx(std::filesystem::path libcxx_path) -> void {
|
|
|
+ std::string libcxx_symbols = NmListDefinedSymbols(libcxx_path);
|
|
|
+
|
|
|
+ // First check a few fundamental symbols from libc++.a, including symbols
|
|
|
+ // both within the ABI namespace and outside of it.
|
|
|
+ ExpectSymbol(libcxx_symbols, "_ZNKSt12bad_any_cast4whatEv");
|
|
|
+ ExpectSymbol(libcxx_symbols, "_ZNSt2_C8to_charsEPcS0_d");
|
|
|
+ ExpectSymbol(libcxx_symbols, "_ZSt17current_exceptionv");
|
|
|
+ ExpectSymbol(libcxx_symbols, "_ZNKSt2_C10filesystem4path10__filenameEv");
|
|
|
+
|
|
|
+ // Check that several of the libc++abi object files are also included in the
|
|
|
+ // archive.
|
|
|
+ ExpectSymbol(libcxx_symbols, "__cxa_bad_cast");
|
|
|
+ ExpectSymbol(libcxx_symbols, "__cxa_new_handler");
|
|
|
+ ExpectSymbol(libcxx_symbols, "__cxa_demangle");
|
|
|
+ ExpectSymbol(libcxx_symbols, "__cxa_get_globals");
|
|
|
+ ExpectSymbol(libcxx_symbols, "_ZSt9terminatev");
|
|
|
+
|
|
|
+ // Check that member names don't contain full paths, as that is the
|
|
|
+ // canonical format produced by `ar`.
|
|
|
+ auto member_names = ListArchiveMemberNames(libcxx_path);
|
|
|
+ EXPECT_THAT(member_names, Each(IsBasename()));
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string exe_path_ = Testing::GetExePath().str();
|
|
|
+ std::unique_ptr<Runfiles> test_runfiles_;
|
|
|
+ InstallPaths install_paths_ = InstallPaths::MakeForBazelRunfiles(exe_path_);
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs_ =
|
|
|
llvm::vfs::getRealFileSystem();
|
|
|
// Note that for debugging, you can pass `llvm::errs()` as the vlog stream,
|
|
|
@@ -152,65 +263,7 @@ TEST_F(ClangRuntimesTest, ResourceDir) {
|
|
|
target_triple_, &runtimes_);
|
|
|
auto build_result = std::move(resource_dir_builder).Wait();
|
|
|
ASSERT_TRUE(build_result.ok()) << build_result.error();
|
|
|
- std::filesystem::path resource_dir_path = std::move(*build_result);
|
|
|
-
|
|
|
- // For Linux we can directly check the CRT begin/end object files.
|
|
|
- if (target_triple_.isOSLinux()) {
|
|
|
- std::filesystem::path crt_begin_path =
|
|
|
- resource_dir_path / "lib" / target_ / "clang_rt.crtbegin.o";
|
|
|
- ASSERT_TRUE(std::filesystem::is_regular_file(crt_begin_path));
|
|
|
- auto begin_result =
|
|
|
- llvm::object::ObjectFile::createObjectFile(crt_begin_path.native());
|
|
|
- llvm::object::ObjectFile& crtbegin = *begin_result->getBinary();
|
|
|
- EXPECT_TRUE(crtbegin.isELF());
|
|
|
- EXPECT_TRUE(crtbegin.isObject());
|
|
|
- EXPECT_THAT(crtbegin.getArch(), Eq(target_triple_.getArch()));
|
|
|
-
|
|
|
- llvm::SmallVector<llvm::object::SymbolRef> symbols(crtbegin.symbols());
|
|
|
- // The first symbol should come from the source file.
|
|
|
- EXPECT_THAT(*symbols.front().getName(), Eq("crtbegin.c"));
|
|
|
-
|
|
|
- // Check for representative symbols of `crtbegin.o` -- we always use
|
|
|
- // `.init_array` in our runtimes build so we have predictable functions.
|
|
|
- EXPECT_THAT(symbols, IsSupersetOf({TextSymbolNamed("__do_init"),
|
|
|
- TextSymbolNamed("__do_fini")}));
|
|
|
-
|
|
|
- std::filesystem::path crt_end_path =
|
|
|
- resource_dir_path / "lib" / target_ / "clang_rt.crtend.o";
|
|
|
- ASSERT_TRUE(std::filesystem::is_regular_file(crt_end_path));
|
|
|
- auto end_result =
|
|
|
- llvm::object::ObjectFile::createObjectFile(crt_end_path.native());
|
|
|
- llvm::object::ObjectFile& crtend = *end_result->getBinary();
|
|
|
- EXPECT_TRUE(crtend.isELF());
|
|
|
- EXPECT_TRUE(crtend.isObject());
|
|
|
- EXPECT_THAT(crtend.getArch(), Eq(target_triple_.getArch()));
|
|
|
-
|
|
|
- // Just check the source file symbol, not much of interest in the end.
|
|
|
- llvm::object::SymbolRef crtend_front_symbol = *crtend.symbol_begin();
|
|
|
- EXPECT_THAT(*crtend_front_symbol.getName(), Eq("crtend.c"));
|
|
|
- }
|
|
|
-
|
|
|
- // Across all targets, check that the builtins archive exists, and contains a
|
|
|
- // relevant symbol by running the `llvm-nm` tool over it. Using `nm` rather
|
|
|
- // than directly inspecting the objects is a bit awkward, but lets us easily
|
|
|
- // ignore the wrapping in an archive file.
|
|
|
- std::filesystem::path builtins_path =
|
|
|
- resource_dir_path / "lib" / target_ / "libclang_rt.builtins.a";
|
|
|
- std::string builtins_symbols = NmListDefinedSymbols(builtins_path);
|
|
|
-
|
|
|
- // Check that we found a definition of `__mulodi4`, a builtin function
|
|
|
- // provided by Compiler-RT.
|
|
|
- ExpectSymbol(builtins_symbols, "__mulodi4");
|
|
|
-
|
|
|
- // Check that we don't include the `chkstk` builtins outside of Windows.
|
|
|
- if (!target_triple_.isOSWindows()) {
|
|
|
- EXPECT_THAT(builtins_symbols, Not(HasSubstr("chkstk")));
|
|
|
- }
|
|
|
-
|
|
|
- // Check that member names don't contain full paths, as that is the
|
|
|
- // canonical format produced by `ar`.
|
|
|
- auto member_names = ListArchiveMemberNames(builtins_path);
|
|
|
- EXPECT_THAT(member_names, Each(IsBasename()));
|
|
|
+ TestResourceDir(std::move(*build_result));
|
|
|
}
|
|
|
|
|
|
TEST_F(ClangRuntimesTest, Libunwind) {
|
|
|
@@ -219,63 +272,40 @@ TEST_F(ClangRuntimesTest, Libunwind) {
|
|
|
auto build_result = std::move(libunwind_builder).Wait();
|
|
|
ASSERT_TRUE(build_result.ok()) << build_result.error();
|
|
|
std::filesystem::path runtimes_path = std::move(*build_result);
|
|
|
-
|
|
|
- std::filesystem::path libunwind_path = runtimes_path / "lib/libunwind.a";
|
|
|
- std::string libunwind_symbols = NmListDefinedSymbols(libunwind_path);
|
|
|
-
|
|
|
- // Check a few of the main exported symbols here. The set here is somewhat
|
|
|
- // arbitrary, but chosen to be among the more stable names and have at least
|
|
|
- // one from most of the object files that should be linked into the archive.
|
|
|
- ExpectSymbol(libunwind_symbols, "_Unwind_Resume");
|
|
|
- ExpectSymbol(libunwind_symbols, "_Unwind_Backtrace");
|
|
|
- ExpectSymbol(libunwind_symbols, "__unw_getcontext");
|
|
|
- ExpectSymbol(libunwind_symbols, "__unw_get_proc_info");
|
|
|
-
|
|
|
- // Check that member names don't contain full paths, as that is the
|
|
|
- // canonical format produced by `ar`.
|
|
|
- auto member_names = ListArchiveMemberNames(libunwind_path);
|
|
|
- EXPECT_THAT(member_names, Each(IsBasename()));
|
|
|
+ TestLibunwind(runtimes_path / "lib/libunwind.a");
|
|
|
}
|
|
|
|
|
|
-TEST_F(ClangRuntimesTest, Libcxx) {
|
|
|
-#if __has_feature(address_sanitizer)
|
|
|
- // ASan causes Clang and LLVM to be _egregiously_ inefficient at compiling
|
|
|
- // libc++, taking 5x - 10x longer than without ASan. Rough estimate is that it
|
|
|
- // would take 5-10 minutes on GitHub's Linux runner. Given the limited utility
|
|
|
- // of this test coverage, skip it in that configuration. This also misses
|
|
|
- // assert-coverage for building libc++, but we don't really expect issues
|
|
|
- // there. Misconfiguration and other common issues should still be covered in
|
|
|
- // fully optimized builds at much lower cost.
|
|
|
- GTEST_SKIP() << "Skipping build of libc++ with an ASan-itized Clang";
|
|
|
-#endif
|
|
|
-
|
|
|
+// ASan causes Clang and LLVM to be _egregiously_ inefficient at compiling
|
|
|
+// libc++, taking 5x - 10x longer than without ASan. Rough estimate is that it
|
|
|
+// would take 5-10 minutes on GitHub's Linux runner.
|
|
|
+//
|
|
|
+// We test libc++ in the prebuilt runtimes below in a more cache friendly and
|
|
|
+// sustainable way. Given that, we disable this test by default but include it
|
|
|
+// for debugging purposes.
|
|
|
+TEST_F(ClangRuntimesTest, DISABLED_Libcxx) {
|
|
|
LibcxxBuilder libcxx_builder(&runner_, &threads_, target_triple_, &runtimes_);
|
|
|
auto build_result = std::move(libcxx_builder).Wait();
|
|
|
ASSERT_TRUE(build_result.ok()) << build_result.error();
|
|
|
std::filesystem::path runtimes_path = std::move(*build_result);
|
|
|
+ TestLibcxx(runtimes_path / "lib/libc++.a");
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(ClangRuntimesTest, PrebuiltResourceDir) {
|
|
|
+ std::filesystem::path prebuilt_runtimes_path = test_runfiles_->Rlocation(
|
|
|
+ "carbon/toolchain/driver/prebuilt_runtimes_tree");
|
|
|
+ TestResourceDir(prebuilt_runtimes_path / "clang_resource_dir");
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(ClangRuntimesTest, PrebuiltLibunwind) {
|
|
|
+ std::filesystem::path prebuilt_runtimes_path = test_runfiles_->Rlocation(
|
|
|
+ "carbon/toolchain/driver/prebuilt_runtimes_tree");
|
|
|
+ TestLibunwind(prebuilt_runtimes_path / "libunwind/lib/libunwind.a");
|
|
|
+}
|
|
|
|
|
|
- std::filesystem::path libcxx_path = runtimes_path / "lib/libc++.a";
|
|
|
- std::string libcxx_symbols = NmListDefinedSymbols(libcxx_path);
|
|
|
-
|
|
|
- // First check a few fundamental symbols from libc++.a, including symbols both
|
|
|
- // within the ABI namespace and outside of it.
|
|
|
- ExpectSymbol(libcxx_symbols, "_ZNKSt12bad_any_cast4whatEv");
|
|
|
- ExpectSymbol(libcxx_symbols, "_ZNSt2_C8to_charsEPcS0_d");
|
|
|
- ExpectSymbol(libcxx_symbols, "_ZSt17current_exceptionv");
|
|
|
- ExpectSymbol(libcxx_symbols, "_ZNKSt2_C10filesystem4path10__filenameEv");
|
|
|
-
|
|
|
- // Check that several of the libc++abi object files are also included in the
|
|
|
- // archive.
|
|
|
- ExpectSymbol(libcxx_symbols, "__cxa_bad_cast");
|
|
|
- ExpectSymbol(libcxx_symbols, "__cxa_new_handler");
|
|
|
- ExpectSymbol(libcxx_symbols, "__cxa_demangle");
|
|
|
- ExpectSymbol(libcxx_symbols, "__cxa_get_globals");
|
|
|
- ExpectSymbol(libcxx_symbols, "_ZSt9terminatev");
|
|
|
-
|
|
|
- // Check that member names don't contain full paths, as that is the
|
|
|
- // canonical format produced by `ar`.
|
|
|
- auto member_names = ListArchiveMemberNames(libcxx_path);
|
|
|
- EXPECT_THAT(member_names, Each(IsBasename()));
|
|
|
+TEST_F(ClangRuntimesTest, PrebuiltLibcxx) {
|
|
|
+ std::filesystem::path prebuilt_runtimes_path = test_runfiles_->Rlocation(
|
|
|
+ "carbon/toolchain/driver/prebuilt_runtimes_tree");
|
|
|
+ TestLibcxx(prebuilt_runtimes_path / "libcxx/lib/libc++.a");
|
|
|
}
|
|
|
|
|
|
} // namespace
|