Skip to content

Commit b6f111f

Browse files
committed
Build and ship the OpenVINO backend in the Windows wheels
The OpenVINO delegate lets ExecuTorch run models on Intel CPUs, GPUs and NPUs. Until now the prebuilt delegate shipped only in the Linux wheels. The Windows wheels left it out, even though Intel publishes an OpenVINO runtime for Windows x86-64, so Windows users had to build ExecuTorch from source. This change builds the delegate into the Windows wheels, CPU and CUDA, the same way the Linux wheels carry it. setup.py enables EXECUTORCH_BUILD_OPENVINO on Windows and packages the delegate's import library (.lib) beside the other Windows DLLs. The build file passes /EHsc and /GR when CMake reports an MSVC style compiler, which includes the clang-cl the wheels use, and -frtti and -fexceptions otherwise. The delegate loads the OpenVINO C library at run time, so the build needs no OpenVINO SDK. That loader used dlopen and a fixed libopenvino_c.so name. It is now split by platform. On Windows it reads OPENVINO_LIB_PATH with _wgetenv and loads with LoadLibraryExW, so a path outside the active code page still works, and it reports load errors as text. A bare name such as openvino_c.dll is searched with LOAD_LIBRARY_SEARCH_DEFAULT_DIRS, which leaves out the current folder and PATH, so a DLL planted in the folder a program runs from is never loaded. A path is made absolute and also searches its own folder, so the runtime's dependencies, openvino.dll and tbb12.dll, load from beside it. The Python package already finds the pip installed runtime and passes its full path; it now looks for openvino_c.dll on Windows. The delegate is a global, so on Windows its destructor keeps the runtime loaded rather than calling FreeLibrary inside DllMain. pip install "executorch[openvino]" now installs the runtime on Windows x86-64 too. The extra installs nothing on Python 3.15, where OpenVINO publishes no build yet, so the command no longer fails there on Linux or Windows. The Windows wheel smoke tests, CPU and CUDA, check that the delegate registers. They then install the openvino extra and the quantizer the partitioner needs, and in a new process require that the delegate loads the runtime and that a model it runs matches eager. They also check that a bare name does not load the runtime from the current folder. The shared model check now asserts that the program is really delegated, and loads it from memory, because Windows does not delete a file the loaded program keeps open. The shared library check requires the delegate on Linux and Windows wheels. The docs, the README and the PyPI description list Windows, and the backend README explains where Windows looks for the runtime's dependencies. Test plan: - Built the loader for Windows with a MinGW cross compiler and ran it under Wine with the OpenVINO 2025.4.1 Windows DLLs. A full path, a path with forward slashes and a relative path load the runtime and find the CPU device. A bare name ignores openvino_c.dll in the current folder, which the previous loader loaded. Unloading the delegate's DLL leaves the runtime loaded, where the previous loader freed it. - On Linux, built the delegate against the wheel's C++ SDK and ran the new OpenVINO check with OpenVINO 2025.4.1 and nncf 3.1.0: the runtime loads and the model matches eager exactly. The check fails when the runtime cannot load, when nncf is missing, or when nothing is delegated. - Ran black, usort, flake8, clang-format and cmake-format on the changed files. - On native Windows, the new checks run in this pull request's Windows wheel jobs.
1 parent b1beef3 commit b6f111f

17 files changed

Lines changed: 353 additions & 66 deletions

‎.ci/scripts/wheel/test_base.py‎

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@
88
import os
99
import subprocess
1010
import sys
11-
import tempfile
1211
from dataclasses import dataclass
1312
from functools import cache
14-
from pathlib import Path
1513
from typing import List
1614

1715

@@ -43,7 +41,7 @@ class ModelTest:
4341
backend: Backend
4442

4543

