util/library.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | #include "chrono_batch_render/util/library.h" | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <string_view> | ||
| 6 | |||
| 7 | #ifdef _WIN32 | ||
| 8 | #include <windows.h> | ||
| 9 | #else | ||
| 10 | #include <dlfcn.h> | ||
| 11 | #endif | ||
| 12 | #ifdef __linux__ | ||
| 13 | #include <link.h> | ||
| 14 | #endif | ||
| 15 | |||
| 16 | #include <fmt/format.h> | ||
| 17 | |||
| 18 | namespace chrono::batch_render { | ||
| 19 | namespace { | ||
| 20 | |||
| 21 | 208 | void* OpenHandle(const char* name, bool loadedOnly) { | |
| 22 | #ifdef _WIN32 | ||
| 23 | return loadedOnly ? GetModuleHandleA(name) : LoadLibraryA(name); | ||
| 24 | #else | ||
| 25 | 284 | return dlopen(name, RTLD_NOW | RTLD_LOCAL | (loadedOnly ? RTLD_NOLOAD : 0)); | |
| 26 | #endif | ||
| 27 | } | ||
| 28 | |||
| 29 | 50 | std::vector<std::string> WithUnversioned(std::initializer_list<const char*> names) { | |
| 30 | 50 | std::vector<std::string> all(names.begin(), names.end()); | |
| 31 | 153 | for (const std::string_view name : names) { | |
| 32 | 103 | const size_t suffix = name.find(".so."); | |
| 33 | 103 | if (suffix != std::string_view::npos) { | |
| 34 | 70 | const std::string unversioned(name.substr(0, suffix + 3)); | |
| 35 | 70 | if (std::ranges::find(all, unversioned) == all.end()) { | |
| 36 | 34 | all.push_back(unversioned); | |
| 37 | } | ||
| 38 | 70 | } | |
| 39 | } | ||
| 40 | 50 | return all; | |
| 41 | ✗ | } | |
| 42 | |||
| 43 | } | ||
| 44 | |||
| 45 | 102 | Library Library::First(const std::vector<std::string>& names, bool loadedOnly) { | |
| 46 | 102 | Library library; | |
| 47 | 301 | for (const std::string& name : names) { | |
| 48 | 208 | if (void* handle = OpenHandle(name.c_str(), loadedOnly)) { | |
| 49 | 9 | library.m_handle = handle; | |
| 50 | 9 | library.m_name = name; | |
| 51 | break; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | 102 | return library; | |
| 55 | ✗ | } | |
| 56 | |||
| 57 | 18 | Library Library::Find(std::initializer_list<const char*> names) { | |
| 58 | 18 | return First(WithUnversioned(names), true); | |
| 59 | } | ||
| 60 | |||
| 61 | 32 | Library Library::Load(std::initializer_list<const char*> names, | |
| 62 | std::initializer_list<const char*> paths, | ||
| 63 | const std::filesystem::path& beside) { | ||
| 64 | 32 | const std::vector<std::string> candidates = WithUnversioned(names); | |
| 65 | 32 | if (Library loaded = First(candidates, true)) { | |
| 66 | 5 | Log(LogLevel::Debug, "using the {} already in the process", loaded.Name()); | |
| 67 | 5 | return loaded; | |
| 68 | 5 | } | |
| 69 | |||
| 70 | 27 | Library library; | |
| 71 | 27 | if (!beside.empty()) { | |
| 72 | 1 | std::vector<std::string> besidePaths; | |
| 73 | 3 | for (const std::string& name : candidates) { | |
| 74 | 8 | besidePaths.push_back((beside / name).string()); | |
| 75 | } | ||
| 76 | 2 | library = First(besidePaths, false); | |
| 77 | 1 | } | |
| 78 | 27 | if (!library) { | |
| 79 | 52 | library = First({names.begin(), names.end()}, false); | |
| 80 | } | ||
| 81 | 27 | if (!library) { | |
| 82 | 50 | library = First({paths.begin(), paths.end()}, false); | |
| 83 | } | ||
| 84 | 27 | if (library) { | |
| 85 | 3 | Log(LogLevel::Debug, "loaded {}", library.Name()); | |
| 86 | } | ||
| 87 | 27 | return library; | |
| 88 | 32 | } | |
| 89 | |||
| 90 | 3 | std::filesystem::path Library::Directory() const { | |
| 91 | 3 | if (m_handle == nullptr) { | |
| 92 | 1 | return {}; | |
| 93 | } | ||
| 94 | #if defined(_WIN32) | ||
| 95 | wchar_t path[MAX_PATH] = {}; | ||
| 96 | const DWORD length = GetModuleFileNameW(static_cast<HMODULE>(m_handle), path, MAX_PATH); | ||
| 97 | return length == 0 || length == MAX_PATH ? std::filesystem::path{} | ||
| 98 | : std::filesystem::path(path).parent_path(); | ||
| 99 | #elif defined(__linux__) | ||
| 100 | 2 | link_map* map = nullptr; | |
| 101 | 2 | if (dlinfo(m_handle, RTLD_DI_LINKMAP, &map) != 0 || map == nullptr || map->l_name == nullptr) { | |
| 102 | ✗ | return {}; | |
| 103 | } | ||
| 104 | 4 | return std::filesystem::path(map->l_name).parent_path(); | |
| 105 | #else | ||
| 106 | return {}; | ||
| 107 | #endif | ||
| 108 | } | ||
| 109 | |||
| 110 | 9 | void* Library::Symbol(const char* symbol) const { | |
| 111 | 9 | if (m_handle == nullptr) { | |
| 112 | return nullptr; | ||
| 113 | } | ||
| 114 | #ifdef _WIN32 | ||
| 115 | return reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(m_handle), symbol)); | ||
| 116 | #else | ||
| 117 | 8 | return dlsym(m_handle, symbol); | |
| 118 | #endif | ||
| 119 | } | ||
| 120 | |||
| 121 | 1 | std::string TooOld(std::string_view what, Version found, Version needed) { | |
| 122 | 1 | return fmt::format("{} is version {}; Chrono Batch Render needs {} or later", what, | |
| 123 | 2 | found.ToString(), needed.ToString()); | |
| 124 | } | ||
| 125 | |||
| 126 | } | ||
| 127 |