diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 5ea812622b9b6..a8e7d2cc9299a 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -1654,9 +1654,25 @@ if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") endif() endif() -#Now the 'onnxruntime_EXTERNAL_LIBRARIES' variable should be sealed. It will be used in onnxruntime.cmake which will be included in the next. -#The order of the following targets matters. Right depends on left. If target A appears before target B. Then A.cmake can not use variables defined in B.cmake. -set(ONNXRUNTIME_CMAKE_FILES onnxruntime_flatbuffers onnxruntime_common onnxruntime_mlas onnxruntime_graph onnxruntime_lora onnxruntime_framework onnxruntime_util onnxruntime_providers onnxruntime_optimizer onnxruntime_session ${ONNXRUNTIME_EAGER_CMAKE_FILE_NAME}) +# From this point on, onnxruntime_EXTERNAL_LIBRARIES should be no longer be modified. It will be used in +# onnxruntime.cmake which will be included next. + +# The order of the CMake file names (which exclude the ".cmake" suffix) in ONNXRUNTIME_CMAKE_FILES matters. Later CMake +# files may depend on earlier ones but earlier ones cannot depend on later ones. For example, if A appears before B, +# then A.cmake cannot use variables defined in B.cmake. +set(ONNXRUNTIME_CMAKE_FILES + onnxruntime_flatbuffers + onnxruntime_common + onnxruntime_mlas + onnxruntime_graph + onnxruntime_lora + onnxruntime_framework + onnxruntime_util + onnxruntime_providers + onnxruntime_optimizer + onnxruntime_session + ${ONNXRUNTIME_EAGER_CMAKE_FILE_NAME} + ) if (onnxruntime_USE_WINML) # WINML uses and depends on the shared lib. Note: You can build WINML without DML and you will get a diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index 57ad1e597a205..d96ade9d32b9e 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -588,6 +588,7 @@ set (onnxruntime_shared_lib_test_SRC ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/test_nontensor_types.cc ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/test_ort_format_models.cc ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/test_run_options.cc + ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/test_runtime_path.cc ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/test_session_options.cc ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/utils.h ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/utils.cc @@ -1518,9 +1519,48 @@ endif() target_link_libraries(onnxruntime_mocked_allocator PRIVATE ${GSL_TARGET}) set_target_properties(onnxruntime_mocked_allocator PROPERTIES FOLDER "ONNXRuntimeTest") + # onnxruntime_runtime_path_test_shared_library + block() + set(onnxruntime_runtime_path_test_shared_library_src + "${TEST_SRC_DIR}/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.h" + "${TEST_SRC_DIR}/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.cc") + + onnxruntime_add_shared_library(onnxruntime_runtime_path_test_shared_library + ${onnxruntime_runtime_path_test_shared_library_src}) + + target_link_libraries(onnxruntime_runtime_path_test_shared_library PRIVATE + onnxruntime_common cpuinfo ${CMAKE_DL_LIBS}) + target_include_directories(onnxruntime_runtime_path_test_shared_library PRIVATE ${ONNXRUNTIME_ROOT}) + + if(UNIX) + if (APPLE) + set(onnxruntime_runtime_path_test_shared_library_link_flags "-Xlinker -dead_strip") + elseif (NOT CMAKE_SYSTEM_NAME MATCHES "AIX") + string(CONCAT onnxruntime_runtime_path_test_shared_library_link_flags + "-Xlinker --version-script=${TEST_SRC_DIR}/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.lds " + "-Xlinker --no-undefined -Xlinker --gc-sections -z noexecstack") + endif() + else() + set(onnxruntime_runtime_path_test_shared_library_link_flags + "-DEF:${TEST_SRC_DIR}/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.def") + endif() + + set_property(TARGET onnxruntime_runtime_path_test_shared_library APPEND_STRING PROPERTY LINK_FLAGS + ${onnxruntime_runtime_path_test_shared_library_link_flags}) + + set_target_properties(onnxruntime_runtime_path_test_shared_library PROPERTIES FOLDER "ONNXRuntimeTest") + source_group(TREE ${TEST_SRC_DIR} FILES ${onnxruntime_runtime_path_test_shared_library_src}) + endblock() + ################################################################# # test inference using shared lib - set(onnxruntime_shared_lib_test_LIBS onnxruntime_mocked_allocator onnxruntime_test_utils onnxruntime_common onnx_proto) + set(onnxruntime_shared_lib_test_LIBS + onnxruntime_mocked_allocator + onnxruntime_test_utils + onnxruntime_common + onnx_proto + onnxruntime_runtime_path_test_shared_library) + if(NOT WIN32) if(onnxruntime_USE_SNPE) list(APPEND onnxruntime_shared_lib_test_LIBS onnxruntime_providers_snpe) @@ -1978,8 +2018,7 @@ endif() # Build library that can be used with RegisterExecutionProviderLibrary and automatic EP selection # We need a shared lib build to use that as a dependency for the test library -# Currently we only have device discovery on Windows so no point building the test app on other platforms. -if (WIN32 AND onnxruntime_BUILD_SHARED_LIB AND +if (onnxruntime_BUILD_SHARED_LIB AND NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten" AND NOT onnxruntime_MINIMAL_BUILD) @@ -1991,7 +2030,7 @@ if (WIN32 AND onnxruntime_BUILD_SHARED_LIB AND "${TEST_SRC_DIR}/autoep/library/plugin_ep_utils.h") onnxruntime_add_shared_library_module(example_plugin_ep ${onnxruntime_autoep_test_library_src}) target_include_directories(example_plugin_ep PRIVATE ${REPO_ROOT}/include/onnxruntime/core/session) - target_link_libraries(example_plugin_ep PRIVATE onnxruntime) + target_link_libraries(example_plugin_ep PRIVATE onnxruntime ${GSL_TARGET}) if(UNIX) if (APPLE) @@ -2024,7 +2063,7 @@ if (WIN32 AND onnxruntime_BUILD_SHARED_LIB AND "${TEST_SRC_DIR}/autoep/library/example_plugin_ep_virt_gpu/ep.cc") onnxruntime_add_shared_library_module(example_plugin_ep_virt_gpu ${onnxruntime_autoep_test_example_plugin_ep_virt_gpu_src}) target_include_directories(example_plugin_ep_virt_gpu PRIVATE ${REPO_ROOT}/include/onnxruntime/core/session) - target_link_libraries(example_plugin_ep_virt_gpu PRIVATE onnxruntime) + target_link_libraries(example_plugin_ep_virt_gpu PRIVATE onnxruntime ${GSL_TARGET}) if(UNIX) if (APPLE) diff --git a/onnxruntime/core/platform/env.h b/onnxruntime/core/platform/env.h index e100d3626f76b..a038237da03a7 100644 --- a/onnxruntime/core/platform/env.h +++ b/onnxruntime/core/platform/env.h @@ -245,9 +245,9 @@ class Env { // \brief Gets the file path of the onnx runtime code // - // Used to help load other shared libraries that live in the same folder as the core code, for example - // The DNNL provider shared library. Without this path, the module won't be found on windows in all cases. - virtual PathString GetRuntimePath() const { return PathString(); } + // Used to help load other shared libraries that live in the same folder as the core code. + // For example, the DNNL provider shared library. + virtual PathString GetRuntimePath() const = 0; // \brief Get a pointer to a symbol from a dynamic library. // diff --git a/onnxruntime/core/platform/posix/env.cc b/onnxruntime/core/platform/posix/env.cc index fed697ea962bc..34b6b2de64a92 100644 --- a/onnxruntime/core/platform/posix/env.cc +++ b/onnxruntime/core/platform/posix/env.cc @@ -31,6 +31,7 @@ limitations under the License. #endif #include +#include #include #include #include @@ -554,6 +555,23 @@ class PosixEnv : public Env { return common::Status::OK(); } + PathString GetRuntimePath() const override { + // Use dladdr() to look up the file that contains an address from this binary. + const void* const address_from_this_binary = reinterpret_cast(Env::Default); + + if (Dl_info dl_info{}; + dladdr(address_from_this_binary, &dl_info) != 0 && dl_info.dli_fname != nullptr) { + LOGS_DEFAULT(VERBOSE) << "Getting runtime path as parent directory of binary: " << dl_info.dli_fname; + + auto runtime_path = std::filesystem::path{dl_info.dli_fname}; + runtime_path = std::filesystem::absolute(runtime_path); + runtime_path.remove_filename(); + return runtime_path; + } + + return PathString{}; + } + common::Status GetSymbolFromLibrary(void* handle, const std::string& symbol_name, void** symbol) const override { dlerror(); // clear any old error str diff --git a/onnxruntime/core/providers/cuda/version_script.lds b/onnxruntime/core/providers/cuda/version_script.lds index c02a8e4bcf724..7ce96e3e611c0 100644 --- a/onnxruntime/core/providers/cuda/version_script.lds +++ b/onnxruntime/core/providers/cuda/version_script.lds @@ -2,6 +2,8 @@ VERS_1.0 { global: GetProvider; + CreateEpFactories; + ReleaseEpFactory; _binary_*; # Hide everything else. diff --git a/onnxruntime/core/session/provider_bridge_ort.cc b/onnxruntime/core/session/provider_bridge_ort.cc index c6f65fe62ef94..19c636ba6aff1 100644 --- a/onnxruntime/core/session/provider_bridge_ort.cc +++ b/onnxruntime/core/session/provider_bridge_ort.cc @@ -1775,24 +1775,27 @@ struct ProviderHostImpl : ProviderHost { #if !defined(ORT_MINIMAL_BUILD) || defined(ORT_MINIMAL_BUILD_CUSTOM_OPS) Status LoadDynamicLibrary(onnxruntime::PathString library_name) override { return LoadDynamicLibraryFromProvider(library_name); }; #endif -} provider_host_; +} g_provider_host; #if defined(_MSC_VER) && !defined(__clang__) #pragma warning(pop) #endif struct ProviderSharedLibrary { - void Ensure() { - if (handle_) - return; + Status Initialize() { + if (handle_) { + return Status::OK(); + } auto full_path = Env::Default().GetRuntimePath() + PathString(LIBRARY_PREFIX ORT_TSTR("onnxruntime_providers_shared") LIBRARY_EXTENSION); - ORT_THROW_IF_ERROR(Env::Default().LoadDynamicLibrary(full_path, true /*shared_globals on unix*/, &handle_)); + ORT_RETURN_IF_ERROR(Env::Default().LoadDynamicLibrary(full_path, true /*shared_globals on unix*/, &handle_)); void (*PProvider_SetHost)(void*); - ORT_THROW_IF_ERROR(Env::Default().GetSymbolFromLibrary(handle_, "Provider_SetHost", (void**)&PProvider_SetHost)); + ORT_RETURN_IF_ERROR(Env::Default().GetSymbolFromLibrary(handle_, "Provider_SetHost", (void**)&PProvider_SetHost)); - PProvider_SetHost(&provider_host_); + PProvider_SetHost(&g_provider_host); + + return Status::OK(); } void Unload() { @@ -1819,7 +1822,7 @@ struct ProviderSharedLibrary { static ProviderSharedLibrary s_library_shared; bool InitProvidersSharedLibrary() try { - s_library_shared.Ensure(); + ORT_THROW_IF_ERROR(s_library_shared.Initialize()); return true; } catch (const std::exception&) { return false; @@ -1841,7 +1844,7 @@ Status ProviderLibrary::Load() { try { std::lock_guard lock{mutex_}; if (!provider_) { - s_library_shared.Ensure(); + ORT_RETURN_IF_ERROR(s_library_shared.Initialize()); if (absolute_) { // If filename_ is not absolute it should not be loaded. diff --git a/onnxruntime/test/autoep/library/example_plugin_ep/ep.cc b/onnxruntime/test/autoep/library/example_plugin_ep/ep.cc index 5d8245618dcd6..bc4e5a441a49a 100644 --- a/onnxruntime/test/autoep/library/example_plugin_ep/ep.cc +++ b/onnxruntime/test/autoep/library/example_plugin_ep/ep.cc @@ -381,7 +381,7 @@ void ORT_API_CALL ExampleEp::ReleaseNodeComputeInfosImpl(OrtEp* this_ptr, size_t num_node_compute_infos) noexcept { (void)this_ptr; for (size_t i = 0; i < num_node_compute_infos; i++) { - delete node_compute_infos[i]; + delete static_cast(node_compute_infos[i]); } } @@ -399,7 +399,7 @@ OrtStatus* ExampleEp::CreateEpContextNodes(gsl::span fused_nodes std::vector value_names; value_names.reserve(value_infos.size()); - for (const auto vi : value_infos) { + for (const auto& vi : value_infos) { value_names.push_back(vi.GetName()); } diff --git a/onnxruntime/test/autoep/library/example_plugin_ep/ep_allocator.h b/onnxruntime/test/autoep/library/example_plugin_ep/ep_allocator.h index febf8c7dbd8c1..f302599619ee9 100644 --- a/onnxruntime/test/autoep/library/example_plugin_ep/ep_allocator.h +++ b/onnxruntime/test/autoep/library/example_plugin_ep/ep_allocator.h @@ -5,6 +5,7 @@ #include "../plugin_ep_utils.h" +#include #include // from onnxruntime/core/framework/allocator_stats.h @@ -51,7 +52,16 @@ struct AllocatorStats { } }; -struct CustomAllocator : OrtAllocator { +// `OrtAllocator` is a C API struct. `BaseAllocator` is a minimal C++ struct which inherits from `OrtAllocator`. +// Notably, `BaseAllocator` has a virtual destructor to enable a derived class to be deleted through a `BaseAllocator` +// pointer. Allocators which need to be deleted through a base class pointer should inherit from `BaseAllocator`. +struct BaseAllocator : OrtAllocator { + virtual ~BaseAllocator() = default; +}; + +using AllocatorUniquePtr = std::unique_ptr; + +struct CustomAllocator : BaseAllocator { CustomAllocator(const OrtMemoryInfo* mem_info, const ApiPtrs& api_ptrs_in) : memory_info{mem_info}, api_ptrs{api_ptrs_in} { version = ORT_API_VERSION; diff --git a/onnxruntime/test/autoep/library/example_plugin_ep/ep_arena.h b/onnxruntime/test/autoep/library/example_plugin_ep/ep_arena.h index c8fd1db5dc007..ade03bb515136 100644 --- a/onnxruntime/test/autoep/library/example_plugin_ep/ep_arena.h +++ b/onnxruntime/test/autoep/library/example_plugin_ep/ep_arena.h @@ -551,7 +551,7 @@ class ArenaImpl { // un-assign chunks when StreamImpl::OnSessionRunEnd is called. std::unordered_map impl_to_stream_; - AllocatorStats stats_; + AllocatorStats stats_{}; const OrtApi& api_; const OrtEpApi& ep_api_; @@ -563,7 +563,7 @@ class ArenaImpl { ArenaImpl& operator=(ArenaImpl&&) = delete; }; -struct ArenaAllocator : OrtAllocator { +struct ArenaAllocator : BaseAllocator { static OrtStatus* CreateOrtArenaAllocator(AllocatorUniquePtr allocator, const OrtKeyValuePairs* options, const OrtApi& api, diff --git a/onnxruntime/test/autoep/library/example_plugin_ep/ep_factory.cc b/onnxruntime/test/autoep/library/example_plugin_ep/ep_factory.cc index a2a4848ed060f..85f9504b14a4d 100644 --- a/onnxruntime/test/autoep/library/example_plugin_ep/ep_factory.cc +++ b/onnxruntime/test/autoep/library/example_plugin_ep/ep_factory.cc @@ -12,7 +12,12 @@ #include "ep_stream_support.h" ExampleEpFactory::ExampleEpFactory(const char* ep_name, ApiPtrs apis, const OrtLogger& default_logger) - : OrtEpFactory{}, ApiPtrs(apis), default_logger_{default_logger}, ep_name_{ep_name} { + : OrtEpFactory{}, + ApiPtrs(apis), + default_logger_{default_logger}, + ep_name_{ep_name}, + default_memory_info_{nullptr}, + readonly_memory_info_{nullptr} { ort_version_supported = ORT_API_VERSION; // set to the ORT version we were compiled with. GetName = GetNameImpl; GetVendor = GetVendorImpl; @@ -34,43 +39,35 @@ ExampleEpFactory::ExampleEpFactory(const char* ep_name, ApiPtrs apis, const OrtL // setup the OrtMemoryInfo instances required by the EP. // We pretend the device the EP is running on is GPU. - OrtMemoryInfo* mem_info = nullptr; - auto* status = ort_api.CreateMemoryInfo_V2("ExampleEP GPU", OrtMemoryInfoDeviceType_GPU, - /*vendor*/ 0xBE57, /* device_id */ 0, - OrtDeviceMemoryType_DEFAULT, - /*alignment*/ 0, - // it is invalid to use OrtArenaAllocator as that is reserved for the - // internal ORT Arena implementation - OrtAllocatorType::OrtDeviceAllocator, - &mem_info); - assert(status == nullptr); // should never fail. - default_memory_info_ = MemoryInfoUniquePtr(mem_info, ort_api.ReleaseMemoryInfo); + default_memory_info_ = Ort::MemoryInfo{"ExampleEP GPU", + OrtMemoryInfoDeviceType_GPU, + /*vendor*/ 0xBE57, /* device_id */ 0, + OrtDeviceMemoryType_DEFAULT, + /*alignment*/ 0, + // it is invalid to use OrtArenaAllocator as that is reserved for the internal + // ORT Arena implementation + OrtAllocatorType::OrtDeviceAllocator}; // create data transfer for the device - const OrtMemoryDevice* device = ep_api.MemoryInfo_GetMemoryDevice(default_memory_info_.get()); + const OrtMemoryDevice* device = ep_api.MemoryInfo_GetMemoryDevice(default_memory_info_); data_transfer_impl_ = std::make_unique(apis, device); // create read-only allocator for use with initializers. same info as DEFAULT memory apart from the allocator type. - status = ort_api.CreateMemoryInfo_V2("ExampleEP GPU readonly", OrtMemoryInfoDeviceType_GPU, - /*vendor*/ 0xBE57, /* device_id */ 0, - OrtDeviceMemoryType_DEFAULT, - /*alignment*/ 0, - OrtAllocatorType::OrtReadOnlyAllocator, - &mem_info); - assert(status == nullptr); // should never fail. - - readonly_memory_info_ = MemoryInfoUniquePtr(mem_info, ort_api.ReleaseMemoryInfo); + readonly_memory_info_ = Ort::MemoryInfo{"ExampleEP GPU readonly", + OrtMemoryInfoDeviceType_GPU, + /*vendor*/ 0xBE57, /* device_id */ 0, + OrtDeviceMemoryType_DEFAULT, + /*alignment*/ 0, + OrtAllocatorType::OrtReadOnlyAllocator}; // HOST_ACCESSIBLE memory example. use the non-CPU device type so it's clear which device the memory is also // accessible from. we infer from the type of HOST_ACCESSIBLE that it's CPU accessible. - mem_info = nullptr; - status = ort_api.CreateMemoryInfo_V2("ExampleEP GPU pinned", OrtMemoryInfoDeviceType_GPU, - /*vendor*/ 0xBE57, /* device_id */ 0, - OrtDeviceMemoryType_HOST_ACCESSIBLE, - /*alignment*/ 0, - OrtAllocatorType::OrtDeviceAllocator, - &mem_info); - ort_api.ReleaseMemoryInfo(mem_info); + auto host_accessible_memory_info = Ort::MemoryInfo{"ExampleEP GPU pinned", + OrtMemoryInfoDeviceType_GPU, + /*vendor*/ 0xBE57, /* device_id */ 0, + OrtDeviceMemoryType_HOST_ACCESSIBLE, + /*alignment*/ 0, + OrtAllocatorType::OrtDeviceAllocator}; } /*static*/ @@ -136,8 +133,8 @@ OrtStatus* ORT_API_CALL ExampleEpFactory::GetSupportedDevicesImpl(OrtEpFactory* // register the allocator info required by the EP. // registering OrtMemoryInfo for host accessible memory would be done in an additional call. // OrtReadOnlyAllocator + OrtDeviceMemoryType_DEFAULT allocator for use with initializers is optional. - RETURN_IF_ERROR(factory->ep_api.EpDevice_AddAllocatorInfo(ep_device, factory->default_memory_info_.get())); - RETURN_IF_ERROR(factory->ep_api.EpDevice_AddAllocatorInfo(ep_device, factory->readonly_memory_info_.get())); + RETURN_IF_ERROR(factory->ep_api.EpDevice_AddAllocatorInfo(ep_device, factory->default_memory_info_)); + RETURN_IF_ERROR(factory->ep_api.EpDevice_AddAllocatorInfo(ep_device, factory->readonly_memory_info_)); ep_devices[num_ep_devices++] = ep_device; } @@ -215,8 +212,8 @@ OrtStatus* ORT_API_CALL ExampleEpFactory::CreateAllocatorImpl(OrtEpFactory* this auto& factory = *static_cast(this_ptr); *allocator = nullptr; - bool is_default_allocator = memory_info == factory.default_memory_info_.get(); - bool is_readonly_allocator = memory_info == factory.readonly_memory_info_.get(); + bool is_default_allocator = memory_info == factory.default_memory_info_; + bool is_readonly_allocator = memory_info == factory.readonly_memory_info_; if (!is_default_allocator && !is_readonly_allocator) { return factory.ort_api.CreateStatus(ORT_INVALID_ARGUMENT, @@ -244,7 +241,7 @@ OrtStatus* ORT_API_CALL ExampleEpFactory::CreateAllocatorImpl(OrtEpFactory* this std::lock_guard lock{factory.mutex_}; if (!factory.arena_allocator_) { - std::unique_ptr ep_allocator = std::make_unique(memory_info, factory); + AllocatorUniquePtr ep_allocator = std::make_unique(memory_info, factory); // initial shared allocator in environment does not have allocator options. // if the user calls CreateSharedAllocator they can provide options to configure the arena differently. diff --git a/onnxruntime/test/autoep/library/example_plugin_ep/ep_factory.h b/onnxruntime/test/autoep/library/example_plugin_ep/ep_factory.h index 6f6aaf2aaa9fc..196e67fc5c558 100644 --- a/onnxruntime/test/autoep/library/example_plugin_ep/ep_factory.h +++ b/onnxruntime/test/autoep/library/example_plugin_ep/ep_factory.h @@ -74,9 +74,8 @@ class ExampleEpFactory : public OrtEpFactory, public ApiPtrs { const std::string ep_version_{"0.1.0"}; // EP version // CPU allocator so we can control the arena behavior. optional as ORT always provides a CPU allocator if needed. - using MemoryInfoUniquePtr = std::unique_ptr>; - MemoryInfoUniquePtr default_memory_info_; - MemoryInfoUniquePtr readonly_memory_info_; // used for initializers + Ort::MemoryInfo default_memory_info_; + Ort::MemoryInfo readonly_memory_info_; // used for initializers bool arena_allocator_using_default_settings_{true}; std::unique_ptr arena_allocator_; // shared device allocator that uses an arena diff --git a/onnxruntime/test/autoep/library/example_plugin_ep/ep_stream_support.cc b/onnxruntime/test/autoep/library/example_plugin_ep/ep_stream_support.cc index c648474d4fad7..12e1b935be4b2 100644 --- a/onnxruntime/test/autoep/library/example_plugin_ep/ep_stream_support.cc +++ b/onnxruntime/test/autoep/library/example_plugin_ep/ep_stream_support.cc @@ -70,14 +70,17 @@ OrtStatus* ORT_API_CALL NotificationImpl::WaitOnDeviceImpl(_In_ OrtSyncNotificat void* handle = impl.ort_api.SyncStream_GetHandle(stream); static_cast(handle); + auto event = impl.event_; + static_cast(event); + // Setup the event or similar that will be activated on notification. // See CudaNotification or CannNotification for examples. // // e.g. - // CUDA: cudaStreamWaitEvent(static_cast(device_stream.GetHandle()), event_) - // CANN: aclrtStreamWaitEvent(static_cast(device_stream.GetHandle()), event_) + // CUDA: cudaStreamWaitEvent(static_cast(handle), event) + // CANN: aclrtStreamWaitEvent(static_cast(handle), event) // - // `event_` should be a member that is created in the ctor. + // `NotificationImpl::event_` should be a member that is created in the ctor. // The stream handle should come from the StreamImpl instance and can be the real type so no static_cast is needed. return nullptr; } @@ -85,11 +88,13 @@ OrtStatus* ORT_API_CALL NotificationImpl::WaitOnDeviceImpl(_In_ OrtSyncNotificat /*static*/ OrtStatus* ORT_API_CALL NotificationImpl::WaitOnHostImpl(_In_ OrtSyncNotificationImpl* this_ptr) noexcept { auto& impl = *static_cast(this_ptr); - static_cast(impl); + + auto event = impl.event_; + static_cast(event); // e.g. - // CUDA: cudaEventSynchronize(event_) - // CANN: aclrtSynchronizeEvent(event_) + // CUDA: cudaEventSynchronize(event) + // CANN: aclrtSynchronizeEvent(event) return nullptr; } diff --git a/onnxruntime/test/autoep/library/example_plugin_ep/ep_stream_support.h b/onnxruntime/test/autoep/library/example_plugin_ep/ep_stream_support.h index 847ed708c5ca7..5daff236089b3 100644 --- a/onnxruntime/test/autoep/library/example_plugin_ep/ep_stream_support.h +++ b/onnxruntime/test/autoep/library/example_plugin_ep/ep_stream_support.h @@ -15,7 +15,12 @@ class ExampleEpFactory; class StreamImpl : public OrtSyncStreamImpl, public ApiPtrs { public: StreamImpl(ExampleEpFactory& factory, const OrtEp* ep, const OrtKeyValuePairs* /*stream_options*/) - : ApiPtrs(factory), ep_{ep}, factory_{&factory} { + : ApiPtrs(factory), factory_{&factory} { + // `ep` is the EP instance if the stream is being created internally for inferencing. + // nullptr when the stream is created outside of an inference session for data copies. + // It is not used by this example implementation. + static_cast(ep); + ort_version_supported = ORT_API_VERSION; CreateNotification = CreateNotificationImpl; GetHandle = GetHandleImpl; @@ -34,9 +39,6 @@ class StreamImpl : public OrtSyncStreamImpl, public ApiPtrs { void* handle_{nullptr}; // use the real stream type, like cudaStream_t or aclrtStream, etc. - // EP instance if the stream is being created internally for inferencing. - // nullptr when the stream is created outside of an inference session for data copies. - const OrtEp* ep_; ExampleEpFactory* factory_{nullptr}; }; diff --git a/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep.cc b/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep.cc index 99a580f5577f7..590e494fbd787 100644 --- a/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep.cc +++ b/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep.cc @@ -48,9 +48,9 @@ struct AddNodeComputeInfo : OrtNodeComputeInfo { EpVirtualGpu& ep; }; -EpVirtualGpu::EpVirtualGpu(EpFactoryVirtualGpu& factory, const EpVirtualGpu::Config& config, const OrtLogger& logger) +EpVirtualGpu::EpVirtualGpu(EpFactoryVirtualGpu& factory, const EpVirtualGpu::Config& config, + const OrtLogger& logger) : OrtEp{}, // explicitly call the struct ctor to ensure all optional values are default initialized - factory_{factory}, config_{config}, ort_api_{factory.GetOrtApi()}, ep_api_{factory.GetEpApi()}, @@ -204,7 +204,7 @@ OrtStatus* EpVirtualGpu::CreateEpContextNodes(gsl::span fused_no std::vector value_names; value_names.reserve(value_infos.size()); - for (const auto vi : value_infos) { + for (const auto& vi : value_infos) { value_names.push_back(vi.GetName()); } @@ -274,7 +274,7 @@ void ORT_API_CALL EpVirtualGpu::ReleaseNodeComputeInfosImpl(OrtEp* this_ptr, size_t num_node_compute_infos) noexcept { (void)this_ptr; for (size_t i = 0; i < num_node_compute_infos; i++) { - delete node_compute_infos[i]; + delete static_cast(node_compute_infos[i]); } } diff --git a/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep.h b/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep.h index da7bb05d79e62..1a217c6e94370 100644 --- a/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep.h +++ b/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep.h @@ -51,7 +51,6 @@ class EpVirtualGpu : public OrtEp { OrtStatus* CreateEpContextNodes(gsl::span fused_nodes, /*out*/ gsl::span ep_context_nodes); - EpFactoryVirtualGpu& factory_; Config config_{}; const OrtApi& ort_api_; const OrtEpApi& ep_api_; diff --git a/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep_factory.cc b/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep_factory.cc index da8c2b3ed7c6b..d841e70187f70 100644 --- a/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep_factory.cc +++ b/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep_factory.cc @@ -11,13 +11,13 @@ #include "../plugin_ep_utils.h" EpFactoryVirtualGpu::EpFactoryVirtualGpu(const OrtApi& ort_api, const OrtEpApi& ep_api, - const OrtModelEditorApi& model_editor_api, const OrtLogger& default_logger) + const OrtModelEditorApi& model_editor_api, + const OrtLogger& /*default_logger*/) : OrtEpFactory{}, ort_api_(ort_api), ep_api_(ep_api), model_editor_api_(model_editor_api), - allow_virtual_devices_{false}, - default_logger_{default_logger} { + allow_virtual_devices_{false} { ort_version_supported = ORT_API_VERSION; // set to the ORT version we were compiled with. GetName = GetNameImpl; GetVendor = GetVendorImpl; diff --git a/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep_factory.h b/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep_factory.h index 2357b3676aa79..1d708d9b40963 100644 --- a/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep_factory.h +++ b/onnxruntime/test/autoep/library/example_plugin_ep_virt_gpu/ep_factory.h @@ -73,7 +73,6 @@ class EpFactoryVirtualGpu : public OrtEpFactory { const OrtEpApi& ep_api_; const OrtModelEditorApi& model_editor_api_; bool allow_virtual_devices_{false}; - const OrtLogger& default_logger_; OrtHardwareDevice* virtual_hw_device_{}; const std::string ep_name_{"EpVirtualGpu"}; const std::string vendor_{"Contoso2"}; // EP vendor name diff --git a/onnxruntime/test/autoep/library/plugin_ep_utils.h b/onnxruntime/test/autoep/library/plugin_ep_utils.h index 2024c5185b0d6..efa9963ef2760 100644 --- a/onnxruntime/test/autoep/library/plugin_ep_utils.h +++ b/onnxruntime/test/autoep/library/plugin_ep_utils.h @@ -47,11 +47,12 @@ #define EP_FILE __FILE__ #endif -#define LOG(level, ...) \ - do { \ - std::ostringstream ss; \ - ss << __VA_ARGS__; \ - api_.Logger_LogMessage(&logger_, ORT_LOGGING_LEVEL_##level, ss.str().c_str(), EP_FILE, __LINE__, __FUNCTION__); \ +#define LOG(level, ...) \ + do { \ + std::ostringstream ss; \ + ss << __VA_ARGS__; \ + Ort::Status ignored{api_.Logger_LogMessage(&logger_, ORT_LOGGING_LEVEL_##level, ss.str().c_str(), \ + EP_FILE, __LINE__, __FUNCTION__)}; \ } while (false) #define RETURN_ERROR(code, ...) \ @@ -72,8 +73,6 @@ struct ApiPtrs { const OrtModelEditorApi& model_editor_api; }; -using AllocatorUniquePtr = std::unique_ptr>; - // Helper to release Ort one or more objects obtained from the public C API at the end of their scope. template struct DeferOrtRelease { diff --git a/onnxruntime/test/autoep/test_allocators.cc b/onnxruntime/test/autoep/test_allocators.cc index 3c73237708828..b90546358d7ba 100644 --- a/onnxruntime/test/autoep/test_allocators.cc +++ b/onnxruntime/test/autoep/test_allocators.cc @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -// registration/selection is only supported on windows as there's no device discovery on other platforms -#ifdef _WIN32 - #include #include #include @@ -134,5 +131,3 @@ TEST(SharedAllocators, GetSharedAllocator) { } // namespace test } // namespace onnxruntime - -#endif // _WIN32 diff --git a/onnxruntime/test/autoep/test_autoep_utils.cc b/onnxruntime/test/autoep/test_autoep_utils.cc index d8404e5161e0a..07e7c12c4ad99 100644 --- a/onnxruntime/test/autoep/test_autoep_utils.cc +++ b/onnxruntime/test/autoep/test_autoep_utils.cc @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -// registration/selection is only supported on windows as there's no device discovery on other platforms -#ifdef _WIN32 - #include "test/autoep/test_autoep_utils.h" #include @@ -11,29 +8,22 @@ #include "core/session/onnxruntime_cxx_api.h" #include "test/util/include/api_asserts.h" +#include "test/util/include/file_util.h" namespace onnxruntime { namespace test { -Utils::ExamplePluginInfo::ExamplePluginInfo(const ORTCHAR_T* lib_path, const char* reg_name, const char* ep_name) - : library_path(lib_path), registration_name(reg_name), ep_name(ep_name) {} +Utils::ExamplePluginInfo::ExamplePluginInfo(std::filesystem::path lib_path, const char* reg_name, const char* ep_name) + : library_path(std::move(lib_path)), registration_name(reg_name), ep_name(ep_name) {} const Utils::ExamplePluginInfo Utils::example_ep_info( -#if _WIN32 - ORT_TSTR("example_plugin_ep.dll"), -#else - ORT_TSTR("libexample_plugin_ep.so"), -#endif + GetSharedLibraryFileName(ORT_TSTR("example_plugin_ep")), // The example_plugin_ep always uses the registration name as the EP name. "example_ep", "example_ep"); const Utils::ExamplePluginInfo Utils::example_ep_virt_gpu_info( -#if _WIN32 - ORT_TSTR("example_plugin_ep_virt_gpu.dll"), -#else - "libexample_plugin_ep_virt_gpu.so", -#endif + GetSharedLibraryFileName(ORT_TSTR("example_plugin_ep_virt_gpu")), "example_plugin_ep_virt_gpu.virtual", // Ends in ".virtual" to allow creation of virtual devices. // This EP's name is hardcoded to the following "EpVirtualGpu"); @@ -69,11 +59,9 @@ void Utils::RegisterAndGetExampleEp(Ort::Env& env, const ExamplePluginInfo& ep_i ASSERT_NE(example_ep, nullptr); registered_ep = RegisteredEpDeviceUniquePtr(example_ep, [&env, &ep_info, c_api](const OrtEpDevice* /*ep*/) { - c_api.UnregisterExecutionProviderLibrary(env, ep_info.registration_name.c_str()); + Ort::Status ignored{c_api.UnregisterExecutionProviderLibrary(env, ep_info.registration_name.c_str())}; }); } } // namespace test } // namespace onnxruntime - -#endif // _WIN32 diff --git a/onnxruntime/test/autoep/test_autoep_utils.h b/onnxruntime/test/autoep/test_autoep_utils.h index f6b5e3623505f..ae930644779a0 100644 --- a/onnxruntime/test/autoep/test_autoep_utils.h +++ b/onnxruntime/test/autoep/test_autoep_utils.h @@ -15,7 +15,7 @@ using RegisteredEpDeviceUniquePtr = std::unique_ptr #include #include @@ -73,5 +70,3 @@ TEST(OrtEpLibrary, DataTransfer) { } // namespace test } // namespace onnxruntime - -#endif // _WIN32 diff --git a/onnxruntime/test/autoep/test_execution.cc b/onnxruntime/test/autoep/test_execution.cc index 42e3aa742d9f6..4c88d3ec2e0f3 100644 --- a/onnxruntime/test/autoep/test_execution.cc +++ b/onnxruntime/test/autoep/test_execution.cc @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -// registration/selection is only supported on windows as there's no device discovery on other platforms -#ifdef _WIN32 - #include // #include #include @@ -243,5 +240,3 @@ TEST(OrtEpLibrary, PluginEp_GenEpContextModel_ErrorOutputModelExists_AutoGenOutp } } // namespace test } // namespace onnxruntime - -#endif // _WIN32 diff --git a/onnxruntime/test/autoep/test_registration.cc b/onnxruntime/test/autoep/test_registration.cc index 9a0219b57092c..7415c5e138874 100644 --- a/onnxruntime/test/autoep/test_registration.cc +++ b/onnxruntime/test/autoep/test_registration.cc @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -// registration/selection is only supported on windows as there's no device discovery on other platforms -#ifdef _WIN32 - #include #include #include @@ -36,7 +33,7 @@ TEST(OrtEpLibrary, LoadUnloadPluginLibrary) { ASSERT_ORTSTATUS_OK(Ort::GetApi().GetEpDevices(*ort_env, &ep_devices, &num_devices)); // should be one device for the example EP auto num_test_ep_devices = std::count_if(ep_devices, ep_devices + num_devices, - [&ep_name, &c_api](const OrtEpDevice* device) { + [&](const OrtEpDevice* device) { return c_api->EpDevice_EpName(device) == ep_name; }); ASSERT_EQ(num_test_ep_devices, 1) << "Expected an OrtEpDevice to have been created by the test library."; @@ -58,7 +55,7 @@ TEST(OrtEpLibrary, LoadUnloadPluginLibraryCxxApi) { // should be one device for the example EP auto test_ep_device = std::find_if(ep_devices.begin(), ep_devices.end(), - [&ep_name](Ort::ConstEpDevice& device) { + [&](Ort::ConstEpDevice& device) { return device.EpName() == ep_name; }); ASSERT_NE(test_ep_device, ep_devices.end()) << "Expected an OrtEpDevice to have been created by the test library."; @@ -82,7 +79,9 @@ TEST(OrtEpLibrary, LoadUnloadPluginLibraryCxxApi) { ASSERT_NE(device.Vendor(), nullptr); Ort::ConstKeyValuePairs device_metadata = device.Metadata(); std::unordered_map metadata_entries = device_metadata.GetKeyValuePairs(); +#if defined(_WIN32) ASSERT_GT(metadata_entries.size(), 0); // should have at least SPDRP_HARDWAREID on Windows +#endif // and this should unload it without throwing ort_env->UnregisterExecutionProviderLibrary(registration_name.c_str()); @@ -95,12 +94,12 @@ TEST(OrtEpLibrary, LoadUnloadPluginVirtGpuLibraryCxxApi) { const std::string& registration_name = "example_plugin_ep_virt_gpu"; const std::string& ep_name = Utils::example_ep_virt_gpu_info.ep_name; - auto get_plugin_ep_devices = [&ep_name]() -> std::vector { + auto get_plugin_ep_devices = [&]() -> std::vector { std::vector all_ep_devices = ort_env->GetEpDevices(); std::vector ep_devices; std::copy_if(all_ep_devices.begin(), all_ep_devices.end(), std::back_inserter(ep_devices), - [&ep_name](Ort::ConstEpDevice& device) { + [&](Ort::ConstEpDevice& device) { return device.EpName() == ep_name; }); @@ -170,5 +169,3 @@ TEST(OrtEpLibrary, LoadUnloadPluginVirtGpuLibraryCxxApi) { } } // namespace test } // namespace onnxruntime - -#endif // _WIN32 diff --git a/onnxruntime/test/autoep/test_selection.cc b/onnxruntime/test/autoep/test_selection.cc index 106a9d474c2b2..4a17668138105 100644 --- a/onnxruntime/test/autoep/test_selection.cc +++ b/onnxruntime/test/autoep/test_selection.cc @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -// registration/selection is only supported on windows as there's no device discovery on other platforms -#ifdef _WIN32 - #include // #include #include @@ -20,6 +17,7 @@ #include "test/shared_lib/utils.h" #include "test/util/include/api_asserts.h" #include "test/util/include/asserts.h" +#include "test/util/include/file_util.h" extern std::unique_ptr ort_env; @@ -178,7 +176,9 @@ TEST(AutoEpSelection, CpuEP) { TEST(AutoEpSelection, CudaEP) { Ort::KeyValuePairs provider_options; provider_options.Add("prefer_nhwc", "1"); - RunBasicTest(kCudaExecutionProvider, "onnxruntime_providers_cuda", provider_options); + const auto cuda_ep_lib_path = + std::filesystem::path{GetSharedLibraryFileName(ORT_TSTR("onnxruntime_providers_cuda"))}; + RunBasicTest(kCudaExecutionProvider, cuda_ep_lib_path, provider_options); } #endif @@ -512,5 +512,3 @@ TEST(AutoEpSelection, PolicyDelegateReturnsError) { } // namespace test } // namespace onnxruntime - -#endif // _WIN32 diff --git a/onnxruntime/test/platform/env_test.cc b/onnxruntime/test/platform/env_test.cc index 9ea8586af693b..be7b0ce397c48 100644 --- a/onnxruntime/test/platform/env_test.cc +++ b/onnxruntime/test/platform/env_test.cc @@ -3,6 +3,7 @@ #include "core/platform/env.h" +#include #include #include "gtest/gtest.h" @@ -53,5 +54,6 @@ TEST(PlatformEnvTest, GetErrnoInfo) { #pragma warning(pop) #endif } + } // namespace test } // namespace onnxruntime diff --git a/onnxruntime/test/shared_lib/runtime_path_test_shared_library/readme.md b/onnxruntime/test/shared_lib/runtime_path_test_shared_library/readme.md new file mode 100644 index 0000000000000..d48d8531662be --- /dev/null +++ b/onnxruntime/test/shared_lib/runtime_path_test_shared_library/readme.md @@ -0,0 +1,9 @@ +`onnxruntime_runtime_path_test_shared_library` is a minimal shared library that exposes a public function which makes a +call to `onnxruntime::Env::GetRuntimePath()` and returns the result. + +We want to ensure that `Env::GetRuntimePath()` returns the directory of the shared library it is called from, e.g., +the onnxruntime shared library. It is used to get paths to other files which may be co-located with the shared library. + +Directly calling `Env::GetRuntimePath()` from a unit test program where it is statically linked in is a slightly +different setup. We use this minimal shared library to test the `Env::GetRuntimePath()` functionality in a setup that +is closer to the real-world usage. diff --git a/onnxruntime/test/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.cc b/onnxruntime/test/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.cc new file mode 100644 index 0000000000000..f2198dd014b6e --- /dev/null +++ b/onnxruntime/test/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.cc @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "runtime_path_test_shared_library.h" + +#include +#include + +#include "core/platform/env.h" +#include "core/common/logging/logging.h" +#include "core/platform/logging/make_platform_default_log_sink.h" + +namespace { + +std::basic_string InitializeAndGetRuntimePath() { + using namespace onnxruntime; + + const bool default_filter_user_data = false; + const std::string default_logger_id = "DefaultLogger"; + auto logging_manager = logging::LoggingManager(logging::MakePlatformDefaultLogSink(), + logging::Severity::kVERBOSE, + default_filter_user_data, + logging::LoggingManager::InstanceType::Default, + &default_logger_id); + + auto runtime_path = Env::Default().GetRuntimePath(); + + return runtime_path; +} + +} // namespace + +extern "C" const PATH_CHAR_T* OrtTestGetSharedLibraryRuntimePath(void) { +#if !defined(ORT_NO_EXCEPTIONS) + try { +#endif + + static const auto runtime_path = InitializeAndGetRuntimePath(); + return runtime_path.c_str(); + +#if !defined(ORT_NO_EXCEPTIONS) + } catch (const std::exception& e) { + std::cerr << __FUNCTION__ << " - caught exception: " << e.what() << "\n"; + return nullptr; + } +#endif +} diff --git a/onnxruntime/test/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.def b/onnxruntime/test/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.def new file mode 100644 index 0000000000000..496faee610075 --- /dev/null +++ b/onnxruntime/test/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.def @@ -0,0 +1,3 @@ +LIBRARY "onnxruntime_runtime_path_test_shared_library.dll" +EXPORTS + OrtTestGetSharedLibraryRuntimePath @1 diff --git a/onnxruntime/test/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.h b/onnxruntime/test/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.h new file mode 100644 index 0000000000000..072c3e473c8ca --- /dev/null +++ b/onnxruntime/test/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.h @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +// To make symbols visible on macOS/iOS +#if defined(__APPLE__) +#define EXPORT_SYMBOL __attribute__((visibility("default"))) +#else +#define EXPORT_SYMBOL +#endif + +#if defined(_WIN32) +#define PATH_CHAR_T wchar_t +#else +#define PATH_CHAR_T char +#endif + +extern "C" { +// +// Public symbols +// + +// Gets the runtime path of the shared library - i.e., the shared library file's parent directory. +EXPORT_SYMBOL const PATH_CHAR_T* OrtTestGetSharedLibraryRuntimePath(void); +} diff --git a/onnxruntime/test/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.lds b/onnxruntime/test/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.lds new file mode 100644 index 0000000000000..4daec4ff8fb5a --- /dev/null +++ b/onnxruntime/test/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.lds @@ -0,0 +1,6 @@ +VERS_1.0.0 { + global: + OrtTestGetSharedLibraryRuntimePath; + local: + *; +}; diff --git a/onnxruntime/test/shared_lib/test_runtime_path.cc b/onnxruntime/test/shared_lib/test_runtime_path.cc new file mode 100644 index 0000000000000..621d006a8659a --- /dev/null +++ b/onnxruntime/test/shared_lib/test_runtime_path.cc @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include + +#include "gtest/gtest.h" + +#include "core/common/path_string.h" + +#include "test/shared_lib/runtime_path_test_shared_library/runtime_path_test_shared_library.h" +#include "test/util/include/file_util.h" + +namespace onnxruntime::test { + +namespace { +bool IsDirectorySeparator(PathChar c) { + constexpr std::array dir_separators{ORT_TSTR('/'), std::filesystem::path::preferred_separator}; + return std::find(dir_separators.begin(), dir_separators.end(), c) != dir_separators.end(); +} +} // namespace + +TEST(GetRuntimePathFromSharedLibraryTest, Basic) { + const auto* runtime_path_cstr = OrtTestGetSharedLibraryRuntimePath(); + ASSERT_NE(runtime_path_cstr, nullptr); + + const auto runtime_path_str = std::basic_string_view{runtime_path_cstr}; + ASSERT_FALSE(runtime_path_str.empty()); + ASSERT_TRUE(IsDirectorySeparator(runtime_path_str.back())); + + const auto runtime_path = std::filesystem::path{runtime_path_str}; + ASSERT_TRUE(runtime_path.is_absolute()); + + // Check that the runtime path contains the shared library file. + const auto shared_library_file_name = + GetSharedLibraryFileName(ORT_TSTR("onnxruntime_runtime_path_test_shared_library")); + + const auto shared_library_path = runtime_path / shared_library_file_name; + + // Get canonical path to ensure it exists and resolve any symlinks. + std::error_code ec{}; + const auto canonical_shared_library_path = std::filesystem::canonical(shared_library_path, ec); + ASSERT_FALSE(ec) << "Failed to get canonical path to shared library file '" << shared_library_path + << "'. Error: " << ec.message(); + + ASSERT_TRUE(std::filesystem::is_regular_file(canonical_shared_library_path)); +} + +} // namespace onnxruntime::test diff --git a/onnxruntime/test/util/file_util.cc b/onnxruntime/test/util/file_util.cc index a4507ac0d1db2..bb7d3a472477d 100644 --- a/onnxruntime/test/util/file_util.cc +++ b/onnxruntime/test/util/file_util.cc @@ -12,6 +12,18 @@ namespace onnxruntime { namespace test { +PathString GetSharedLibraryFileName(const PathString& base_library_name) { +#if defined(_WIN32) + constexpr auto kPrefix{ORT_TSTR("")}, kSuffix{ORT_TSTR(".dll")}; +#elif defined(__APPLE__) + constexpr auto kPrefix{ORT_TSTR("lib")}, kSuffix{ORT_TSTR(".dylib")}; +#else + constexpr auto kPrefix{ORT_TSTR("lib")}, kSuffix{ORT_TSTR(".so")}; +#endif + + return PathString{kPrefix} + base_library_name + kSuffix; +} + void DeleteFileFromDisk(const ORTCHAR_T* path) { #ifdef _WIN32 ASSERT_EQ(TRUE, DeleteFileW(path)); diff --git a/onnxruntime/test/util/include/file_util.h b/onnxruntime/test/util/include/file_util.h index 9100f2ce166a9..3c9ee5f6d3a21 100644 --- a/onnxruntime/test/util/include/file_util.h +++ b/onnxruntime/test/util/include/file_util.h @@ -8,6 +8,11 @@ namespace onnxruntime { namespace test { + +// Creates a platform-specific shared library file name from a base library name. +// E.g., "onnxruntime" -> "libonnxruntime.so". +PathString GetSharedLibraryFileName(const PathString& base_library_name); + void CreateTestFile(FILE*& out, std::basic_string& filename_template); void CreateTestFile(int& out, std::basic_string& filename_template); void DeleteFileFromDisk(const ORTCHAR_T* path); @@ -39,4 +44,4 @@ class ScopedFileDeleter { }; } // namespace test -} // namespace onnxruntime \ No newline at end of file +} // namespace onnxruntime diff --git a/tools/ci_build/build.py b/tools/ci_build/build.py index c316b0db6a1b0..5d532753d930c 100644 --- a/tools/ci_build/build.py +++ b/tools/ci_build/build.py @@ -1597,19 +1597,30 @@ def dump_logs_on_failure(): else: run_adb_shell(f"{device_dir}/onnx_test_runner {device_dir}/test") - # run shared_lib_test if necessary + # run shared library tests if applicable if args.build_shared_lib: - adb_push("libonnxruntime.so", device_dir, cwd=cwd) - adb_push("onnxruntime_shared_lib_test", device_dir, cwd=cwd) - adb_push("libcustom_op_library.so", device_dir, cwd=cwd) - adb_push("libcustom_op_get_const_input_test_library.so", device_dir, cwd=cwd) - adb_push("onnxruntime_customopregistration_test", device_dir, cwd=cwd) - adb_shell(f"chmod +x {device_dir}/onnxruntime_shared_lib_test") - adb_shell(f"chmod +x {device_dir}/onnxruntime_customopregistration_test") - run_adb_shell(f"LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{device_dir} {device_dir}/onnxruntime_shared_lib_test") - run_adb_shell( - f"LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{device_dir} {device_dir}/onnxruntime_customopregistration_test" - ) + shared_library_test_program_names = [ + "onnxruntime_autoep_test", + "onnxruntime_customopregistration_test", + "onnxruntime_shared_lib_test", + ] + + shared_libraries = [ + "libcustom_op_get_const_input_test_library.so", + "libcustom_op_library.so", + "libexample_plugin_ep_virt_gpu.so", + "libexample_plugin_ep.so", + "libonnxruntime_runtime_path_test_shared_library.so", + "libonnxruntime.so", + ] + + for file_to_copy in shared_library_test_program_names + shared_libraries: + adb_push(file_to_copy, device_dir, cwd=cwd) + + # run test programs + for test_program_name in shared_library_test_program_names: + adb_shell(f"chmod +x {device_dir}/{test_program_name}") + run_adb_shell(f"LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{device_dir} {device_dir}/{test_program_name}") all_android_tests_passed = True