46-
def test_a_model_runs_through_the_openvino_delegate() -> None:
44+
def test_a_model_runs_through_the_openvino_delegate(required: bool = False) -> None:
4745
"""Export a model to the OpenVINO delegate and run it, comparing against eager.
4846
4947
The checks elsewhere prove the delegate library ships, owns its symbols and registers
@@ -53,7 +51,8 @@ def test_a_model_runs_through_the_openvino_delegate() -> None:
5351
The OpenVINO runtime is not part of the wheel. It comes from the openvino extra, so a
5452
default install cannot run this and says so rather than passing quietly. Where the runtime
5553
is present the export and the comparison are the point of the check, and a failure there
56-
is a real one.
54+
is a real one. A caller that installed the export stack passes required=True, so a missing
55+
piece fails instead of skipping.
5756
"""
5857
import torch
5958
from executorch.exir import to_edge_transform_and_lower
@@ -67,6 +66,8 @@ def test_a_model_runs_through_the_openvino_delegate() -> None:
6766
try:
6867
from executorch.backends.openvino.partitioner import OpenvinoPartitioner
6968
except ImportError as error:
69+
if required:
70+
raise
7071
print(
7172
f"SKIP: the OpenVINO export stack is not installed, so the delegate cannot "
7273
f"execute here ({error}). The wheel ships the adapter only; the runtime and the "
@@ -88,13 +89,16 @@ def forward(self, x, y):
8889
partitioner=[OpenvinoPartitioner([CompileSpec("device", b"CPU")])],
8990
)
9091
program = lowered.to_executorch()
92+
delegates = [d.id for d in program.executorch_program.execution_plan[0].delegates]
93+
assert "OpenvinoBackend" in delegates, (
94+
f"the partitioner did not hand the model to OpenVINO (delegates: {delegates}), so "
95+
f"running it would test the portable kernels instead of the delegate"
96+
)
9197

92-
with tempfile.TemporaryDirectory() as work_dir:
93-
pte = Path(work_dir) / "add_openvino.pte"
94-
pte.write_bytes(program.buffer)
95-
96-
method = Runtime.get().load_program(pte).load_method("forward")
97-
actual = method.execute(list(example))[0]
98+
# From memory rather than a temporary file, which the loaded program keeps open and
99+
# Windows then refuses to delete.
100+
method = Runtime.get().load_program(program.buffer).load_method("forward")
101+
actual = method.execute(list(example))[0]
98102

