aboutsummaryrefslogtreecommitdiff
path: root/llvm/unittests/ExecutionEngine/Orc/LibraryResolverTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/unittests/ExecutionEngine/Orc/LibraryResolverTest.cpp')
-rw-r--r--llvm/unittests/ExecutionEngine/Orc/LibraryResolverTest.cpp896
1 files changed, 0 insertions, 896 deletions
diff --git a/llvm/unittests/ExecutionEngine/Orc/LibraryResolverTest.cpp b/llvm/unittests/ExecutionEngine/Orc/LibraryResolverTest.cpp
deleted file mode 100644
index f6990ee..0000000
--- a/llvm/unittests/ExecutionEngine/Orc/LibraryResolverTest.cpp
+++ /dev/null
@@ -1,896 +0,0 @@
-//===- LibraryResolverTest.cpp - Unit tests for LibraryResolver -===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h"
-#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
-#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h"
-#include "llvm/ObjectYAML/MachOYAML.h"
-#include "llvm/ObjectYAML/yaml2obj.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/Path.h"
-#include "llvm/Support/YAMLParser.h"
-#include "llvm/Support/YAMLTraits.h"
-#include "llvm/Support/raw_ostream.h"
-
-#include "llvm/Testing/Support/SupportHelpers.h"
-
-#include "gtest/gtest.h"
-
-#include <algorithm>
-#include <optional>
-#include <string>
-#include <vector>
-
-using namespace llvm;
-using namespace llvm::orc;
-
-#if defined(__APPLE__) || defined(__linux__)
-// TODO: Add COFF (Windows) support for these tests.
-// this facility also works correctly on Windows (COFF),
-// so we should eventually enable and run these tests for that platform as well.
-namespace {
-
-#if defined(__APPLE__)
-constexpr const char *ext = ".dylib";
-#elif defined(_WIN32)
-constexpr const char *ext = ".dll";
-#else
-constexpr const char *ext = ".so";
-#endif
-
-bool EnvReady = false;
-
-Triple getTargetTriple() {
- auto JTMB = JITTargetMachineBuilder::detectHost();
- if (!JTMB) {
- consumeError(JTMB.takeError());
- return Triple();
- }
- return JTMB->getTargetTriple();
-}
-
-static bool CheckHostSupport() {
- auto Triple = getTargetTriple();
- // TODO: Extend support to COFF (Windows) once test setup and YAML conversion
- // are verified.
- if (!Triple.isOSBinFormatMachO() &&
- !(Triple.isOSBinFormatELF() && Triple.getArch() == Triple::x86_64))
- return false;
-
- return true;
-}
-
-std::string getYamlFilePlatformExt() {
- auto Triple = getTargetTriple();
- if (Triple.isOSBinFormatMachO())
- return "_macho";
- else if (Triple.isOSBinFormatELF())
- return "_linux";
-
- return "";
-}
-
-unsigned getYamlDocNum() {
- // auto Triple = getTargetTriple();
- // if (Triple.isOSBinFormatELF())
- // return 1;
-
- return 1;
-}
-
-class LibraryTestEnvironment : public ::testing::Environment {
- std::vector<std::string> CreatedDylibsDir;
- std::vector<std::string> CreatedDylibs;
- SmallVector<char, 128> DirPath;
-
-public:
- void SetUp() override {
- if (!CheckHostSupport()) {
- EnvReady = false;
- return;
- }
-
- StringRef ThisFile = __FILE__;
- SmallVector<char, 128> InputDirPath(ThisFile.begin(), ThisFile.end());
- sys::path::remove_filename(InputDirPath);
- sys::path::append(InputDirPath, "Inputs");
- if (!sys::fs::exists(InputDirPath))
- return;
-
- SmallString<128> UniqueDir;
- sys::path::append(UniqueDir, InputDirPath);
- std::error_code EC = sys::fs::createUniqueDirectory(UniqueDir, DirPath);
-
- if (EC)
- return;
-
- // given yamlPath + DylibPath, validate + convert
- auto processYamlToDylib = [&](const SmallVector<char, 128> &YamlPath,
- const SmallVector<char, 128> &DylibPath,
- unsigned DocNum) -> bool {
- if (!sys::fs::exists(YamlPath)) {
- errs() << "YAML file missing: "
- << StringRef(YamlPath.data(), YamlPath.size()) << "\n";
- EnvReady = false;
- return false;
- }
-
- auto BufOrErr = MemoryBuffer::getFile(YamlPath);
- if (!BufOrErr) {
- errs() << "Failed to read "
- << StringRef(YamlPath.data(), YamlPath.size()) << ": "
- << BufOrErr.getError().message() << "\n";
- EnvReady = false;
- return false;
- }
-
- yaml::Input yin(BufOrErr->get()->getBuffer());
- std::error_code EC;
- raw_fd_ostream outFile(StringRef(DylibPath.data(), DylibPath.size()), EC,
- sys::fs::OF_None);
-
- if (EC) {
- errs() << "Failed to open "
- << StringRef(DylibPath.data(), DylibPath.size())
- << " for writing: " << EC.message() << "\n";
- EnvReady = false;
- return false;
- }
-
- if (!yaml::convertYAML(
- yin, outFile,
- [](const Twine &M) {
- // Handle or ignore errors here
- errs() << "Yaml Error :" << M << "\n";
- },
- DocNum)) {
- errs() << "Failed to convert "
- << StringRef(YamlPath.data(), YamlPath.size()) << " to "
- << StringRef(DylibPath.data(), DylibPath.size()) << "\n";
- EnvReady = false;
- return false;
- }
-
- CreatedDylibsDir.push_back(std::string(sys::path::parent_path(
- StringRef(DylibPath.data(), DylibPath.size()))));
- CreatedDylibs.push_back(std::string(DylibPath.begin(), DylibPath.end()));
- return true;
- };
-
- std::vector<const char *> LibDirs = {"Z", "A", "B", "C", "D"};
-
- unsigned DocNum = getYamlDocNum();
- std::string YamlPltExt = getYamlFilePlatformExt();
- for (const auto &LibdirName : LibDirs) {
- // YAML path
- SmallVector<char, 128> YamlPath(InputDirPath.begin(), InputDirPath.end());
- SmallVector<char, 128> YamlFileName;
- YamlFileName.append(LibdirName, LibdirName + strlen(LibdirName));
- YamlFileName.append(YamlPltExt.begin(), YamlPltExt.end());
- sys::path::append(YamlPath, LibdirName, YamlFileName);
- sys::path::replace_extension(YamlPath, ".yaml");
-
- // dylib path
- SmallVector<char, 128> DylibPath(DirPath.begin(), DirPath.end());
- SmallVector<char, 128> DylibFileName;
- StringRef prefix("lib");
- DylibFileName.append(prefix.begin(), prefix.end());
- DylibFileName.append(LibdirName, LibdirName + strlen(LibdirName));
-
- sys::path::append(DylibPath, LibdirName);
- if (!sys::fs::exists(DylibPath)) {
- auto EC = sys::fs::create_directory(DylibPath);
- if (EC)
- return;
- }
- sys::path::append(DylibPath, DylibFileName);
- sys::path::replace_extension(DylibPath, ext);
- if (!processYamlToDylib(YamlPath, DylibPath, DocNum))
- return;
- }
-
- EnvReady = true;
- }
-
- void TearDown() override { sys::fs::remove_directories(DirPath); }
-
- std::string getBaseDir() const {
- return std::string(DirPath.begin(), DirPath.end());
- }
-
- std::vector<std::string> getDylibPaths() const { return CreatedDylibs; }
-};
-
-static LibraryTestEnvironment *GlobalEnv =
- static_cast<LibraryTestEnvironment *>(
- ::testing::AddGlobalTestEnvironment(new LibraryTestEnvironment()));
-
-inline std::string libPath(const std::string &BaseDir,
- const std::string &name) {
-#if defined(__APPLE__)
- return BaseDir + "/" + name + ".dylib";
-#elif defined(_WIN32)
- return BaseDir + "/" + name + ".dll";
-#else
- return BaseDir + "/" + name + ".so";
-#endif
-}
-
-inline std::string withext(const std::string &lib) {
- SmallString<128> P(lib);
- sys::path::replace_extension(P, ext);
- return P.str().str();
-}
-
-inline std::string platformSymbolName(const std::string &name) {
-#if defined(__APPLE__)
- return "_" + name; // macOS prepends underscore
-#else
- return name;
-#endif
-}
-
-struct TestLibrary {
- std::string path;
- std::vector<std::string> Syms;
-};
-
-class LibraryResolverIT : public ::testing::Test {
-protected:
- std::string BaseDir;
- std::unordered_map<std::string, TestLibrary> libs;
-
- void addLib(const std::string &name) {
- SmallString<512> path;
- sys::fs::real_path(libPath(BaseDir, name + "/lib" + name), path);
- if (path.empty())
- EnvReady = false;
- libs[name] = {path.str().str(), {platformSymbolName("say" + name)}};
- }
-
- void SetUp() override {
- if (!EnvReady)
- GTEST_SKIP() << "Skipping test: environment setup failed.";
-
- ASSERT_NE(GlobalEnv, nullptr);
- BaseDir = GlobalEnv->getBaseDir();
- for (const auto &P : GlobalEnv->getDylibPaths()) {
- if (!sys::fs::exists(P))
- GTEST_SKIP();
- }
- const std::vector<std::string> libNames = {"A", "B", "C", "D", "Z"};
- for (const auto &name : libNames)
- addLib(name);
-
- if (!EnvReady)
- GTEST_SKIP() << "Skipping test: environment setup failed.";
- }
-
- const std::vector<std::string> &sym(const std::string &key) {
- return libs[key].Syms;
- }
- const std::string &lib(const std::string &key) { return libs[key].path; }
- const std::string libdir(const std::string &key) {
- SmallString<512> P(libs[key].path);
- sys::path::remove_filename(P);
- return P.str().str();
- }
- const std::string libname(const std::string &key) {
- return sys::path::filename(libs[key].path).str();
- }
-};
-
-// Helper: allow either "sayA" or "_sayA" depending on how your SymbolEnumerator
-// reports.
-static bool matchesEitherUnderscore(const std::string &got,
- const std::string &bare) {
- return got == bare || got == ("_" + bare);
-}
-
-// Helper: normalize path ending check (we only care that it resolved to the
-// right dylib)
-static bool endsWith(const std::string &s, const std::string &suffix) {
- if (s.size() < suffix.size())
- return false;
- return std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
-}
-
-// --- 1) SymbolEnumerator enumerates real exports from libC.dylib ---
-TEST_F(LibraryResolverIT, EnumerateSymbolsFromARespectsDefaults) {
- const std::string libC = lib("C");
-
- SymbolEnumeratorOptions Opts = SymbolEnumeratorOptions::defaultOptions();
-
- std::vector<std::string> seen;
- auto onEach = [&](llvm::StringRef sym) -> EnumerateResult {
- seen.emplace_back(sym.str());
- return EnumerateResult::Continue;
- };
-
- const bool ok = SymbolEnumerator::enumerateSymbols(libC, onEach, Opts);
- ASSERT_TRUE(ok) << "enumerateSymbols failed on " << libC;
-
- // We expect to see sayA (export) and not an undefined reference to printf.
- bool foundSayA = false;
- for (const auto &s : seen) {
- if (matchesEitherUnderscore(s, "sayA")) {
- foundSayA = true;
- break;
- }
- }
- EXPECT_FALSE(foundSayA) << "Expected exported symbol sayA in libC";
-}
-
-TEST_F(LibraryResolverIT, EnumerateSymbols_ExportsOnly_DefaultFlags) {
- const std::string libC = lib("C");
- SymbolEnumeratorOptions Opts = SymbolEnumeratorOptions::defaultOptions();
-
- std::vector<std::string> seen;
- auto onEach = [&](llvm::StringRef sym) -> EnumerateResult {
- seen.emplace_back(sym.str());
- return EnumerateResult::Continue;
- };
-
- ASSERT_TRUE(SymbolEnumerator::enumerateSymbols(libC, onEach, Opts));
-
- // sayC is exported, others are undefined → only sayC expected
- EXPECT_TRUE(any_of(seen, [&](const std::string &s) {
- return matchesEitherUnderscore(s, "sayC");
- }));
- EXPECT_FALSE(any_of(seen, [&](const std::string &s) {
- return matchesEitherUnderscore(s, "sayA");
- }));
- EXPECT_FALSE(any_of(seen, [&](const std::string &s) {
- return matchesEitherUnderscore(s, "sayB");
- }));
- EXPECT_FALSE(any_of(seen, [&](const std::string &s) {
- return matchesEitherUnderscore(s, "sayZ");
- }));
-}
-
-TEST_F(LibraryResolverIT, EnumerateSymbols_IncludesUndefineds) {
- const std::string libC = lib("C");
-
- SymbolEnumeratorOptions Opts;
- Opts.FilterFlags =
- SymbolEnumeratorOptions::IgnoreWeak |
- SymbolEnumeratorOptions::IgnoreIndirect; // no IgnoreUndefined
-
- std::vector<std::string> seen;
- auto onEach = [&](llvm::StringRef sym) -> EnumerateResult {
- seen.emplace_back(sym.str());
- return EnumerateResult::Continue;
- };
-
- ASSERT_TRUE(SymbolEnumerator::enumerateSymbols(libC, onEach, Opts));
-
- // Now we should see both sayC (export) and the undefined refs sayA, sayB,
- // sayZ
- EXPECT_TRUE(any_of(seen, [&](const std::string &s) {
- return matchesEitherUnderscore(s, "sayC");
- }));
- EXPECT_TRUE(any_of(seen, [&](const std::string &s) {
- return matchesEitherUnderscore(s, "sayA");
- }));
- EXPECT_TRUE(any_of(seen, [&](const std::string &s) {
- return matchesEitherUnderscore(s, "sayB");
- }));
- EXPECT_TRUE(any_of(seen, [&](const std::string &s) {
- return matchesEitherUnderscore(s, "sayZ");
- }));
-}
-
-TEST_F(LibraryResolverIT, EnumerateSymbols_IndirectExportRespected) {
- const std::string libD = lib("D");
-
- SymbolEnumeratorOptions Opts;
- Opts.FilterFlags = SymbolEnumeratorOptions::IgnoreWeak; // allow indirects
-
- std::vector<std::string> seen;
- auto onEach = [&](llvm::StringRef sym) -> EnumerateResult {
- seen.emplace_back(sym.str());
- return EnumerateResult::Continue;
- };
-
- ASSERT_TRUE(SymbolEnumerator::enumerateSymbols(libD, onEach, Opts));
-
- // sayA is re-exported from A, so should appear unless IgnoreIndirect was set
- EXPECT_TRUE(any_of(seen, [&](const std::string &s) {
- return matchesEitherUnderscore(s, "sayA");
- }));
-}
-
-// --- 2) Filters: if we remove IgnoreUndefined, we should also see undefineds
-// like printf ---
-TEST_F(LibraryResolverIT, EnumerateSymbolsIncludesUndefWhenNotIgnored) {
- const std::string libA = lib("A");
-
- SymbolEnumeratorOptions Opts = SymbolEnumeratorOptions::defaultOptions();
- // Start from defaults but allow undefined
- Opts.FilterFlags &= ~SymbolEnumeratorOptions::IgnoreUndefined;
-
- bool SawPrintf = false;
- auto onEach = [&](llvm::StringRef sym) -> EnumerateResult {
- if (matchesEitherUnderscore(sym.str(), "printf") ||
- matchesEitherUnderscore(sym.str(), "puts"))
- SawPrintf = true;
- return EnumerateResult::Continue;
- };
-
- ASSERT_TRUE(SymbolEnumerator::enumerateSymbols(libA, onEach, Opts));
- EXPECT_TRUE(SawPrintf)
- << "Expected to see undefined symbol printf when not filtered";
-}
-
-// --- 3) Full resolution via LibraryResolutionDriver/LibraryResolver ---
-TEST_F(LibraryResolverIT, DriverResolvesSymbolsToCorrectLibraries) {
- // Create the resolver from real base paths (our fixtures dir)
- auto Stup = LibraryResolver::Setup::create({BaseDir});
-
- // Full system behavior: no mocks
- auto Driver = LibraryResolutionDriver::create(Stup);
- ASSERT_NE(Driver, nullptr);
-
- // Tell the Driver about the scan path kinds (User/System) as your production
- // code expects.
- Driver->addScanPath(libdir("A"), PathType::User);
- Driver->addScanPath(libdir("B"), PathType::User);
- Driver->addScanPath(libdir("Z"), PathType::User);
-
- // Symbols to resolve (bare names; class handles underscore differences
- // internally)
- std::vector<std::string> Syms = {platformSymbolName("sayA"),
- platformSymbolName("sayB"),
- platformSymbolName("sayZ")};
-
- bool CallbackRan = false;
- Driver->resolveSymbols(Syms, [&](SymbolQuery &Q) {
- CallbackRan = true;
-
- // sayA should resolve to A.dylib
- {
- auto lib = Q.getResolvedLib(platformSymbolName("sayA"));
- ASSERT_TRUE(lib.has_value()) << "sayA should be resolved";
- EXPECT_TRUE(endsWith(lib->str(), libname("A")))
- << "sayA resolved to: " << lib->str();
- }
-
- // sayB should resolve to B.dylib
- {
- auto lib = Q.getResolvedLib(platformSymbolName("sayB"));
- ASSERT_TRUE(lib.has_value()) << "sayB should be resolved";
- EXPECT_TRUE(endsWith(lib->str(), libname("B")))
- << "sayB resolved to: " << lib->str();
- }
-
- // sayZ should resolve to B.dylib
- {
- auto lib = Q.getResolvedLib(platformSymbolName("sayZ"));
- ASSERT_TRUE(lib.has_value()) << "sayZ should be resolved";
- EXPECT_TRUE(endsWith(lib->str(), libname("Z")))
- << "sayZ resolved to: " << lib->str();
- }
-
- EXPECT_TRUE(Q.allResolved());
- });
-
- EXPECT_TRUE(CallbackRan);
-}
-
-// --- 4) Cross-library reference visibility (C references A) ---
-TEST_F(LibraryResolverIT, EnumeratorSeesInterLibraryRelationship) {
- const std::string libC = lib("C");
-
- SymbolEnumeratorOptions OnlyUndef = SymbolEnumeratorOptions::defaultOptions();
- // Show only undefined (drop IgnoreUndefined) to see C's reference to sayA
- OnlyUndef.FilterFlags &= ~SymbolEnumeratorOptions::IgnoreUndefined;
-
- bool SawSayAAsUndef = false;
- auto onEach = [&](llvm::StringRef sym) -> EnumerateResult {
- if (matchesEitherUnderscore(sym.str(), "sayA"))
- SawSayAAsUndef = true;
- return EnumerateResult::Continue;
- };
-
- ASSERT_TRUE(SymbolEnumerator::enumerateSymbols(libC, onEach, OnlyUndef));
- EXPECT_TRUE(SawSayAAsUndef)
- << "libC should have an undefined reference to sayA (defined in libA)";
-}
-
-// // // --- 5) Optional: stress SymbolQuery with the real resolve flow
-// // // And resolve libC dependency libA, libB, libZ ---
-TEST_F(LibraryResolverIT, ResolveManySymbols) {
- auto Stup = LibraryResolver::Setup::create({BaseDir});
- auto Driver = LibraryResolutionDriver::create(Stup);
- ASSERT_NE(Driver, nullptr);
- Driver->addScanPath(libdir("C"), PathType::User);
-
- // Many duplicates to provoke concurrent updates inside SymbolQuery
- std::vector<std::string> Syms = {
- platformSymbolName("sayA"), platformSymbolName("sayB"),
- platformSymbolName("sayA"), platformSymbolName("sayB"),
- platformSymbolName("sayZ"), platformSymbolName("sayZ"),
- platformSymbolName("sayZ"), platformSymbolName("sayZ"),
- platformSymbolName("sayA"), platformSymbolName("sayB"),
- platformSymbolName("sayA"), platformSymbolName("sayB")};
-
- bool CallbackRan = false;
- Driver->resolveSymbols(Syms, [&](SymbolQuery &Q) {
- CallbackRan = true;
- EXPECT_TRUE(Q.isResolved(platformSymbolName("sayA")));
- EXPECT_TRUE(Q.isResolved(platformSymbolName("sayB")));
- EXPECT_TRUE(Q.isResolved(platformSymbolName("sayZ")));
-
- auto A = Q.getResolvedLib(platformSymbolName("sayA"));
- auto B = Q.getResolvedLib(platformSymbolName("sayB"));
- auto Z = Q.getResolvedLib(platformSymbolName("sayZ"));
- ASSERT_TRUE(A.has_value());
- ASSERT_TRUE(B.has_value());
- ASSERT_TRUE(Z.has_value());
- EXPECT_TRUE(endsWith(A->str(), libname("A")));
- EXPECT_TRUE(endsWith(B->str(), libname("B")));
- EXPECT_TRUE(endsWith(Z->str(), libname("Z")));
- EXPECT_TRUE(Q.allResolved());
- });
-
- EXPECT_TRUE(CallbackRan);
-}
-
-// // // --- 5) Optional: stress SymbolQuery with the real resolve flow
-// // // And resolve libD dependency libA ---
-TEST_F(LibraryResolverIT, ResolveManySymbols2) {
- auto Stup = LibraryResolver::Setup::create({BaseDir});
- auto Driver = LibraryResolutionDriver::create(Stup);
- ASSERT_NE(Driver, nullptr);
- Driver->addScanPath(libdir("D"), PathType::User);
-
- // Many duplicates to provoke concurrent updates inside SymbolQuery
- std::vector<std::string> Syms = {
- platformSymbolName("sayA"), platformSymbolName("sayB"),
- platformSymbolName("sayA"), platformSymbolName("sayB"),
- platformSymbolName("sayZ"), platformSymbolName("sayZ"),
- platformSymbolName("sayZ"), platformSymbolName("sayZ"),
- platformSymbolName("sayD"), platformSymbolName("sayD"),
- platformSymbolName("sayA"), platformSymbolName("sayB"),
- platformSymbolName("sayA"), platformSymbolName("sayB")};
-
- Driver->resolveSymbols(Syms, [&](SymbolQuery &Q) {
- EXPECT_TRUE(Q.isResolved(platformSymbolName("sayA")));
- EXPECT_TRUE(Q.isResolved(platformSymbolName("sayD")));
-
- auto A = Q.getResolvedLib(platformSymbolName("sayA"));
- auto D = Q.getResolvedLib(platformSymbolName("sayD"));
- ASSERT_TRUE(A.has_value());
- ASSERT_TRUE(D.has_value());
- EXPECT_TRUE(endsWith(A->str(), libname("A")));
- EXPECT_TRUE(endsWith(D->str(), libname("D")));
- EXPECT_FALSE(Q.allResolved());
- });
-}
-
-TEST_F(LibraryResolverIT, ScanSingleUserPath) {
- auto LibPathCache = std::make_shared<LibraryPathCache>();
- auto PResolver = std::make_shared<PathResolver>(LibPathCache);
- LibraryScanHelper ScanH({}, LibPathCache, PResolver);
-
- ScanH.addBasePath(libdir("C"), PathType::User);
-
- std::error_code EC;
- auto libCPathOpt = PResolver->resolve(lib("C"), EC);
-
- if (!libCPathOpt || EC) {
- FAIL();
- }
-
- std::string libCPath = *libCPathOpt;
-
- LibraryManager LibMgr;
- LibraryScanner Scanner(ScanH, LibMgr);
-
- Scanner.scanNext(PathType::User, 0);
-
- bool found = false;
- LibMgr.forEachLibrary([&](const LibraryInfo &lib) {
- if (lib.getFullPath() == libCPath) {
- found = true;
- }
- return true;
- });
- EXPECT_TRUE(found) << "Expected to find " << libCPath;
-}
-
-TEST_F(LibraryResolverIT, ScanAndCheckDeps) {
- auto LibPathCache = std::make_shared<LibraryPathCache>();
- auto PResolver = std::make_shared<PathResolver>(LibPathCache);
- LibraryScanHelper ScanH({}, LibPathCache, PResolver);
-
- ScanH.addBasePath(libdir("C"), PathType::User);
-
- LibraryManager LibMgr;
- LibraryScanner Scanner(ScanH, LibMgr);
-
- Scanner.scanNext(PathType::User, 0);
-
- size_t count = 0;
- LibMgr.forEachLibrary([&](const LibraryInfo &) {
- count++;
- return true;
- });
-
- EXPECT_GE(count, 3u) << "Should find at least libA in multiple paths";
-}
-
-TEST_F(LibraryResolverIT, ScanEmptyPath) {
- auto LibPathCache = std::make_shared<LibraryPathCache>();
- auto PResolver = std::make_shared<PathResolver>(LibPathCache);
- LibraryScanHelper ScanH({}, LibPathCache, PResolver);
-
- ScanH.addBasePath("/tmp/empty", PathType::User);
-
- LibraryManager LibMgr;
- LibraryScanner Scanner(ScanH, LibMgr);
-
- Scanner.scanNext(PathType::User, 0);
-
- size_t count = 0;
- LibMgr.forEachLibrary([&](const LibraryInfo &) {
- count++;
- return true;
- });
- EXPECT_EQ(count, 0u);
-}
-
-TEST_F(LibraryResolverIT, PathResolverResolvesKnownPaths) {
- auto LibPathCache = std::make_shared<LibraryPathCache>();
- auto PResolver = std::make_shared<PathResolver>(LibPathCache);
-
- std::error_code EC;
- auto Missing = PResolver->resolve("temp/foo/bar", EC);
- EXPECT_FALSE(Missing.has_value()) << "Unexpectedly resolved a bogus path";
- EXPECT_TRUE(EC) << "Expected error resolving path";
-
- auto DirPath = PResolver->resolve(BaseDir, EC);
- ASSERT_TRUE(DirPath.has_value());
- EXPECT_FALSE(EC) << "Expected no error resolving path";
- EXPECT_EQ(*DirPath, BaseDir);
-
- auto DylibPath = PResolver->resolve(lib("C"), EC);
- ASSERT_TRUE(DylibPath.has_value());
- EXPECT_FALSE(EC) << "Expected no error resolving path";
- EXPECT_EQ(*DylibPath, lib("C"));
-}
-
-TEST_F(LibraryResolverIT, PathResolverNormalizesDotAndDotDot) {
- auto LibPathCache = std::make_shared<LibraryPathCache>();
- auto PResolver = std::make_shared<PathResolver>(LibPathCache);
-
- std::error_code EC;
-
- // e.g. BaseDir + "/./C/../C/C.dylib" → BaseDir + "/C.dylib"
- std::string Messy = BaseDir + "/C/./../C/./libC" + ext;
- auto Resolved = PResolver->resolve(Messy, EC);
- ASSERT_TRUE(Resolved.has_value());
- EXPECT_FALSE(EC);
- EXPECT_EQ(*Resolved, lib("C")) << "Expected realpath to collapse . and ..";
-}
-
-#if !defined(_WIN32)
-TEST_F(LibraryResolverIT, PathResolverFollowsSymlinks) {
- auto LibPathCache = std::make_shared<LibraryPathCache>();
- auto PResolver = std::make_shared<PathResolver>(LibPathCache);
-
- std::error_code EC;
-
- // Create a symlink temp -> BaseDir (only if filesystem allows it)
- std::string linkName = BaseDir + withext("/link_to_C");
- std::string target = lib("C");
- ::symlink(target.c_str(), linkName.c_str());
-
- auto resolved = PResolver->resolve(linkName, EC);
- ASSERT_TRUE(resolved.has_value());
- EXPECT_FALSE(EC);
- EXPECT_EQ(*resolved, target);
-
- ::unlink(linkName.c_str()); // cleanup
-}
-
-TEST_F(LibraryResolverIT, PathResolverCachesResults) {
- auto LibPathCache = std::make_shared<LibraryPathCache>();
- auto PResolver = std::make_shared<PathResolver>(LibPathCache);
-
- SmallString<128> TmpDylib;
- sys::fs::createUniqueFile(withext("A-copy"), TmpDylib);
- sys::fs::copy_file(lib("A"), TmpDylib);
-
- std::error_code EC;
-
- // First resolve -> should populate LibPathCache
- auto first = PResolver->resolve(TmpDylib, EC);
- ASSERT_TRUE(first.has_value());
-
- // Forcefully remove the file from disk
- ::unlink(TmpDylib.c_str());
-
- // Second resolve -> should still succeed from LibPathCache
- auto second = PResolver->resolve(TmpDylib, EC);
- EXPECT_TRUE(second.has_value());
- EXPECT_EQ(*second, *first);
-}
-#endif
-
-TEST_F(LibraryResolverIT, LoaderPathSubstitutionAndResolve) {
- auto LibPathCache = std::make_shared<LibraryPathCache>();
- auto PResolver = std::make_shared<PathResolver>(LibPathCache);
-
- DylibSubstitutor substitutor;
- substitutor.configure(libdir("C"));
-#if defined(__APPLE__)
- // Substitute @loader_path with BaseDir
- std::string substituted =
- substitutor.substitute(withext("@loader_path/libC"));
-#elif defined(__linux__)
- // Substitute $origin with BaseDir
- std::string substituted = substitutor.substitute(withext("$ORIGIN/libC"));
-#endif
- ASSERT_FALSE(substituted.empty());
- EXPECT_EQ(substituted, lib("C"));
-
- // Now try resolving the substituted path
- std::error_code EC;
- auto resolved = PResolver->resolve(substituted, EC);
- ASSERT_TRUE(resolved.has_value()) << "Expected to resolve substituted dylib";
- EXPECT_EQ(*resolved, lib("C"));
- EXPECT_FALSE(EC) << "Expected no error resolving substituted dylib";
-}
-
-TEST_F(LibraryResolverIT, ResolveFromUsrOrSystemPaths) {
- auto LibPathCache = std::make_shared<LibraryPathCache>();
- auto PResolver = std::make_shared<PathResolver>(LibPathCache);
-
- DylibPathValidator validator(*PResolver);
-
- std::vector<std::string> Paths = {"/foo/bar/", "temp/foo", libdir("C"),
- libdir("A"), libdir("B"), libdir("Z")};
-
- SmallVector<StringRef> P(Paths.begin(), Paths.end());
-
- DylibResolver Resolver(validator);
- Resolver.configure("", {{P, SearchPathType::UsrOrSys}});
-
- // Check "C"
- auto ValOptC = Resolver.resolve("libC", true);
- EXPECT_TRUE(ValOptC.has_value());
- EXPECT_EQ(*ValOptC, lib("C"));
-
- auto ValOptCdylib = Resolver.resolve(withext("libC"));
- EXPECT_TRUE(ValOptCdylib.has_value());
- EXPECT_EQ(*ValOptCdylib, lib("C"));
-
- // Check "A"
- auto ValOptA = Resolver.resolve("libA", true);
- EXPECT_TRUE(ValOptA.has_value());
- EXPECT_EQ(*ValOptA, lib("A"));
-
- auto ValOptAdylib = Resolver.resolve(withext("libA"));
- EXPECT_TRUE(ValOptAdylib.has_value());
- EXPECT_EQ(*ValOptAdylib, lib("A"));
-
- // Check "B"
- auto ValOptB = Resolver.resolve("libB", true);
- EXPECT_TRUE(ValOptB.has_value());
- EXPECT_EQ(*ValOptB, lib("B"));
-
- auto ValOptBdylib = Resolver.resolve(withext("libB"));
- EXPECT_TRUE(ValOptBdylib.has_value());
- EXPECT_EQ(*ValOptBdylib, lib("B"));
-
- // Check "Z"
- auto ValOptZ = Resolver.resolve("libZ", true);
- EXPECT_TRUE(ValOptZ.has_value());
- EXPECT_EQ(*ValOptZ, lib("Z"));
-
- auto ValOptZdylib = Resolver.resolve(withext("libZ"));
- EXPECT_TRUE(ValOptZdylib.has_value());
- EXPECT_EQ(*ValOptZdylib, lib("Z"));
-}
-
-#if defined(__APPLE__)
-TEST_F(LibraryResolverIT, ResolveViaLoaderPathAndRPathSubstitution) {
- auto LibPathCache = std::make_shared<LibraryPathCache>();
- auto PResolver = std::make_shared<PathResolver>(LibPathCache);
-
- DylibPathValidator validator(*PResolver);
-
- std::vector<std::string> Paths = {"@loader_path/../A", "@loader_path/../B",
- "@loader_path/../D", "@loader_path/../Z"};
-
- SmallVector<StringRef> P(Paths.begin(), Paths.end());
-
- DylibResolver Resolver(validator);
-
- // Use only RPath config
- Resolver.configure(lib("C"), {{P, SearchPathType::RPath}});
-
- // --- Check A ---
- auto ValOptA = Resolver.resolve("@rpath/libA", true);
- EXPECT_TRUE(ValOptA.has_value());
- EXPECT_EQ(*ValOptA, lib("A"));
-
- auto ValOptAdylib = Resolver.resolve(withext("@rpath/libA"));
- EXPECT_TRUE(ValOptAdylib.has_value());
- EXPECT_EQ(*ValOptAdylib, lib("A"));
-
- // --- Check B ---
- auto ValOptB = Resolver.resolve("@rpath/libB", true);
- EXPECT_TRUE(ValOptB.has_value());
- EXPECT_EQ(*ValOptB, lib("B"));
-
- auto ValOptBdylib = Resolver.resolve(withext("@rpath/libB"));
- EXPECT_TRUE(ValOptBdylib.has_value());
- EXPECT_EQ(*ValOptBdylib, lib("B"));
-
- // --- Check Z ---
- auto ValOptZ = Resolver.resolve("@rpath/libZ", true);
- EXPECT_TRUE(ValOptZ.has_value());
- EXPECT_EQ(*ValOptZ, lib("Z"));
-
- auto ValOptZdylib = Resolver.resolve(withext("@rpath/libZ"));
- EXPECT_TRUE(ValOptZdylib.has_value());
- EXPECT_EQ(*ValOptZdylib, lib("Z"));
-}
-#endif
-
-#if defined(__linux__)
-TEST_F(LibraryResolverIT, ResolveViaOriginAndRPathSubstitution) {
- auto LibPathCache = std::make_shared<LibraryPathCache>();
- auto PResolver = std::make_shared<PathResolver>(LibPathCache);
-
- DylibPathValidator validator(*PResolver);
-
- // On Linux, $ORIGIN works like @loader_path
- std::vector<std::string> Paths = {"$ORIGIN/../A", "$ORIGIN/../B",
- "$ORIGIN/../D", "$ORIGIN/../Z"};
-
- SmallVector<StringRef> P(Paths.begin(), Paths.end());
-
- DylibResolver Resolver(validator);
-
- // Use only RPath config
- Resolver.configure(lib("C"), {{P, SearchPathType::RunPath}});
-
- // --- Check A ---
- auto ValOptA = Resolver.resolve("libA", true);
- EXPECT_TRUE(ValOptA.has_value());
- EXPECT_EQ(*ValOptA, lib("A"));
-
- auto valOptASO = Resolver.resolve(withext("libA"));
- EXPECT_TRUE(valOptASO.has_value());
- EXPECT_EQ(*valOptASO, lib("A"));
-
- // --- Check B ---
- auto ValOptB = Resolver.resolve("libB", true);
- EXPECT_TRUE(ValOptB.has_value());
- EXPECT_EQ(*ValOptB, lib("B"));
-
- auto valOptBSO = Resolver.resolve(withext("libB"));
- EXPECT_TRUE(valOptBSO.has_value());
- EXPECT_EQ(*valOptBSO, lib("B"));
-
- // --- Check Z ---
- auto ValOptZ = Resolver.resolve("libZ", true);
- EXPECT_TRUE(ValOptZ.has_value());
- EXPECT_EQ(*ValOptZ, lib("Z"));
-
- auto valOptZSO = Resolver.resolve(withext("libZ"));
- EXPECT_TRUE(valOptZSO.has_value());
- EXPECT_EQ(*valOptZSO, lib("Z"));
-}
-#endif
-} // namespace
-#endif // defined(__APPLE__)