File indexing completed on 2026-04-17 07:46:40
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Geometry/GeometryModuleLoader.hpp"
0010
0011 #include "Acts/Geometry/GeometryModule.h"
0012 #include "Acts/Geometry/TrackingGeometry.hpp"
0013
0014 #include <cstring>
0015 #include <format>
0016 #include <stdexcept>
0017
0018 #include <dlfcn.h>
0019
0020 #ifndef ACTS_GEOMETRY_MODULE_ABI_TAG
0021 #error \
0022 "ACTS_GEOMETRY_MODULE_ABI_TAG must be provided by CMake when building ActsCore."
0023 #endif
0024
0025 namespace {
0026
0027 using GeometryModuleEntryPointV1 = const ActsGeometryModuleV1* (*)(void);
0028
0029 std::shared_ptr<void> openSharedLibrary(const std::filesystem::path& path) {
0030 if (!std::filesystem::exists(path)) {
0031 throw std::runtime_error(
0032 std::format("Geometry module file does not exist: {}", path.string()));
0033 }
0034
0035 void* rawHandle = ::dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
0036 if (rawHandle == nullptr) {
0037 const char* error = ::dlerror();
0038 throw std::runtime_error(std::format(
0039 "Failed to load geometry module '{}': {}", path.string(), error));
0040 }
0041 return std::shared_ptr<void>(rawHandle, [](void* handle) {
0042 if (handle != nullptr) {
0043 ::dlclose(handle);
0044 }
0045 });
0046 }
0047
0048 GeometryModuleEntryPointV1 resolveEntrypointV1(
0049 const std::filesystem::path& path, const std::shared_ptr<void>& library) {
0050 ::dlerror();
0051 void* symbol = ::dlsym(library.get(), "acts_geometry_module_v1");
0052 if (const char* error = ::dlerror(); error != nullptr) {
0053 throw std::runtime_error(
0054 std::format("Failed to resolve acts_geometry_module_v1 in '{}': {}",
0055 path.string(), error));
0056 }
0057 if (symbol == nullptr) {
0058 throw std::runtime_error(std::format(
0059 "Entry point acts_geometry_module_v1 resolved to nullptr in '{}'",
0060 path.string()));
0061 }
0062 return reinterpret_cast<GeometryModuleEntryPointV1>(symbol);
0063 }
0064
0065 const char* geometryModuleHostAbiTag() noexcept {
0066 return ACTS_GEOMETRY_MODULE_ABI_TAG;
0067 }
0068
0069 }
0070
0071 namespace Acts::detail {
0072
0073 std::shared_ptr<TrackingGeometry> loadGeometryModuleImpl(
0074 const std::filesystem::path& modulePath, const char* expectedAbiTag,
0075 const char* expectedUserDataType, const void* userData,
0076 const Logger& logger) {
0077 auto library = openSharedLibrary(modulePath);
0078 auto entryPoint = resolveEntrypointV1(modulePath, library);
0079 const ActsGeometryModuleV1* descriptor = entryPoint();
0080 if (descriptor == nullptr) {
0081 throw std::runtime_error("Geometry module descriptor is null");
0082 }
0083 if (descriptor->module_abi_tag == nullptr || descriptor->build == nullptr ||
0084 descriptor->destroy == nullptr) {
0085 throw std::runtime_error("Geometry module descriptor is incomplete");
0086 }
0087 if (expectedAbiTag == nullptr) {
0088 throw std::runtime_error("Expected geometry module ABI tag is null");
0089 }
0090 if (std::strcmp(descriptor->module_abi_tag, expectedAbiTag) != 0) {
0091 throw std::runtime_error(std::format(
0092 "Geometry module ABI mismatch: module='{}' host='{}' path='{}'",
0093 descriptor->module_abi_tag, expectedAbiTag, modulePath.string()));
0094 }
0095
0096
0097
0098 if (const char* actualType = descriptor->user_data_type;
0099 !((expectedUserDataType == nullptr && actualType == nullptr) ||
0100 (expectedUserDataType != nullptr && actualType != nullptr &&
0101 std::strcmp(actualType, expectedUserDataType) == 0))) {
0102 if (actualType == nullptr) {
0103 throw std::runtime_error(std::format(
0104 "Geometry module '{}' does not require user data; "
0105 "use loadGeometryModule(path, logger) without extra context",
0106 modulePath.string()));
0107 } else if (expectedUserDataType == nullptr) {
0108 throw std::runtime_error(std::format(
0109 "Geometry module '{}' requires user data of type '{}'; "
0110 "use the appropriate typed loader (e.g. loadDD4hepGeometryModule)",
0111 modulePath.string(), actualType));
0112 } else {
0113 throw std::runtime_error(
0114 std::format("Geometry module '{}' user_data_type mismatch: "
0115 "expected '{}', module declares '{}'",
0116 modulePath.string(), expectedUserDataType, actualType));
0117 }
0118 }
0119
0120 void* rawHandle = descriptor->build(userData, &logger);
0121 if (rawHandle == nullptr) {
0122 throw std::runtime_error("Geometry module build returned null handle");
0123 }
0124
0125 auto destroyFn = descriptor->destroy;
0126 return std::shared_ptr<TrackingGeometry>(
0127 static_cast<TrackingGeometry*>(rawHandle),
0128 [destroyFn, library = std::move(library)](TrackingGeometry* geometry) {
0129 destroyFn(static_cast<void*>(geometry));
0130 });
0131 }
0132
0133 }
0134
0135 namespace Acts {
0136
0137 std::shared_ptr<TrackingGeometry> loadGeometryModule(
0138 const std::filesystem::path& modulePath, const Logger& logger) {
0139 return detail::loadGeometryModuleImpl(modulePath, geometryModuleHostAbiTag(),
0140 nullptr, nullptr, logger);
0141 }
0142
0143 }