99103
difference = (actual - eager).abs().max().item()
100104
assert difference == 0.0, (

‎.ci/scripts/wheel/test_cuda_windows.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,15 @@ def export(destination: str) -> None:
640640
with tempfile.TemporaryDirectory() as work_dir:
641641
test_cpp_sdk.run_tests(Path(work_dir))
642642
import test_base
643+
from executorch.extension.pybindings.portable_lib import (
644+
_get_registered_backend_names,
645+
)
646+
647+
registered = _get_registered_backend_names()
648+
assert (
649+
"OpenvinoBackend" in registered
650+
), f"OpenvinoBackend not found in registered backends: {registered}"
651+
print("✓ OpenvinoBackend is registered")
643652

644653
test_base.test_cmsis_nn_install()
645654
# The same model run the CPU row ends with, through the Python bindings.
@@ -650,3 +659,6 @@ def export(destination: str) -> None:
650659
test_windows.run_tests(
651660
model_tests=[ModelTest(model=Model.Mv3, backend=Backend.Xnnpack)]
652661
)
662+
663+
# Last, because it installs packages the wheel does not declare.
664+
test_windows.test_the_openvino_delegate_runs()

‎.ci/scripts/wheel/test_shared_libraries.py‎

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,11 @@
149149
# The delegate's own entry points. A second definer means the delegate is compiled
150150
# into the Python extension as well, which would register it twice in one process.
151151
_QNN_BACKEND_SYMBOLS = ("QnnExecuTorchBackendRegister",)
152-
_OPENVINO_BACKEND_SYMBOLS = ("executorch::backends::openvino::OpenvinoBackend",)
152+
# A member rather than the bare class name, because the PE reader matches MSVC decorated
153+
# names, which spell a member as ?is_available@OpenvinoBackend@... and never the class alone.
154+
_OPENVINO_BACKEND_SYMBOLS = (
155+
"executorch::backends::openvino::OpenvinoBackend::is_available",
156+
)
153157

154158
_BUNDLED_XNNPACK_SYMBOLS = ("xnn_create_runtime_v4",)
155159

@@ -1097,17 +1101,18 @@ def _wheel_cuda_train() -> str:
10971101
# wheel is inspected, and a row cannot call that at import time.
10981102
_REQUIRED_ON_A_CUDA_WHEEL = "cuda-wheel-only"
10991103

1100-
# Marker for a row whose owner every Linux wheel carries and no macOS wheel does.
1101-
_REQUIRED_ON_LINUX = "linux-only"
1104+
# Marker for a row whose owner every Linux and Windows wheel carries and no macOS wheel
1105+
# does.
1106+
_REQUIRED_ON_LINUX_AND_WINDOWS = "linux-and-windows-only"
11021107
# Marker for a row whose owner every macOS wheel carries and no other wheel does.
11031108
# The Core ML delegate is built on every macOS wheel, so its presence is decidable
11041109
# from the installed wheel and a macOS wheel that dropped it should fail rather than
11051110
# quietly regress to folding it into the Python extension.
11061111
_REQUIRED_ON_MACOS = "macos-only"
1107-
# Narrower than the marker above on purpose. The Qualcomm SDK the delegate builds
1108-
# against is published for Linux x86_64 only, so the aarch64 Linux wheels ship no such
1109-
# library and demanding it there would fail a wheel that is correct. Reusing the
1110-
# Linux-wide marker would do exactly that.
1112+
# Narrower than the Linux and Windows marker on purpose. The Qualcomm SDK the delegate
1113+
# builds against is published for Linux x86_64 only, so the aarch64 Linux wheels ship no
1114+
# such library and demanding it there would fail a wheel that is correct. Reusing that
1115+
# marker would do exactly that.
11111116
_REQUIRED_ON_LINUX_X86 = "linux-x86-only"
11121117
# Marker for a row whose owner every aarch64 wheel carries, macOS and Linux alike. The
11131118
# TorchAO kernels are selected by the processor rather than the operating system, so
@@ -1120,8 +1125,8 @@ def _resolve_required(required):
11201125
"""Turn a row's requirement marker into the answer for the installed wheel."""
11211126
if required == _REQUIRED_ON_A_CUDA_WHEEL:
11221127
return bool(_wheel_cuda_train())
1123-
if required == _REQUIRED_ON_LINUX:
1124-
return sys.platform == "linux"
1128+
if required == _REQUIRED_ON_LINUX_AND_WINDOWS:
1129+
return sys.platform in ("linux", "win32")
11251130
if required == _REQUIRED_ON_MACOS:
11261131
return sys.platform == "darwin"
11271132
if required == _REQUIRED_ON_LINUX_X86:
@@ -1279,15 +1284,15 @@ def _resolve_required(required):
12791284
_library_file_name("libexecutorch_backend_mlx"),
12801285
False,
12811286
),
1282-
# Required on Linux, where packaging turns the backend on for every non-minimal
1283-
# build. A fixed False passed a wheel that had compiled the delegate back into the
1284-
# extension: no library ships, so the row skips, and one definer inside the
1287+
# Required on Linux and Windows, where packaging turns the backend on for every
1288+
# non-minimal build. A fixed False passed a wheel that had compiled the delegate back
1289+
# into the extension: no library ships, so the row skips, and one definer inside the
12851290
# extension is exactly the monolithic layout a definer count cannot distinguish.
12861291
(
12871292
"OpenVINO delegate",
12881293
_OPENVINO_BACKEND_SYMBOLS,
12891294
_library_file_name("libexecutorch_backend_openvino"),
1290-
_REQUIRED_ON_LINUX,
1295+
_REQUIRED_ON_LINUX_AND_WINDOWS,
12911296
),
12921297
# Required on Linux x86_64 only, where the Qualcomm SDK the delegate builds against
12931298
# is published. A fixed False would pass a wheel that compiled the delegate back

‎.ci/scripts/wheel/test_windows.py‎

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
# This source code is licensed under the BSD-style license found in the
77
# LICENSE file in the root directory of this source tree.
88

9+
import importlib.util
10+
import os
911
import platform
12+
import shutil
13+
import subprocess
14+
import sys
1015
import tempfile
1116
from pathlib import Path
1217
from typing import List
@@ -24,6 +29,7 @@
2429
from executorch.exir import EdgeCompileConfig, to_edge_transform_and_lower
2530
from executorch.extension.pybindings.portable_lib import (
2631
_get_registered_backend_names,
32+
_is_available,
2733
_load_for_executorch_from_buffer,
2834
)
2935
from test_base import ModelTest
@@ -71,14 +77,96 @@ def run_tests(model_tests: List[ModelTest]) -> None:
7177
raise RuntimeError(f"Unsupported backend {model_test.backend}.")
7278

7379

80+
def test_the_openvino_delegate_runs() -> None:
81+
"""Install the OpenVINO runtime the way a user does, then run a model through it.
82+
83+
The wheel ships the delegate but not the runtime, and registering the delegate does not
84+
load the runtime, so nothing else here proves the delegate can open openvino_c.dll and
85+
compute. The check runs in a new process because the package looks for the runtime when
86+
it is imported, which in this process happened before the install.
87+
"""
88+
# The same bound as the python_version marker on the openvino extra.
89+
if sys.version_info >= (3, 15):
90+
print(
91+
"SKIP: OpenVINO publishes no build for this Python, so the openvino extra "
92+
"installs no runtime and the delegate cannot load here."
93+
)
94+
return
95+
requirements = (
96+
Path(test_base._repository_root_dir()) / "backends/openvino/requirements.txt"
97+
)
98+
subprocess.run(
99+
[
100+
sys.executable,
101+
"-m",
102+
"pip",
103+
"install",
104+
"executorch[openvino]",
105+
"-r",
106+
str(requirements),
107+
],
108+
check=True,
109+
)
110+
subprocess.run(
111+
[sys.executable, str(Path(__file__).resolve()), "--openvino"], check=True
112+
)
113+
importlib.invalidate_caches()
114+
openvino = importlib.util.find_spec("openvino")
115+
test_a_bare_name_does_not_load_from_the_current_folder(
116+
Path(openvino.submodule_search_locations[0]) / "libs"
117+
)
118+
119+
120+
def test_a_bare_name_does_not_load_from_the_current_folder(libs: Path) -> None:
121+
"""Run from a folder holding the real runtime and ask for it by bare name.
122+
123+
The standard Windows search order includes the current folder, where a planted
124+
openvino_c.dll would run, so the delegate has to refuse the copy found there.
125+
"""
126+
with tempfile.TemporaryDirectory() as work_dir:
127+
for library in libs.glob("*.dll"):
128+
shutil.copy2(library, work_dir)
129+
result = subprocess.run(
130+
[
131+
sys.executable,
132+
"-c",
133+
"from executorch.extension.pybindings.portable_lib import _is_available; "
134+
"print(_is_available('OpenvinoBackend'))",
135+
],
136+
cwd=work_dir,
137+
env={**os.environ, "OPENVINO_LIB_PATH": "openvino_c.dll"},
138+
capture_output=True,
139+
text=True,
140+
check=True,
141+
)
142+
assert result.stdout.split()[-1] == "False", (
143+
"the delegate loaded openvino_c.dll by its bare name from the current folder, "
144+
f"where a planted copy would run:\n{result.stdout}{result.stderr}"
145+
)
146+
print("✓ a bare openvino_c.dll name does not load from the current folder")
147+
148+
74149
if __name__ == "__main__":
150+
if sys.argv[1:] == ["--openvino"]:
151+
assert _is_available(
152+
"OpenvinoBackend"
153+
), "OpenvinoBackend is registered but cannot load the OpenVINO runtime"
154+
print("✓ OpenvinoBackend loads the OpenVINO runtime")
155+
test_base.test_a_model_runs_through_the_openvino_delegate(required=True)
156+
sys.exit(0)
157+
75158
# Before anything else, because this is the check that fails the way a user
76159
# fails: with only the dependencies the wheel declares.
77160
with tempfile.TemporaryDirectory() as work_dir:
78161
test_clean_install.run_tests(Path(work_dir))
79162

80163
if platform.system() == "Windows":
81164
registered = _get_registered_backend_names()
165+
assert (
166+
"OpenvinoBackend" in registered
167+
), f"OpenvinoBackend not found in registered backends: {registered}"
168+
print("✓ OpenvinoBackend is registered")
169+
82170
# Vulkan backend is optional: only present when the wheel was built with
83171
# EXECUTORCH_BUILD_VULKAN=1 and the Vulkan SDK (glslc) was available.
84172
if "VulkanBackend" in registered:
@@ -108,3 +196,7 @@ def run_tests(model_tests: List[ModelTest]) -> None:
108196
),
109197
]
110198
)
199+
200+
# Last, because it installs packages the wheel does not declare.
201+
if platform.system() == "Windows":
202+
test_the_openvino_delegate_runs()

‎README-wheel.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ to run ExecuTorch `.pte` files, with some restrictions:
3434
* \[macOS only] [Core ML](docs/source/backends/coreml/coreml-overview.md) and MLX backends are
3535
also linked into the prebuilt module.
3636
* \[Linux x86_64] [QNN](docs/source/backends-qualcomm.md) backend is linked into the prebuilt module.
37-
* \[Linux] [OpenVINO](docs/source/build-run-openvino.md) backend is also linked into the
37+
* \[Linux and Windows] [OpenVINO](docs/source/build-run-openvino.md) backend is also linked into the
3838
prebuilt module. OpenVINO requires the runtime to be installed separately:
3939
`pip install executorch[openvino]`
4040

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ is authoritative. See also the [Desktop guide](desktop/README.md).
200200
| iOS / iPadOS | [XNNPACK](https://docs.pytorch.org/executorch/main/backends/xnnpack/xnnpack-overview.html) CPU; [Core ML](https://docs.pytorch.org/executorch/main/backends/coreml/coreml-overview.html); [MLX](https://docs.pytorch.org/executorch/main/backends/mlx/mlx-overview.html) on physical devices *(experimental)* |
201201
| macOS | [XNNPACK](https://docs.pytorch.org/executorch/main/backends/xnnpack/xnnpack-overview.html); [Core ML](https://docs.pytorch.org/executorch/main/backends/coreml/coreml-overview.html); experimental [MLX](https://docs.pytorch.org/executorch/main/backends/mlx/mlx-overview.html), [Metal/AOTInductor](backends/apple/metal/README.md), and [WebGPU](https://docs.pytorch.org/executorch/main/backends/webgpu/webgpu-overview.html) paths |
202202
| Linux | [XNNPACK](https://docs.pytorch.org/executorch/main/backends/xnnpack/xnnpack-overview.html); [OpenVINO](https://docs.pytorch.org/executorch/main/build-run-openvino.html); experimental [CUDA/AOTInductor](https://docs.pytorch.org/executorch/main/backends/cuda/cuda-overview.html), [Vulkan](https://docs.pytorch.org/executorch/main/backends/vulkan/vulkan-overview.html), and [WebGPU](https://docs.pytorch.org/executorch/main/backends/webgpu/webgpu-overview.html) desktop paths |
203-
| Windows | [XNNPACK](https://docs.pytorch.org/executorch/main/backends/xnnpack/xnnpack-overview.html); experimental [CUDA/AOTInductor](https://docs.pytorch.org/executorch/main/backends/cuda/cuda-overview.html) and [Vulkan](https://docs.pytorch.org/executorch/main/backends/vulkan/vulkan-overview.html) desktop paths |
203+
| Windows | [XNNPACK](https://docs.pytorch.org/executorch/main/backends/xnnpack/xnnpack-overview.html); [OpenVINO](https://docs.pytorch.org/executorch/main/build-run-openvino.html); experimental [CUDA/AOTInductor](https://docs.pytorch.org/executorch/main/backends/cuda/cuda-overview.html) and [Vulkan](https://docs.pytorch.org/executorch/main/backends/vulkan/vulkan-overview.html) desktop paths |
204204
| Browser / WebAssembly | [Portable WebAssembly runtime](extension/wasm/README.md) and [WebGPU](https://docs.pytorch.org/executorch/main/backends/webgpu/webgpu-overview.html) *(both experimental)* |
205205
| Embedded / MCU | [Arm Cortex-M with CMSIS-NN](https://docs.pytorch.org/executorch/main/backends/arm-cortex-m/arm-cortex-m-overview.html) *(beta)*; [Arm Ethos-U](https://docs.pytorch.org/executorch/main/backends/arm-ethos-u/arm-ethos-u-overview.html); [NXP eIQ Neutron](https://docs.pytorch.org/executorch/main/backends/nxp/nxp-overview.html); [Cadence DSP](https://docs.pytorch.org/executorch/main/backends-cadence.html); [Zephyr](zephyr/README.md) and [Arduino](examples/arduino/README.md) integrations |
206206

‎backends/openvino/CMakeLists.txt‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ set(COMMON_INCLUDE_DIRS ${EXECUTORCH_ROOT}/..)
2828
# Include utility CMake scripts from ExecuteTorch
2929
include(${EXECUTORCH_ROOT}/tools/cmake/Utils.cmake)
3030

31-
# The backend resolves OpenVINO C API symbols via dlopen/dlsym at runtime, so
32-
# there is no build-time dependency on the OpenVINO SDK.
31+
# The backend resolves OpenVINO C API symbols at runtime (dlopen on Linux,
32+
# LoadLibrary on Windows), so there is no build-time dependency on the OpenVINO
33+
# SDK.
3334

3435
# Shared when the wheel build asks for it, static otherwise.
3536
if(EXECUTORCH_BUILD_SHARED)
@@ -39,8 +40,13 @@ else()
3940
endif()
4041
add_library(openvino_backend ${_openvino_backend_library_type})
4142

42-
# Enable exceptions and RTTI for OpenVINO backend
43-
target_compile_options(openvino_backend PRIVATE -frtti -fexceptions)
43+
# Enable exceptions and RTTI for OpenVINO backend. MSVC is also set for
44+
# clang-cl, which takes the cl spellings.
45+
if(MSVC)
46+
target_compile_options(openvino_backend PRIVATE /EHsc /GR)
47+
else()
48+
target_compile_options(openvino_backend PRIVATE -frtti -fexceptions)
49+
endif()
4450

4551
# Add source files for OpenVINO backend
4652
target_sources(

‎backends/openvino/README.md‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,28 @@ For more information on the supported hardware, please refer to [OpenVINO System
1414

1515
## Quick Start (pip wheel)
1616

17-
On Linux, the OpenVINO backend is included in the ExecuTorch pip wheel. Install the OpenVINO runtime to activate it:
17+
On Linux and Windows, the OpenVINO backend is included in the ExecuTorch pip wheel. Install the OpenVINO runtime to activate it:
1818

1919
```bash
2020
pip install executorch[openvino]
2121
```
2222

23-
The backend automatically discovers the OpenVINO C library from the pip-installed package — no `LD_LIBRARY_PATH` setup is needed.
23+
The backend automatically discovers the OpenVINO C library from the pip-installed package, so no library search path setup is needed.
2424

25-
If auto-discovery fails (e.g. non-standard install), you can point to the library explicitly:
25+
If auto-discovery fails (e.g. non-standard install), set `OPENVINO_LIB_PATH` to the full path of the OpenVINO C library: a `libopenvino_c.so` file on Linux, or `openvino_c.dll` on Windows. For the pip package on Linux:
2626

2727
```bash
28-
export OPENVINO_LIB_PATH=$(python3 -c "import openvino, os; print(os.path.join(os.path.dirname(openvino.__file__), 'libs', 'libopenvino_c.so'))")
28+
export OPENVINO_LIB_PATH="$(python3 -c 'import glob, openvino, os; print(sorted(glob.glob(os.path.join(os.path.dirname(openvino.__file__), "libs", "libopenvino_c.so*")))[0])')"
2929
```
3030

31+
On Windows, in PowerShell:
32+
33+
```powershell
34+
$env:OPENVINO_LIB_PATH = python -c "import openvino, os; print(os.path.join(os.path.dirname(openvino.__file__), 'libs', 'openvino_c.dll'))"
35+
```
36+
37+
On Windows, `PATH` and the current folder are not searched for the library or for its dependencies, such as `tbb12.dll`. Those must sit beside `openvino_c.dll`, as they do in the pip package, or in a folder added with `os.add_dll_directory` (`AddDllDirectory` in C++). Importing the `openvino` Python package adds its folders this way, including the ones Intel's `setupvars.bat` lists.
38+
3139
Verify the backend is available:
3240

3341
```python

‎backends/openvino/runtime/OpenvinoApi.h‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
#pragma once
1010

11-
#include <dlfcn.h>
1211
#include <cstddef>
1312
#include <cstdint>
1413
#include <memory>
@@ -96,12 +95,9 @@ using ov_shape_create_fn =
9695
ov_status_e (*)(int64_t, const int64_t*, ov_shape_t*);
9796
using ov_shape_free_fn = ov_status_e (*)(ov_shape_t*);
9897

98+
// Defined in OpenvinoBackend.cpp, where the platform's loader API is included.
9999
struct DlCloser {
100-
void operator()(void* handle) {
101-
if (handle) {
102-
dlclose(handle);
103-
}
104-
}
100+
void operator()(void* handle);
105101
};
106102
using DlHandle = std::unique_ptr<void, DlCloser>;
107103

0 commit comments

Comments
 (0)