Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
7fb60b0
Build and publish CUDA wheels
shoumikhin Aug 12, 2026
fac460c
Ship the OpenVINO delegate as its own library
shoumikhin Aug 12, 2026
9d4616b
Update
shoumikhin Aug 13, 2026
d91a185
Update
shoumikhin Aug 13, 2026
d8d97dd
Update
shoumikhin Aug 13, 2026
15b9873
Update
shoumikhin Aug 13, 2026
b1dff39
Update
shoumikhin Aug 13, 2026
b30a3c0
Update
shoumikhin Aug 13, 2026
01f16ef
Update
shoumikhin Aug 13, 2026
98848ea
Update
shoumikhin Aug 13, 2026
ecdc320
Update
shoumikhin Aug 13, 2026
e0d8aa5
Update
shoumikhin Aug 14, 2026
82cacfe
Update
shoumikhin Aug 14, 2026
91aa39e
Update
shoumikhin Aug 14, 2026
d8c4c6b
Update
shoumikhin Aug 14, 2026
02bec45
Update
shoumikhin Aug 14, 2026
287fe98
Update
shoumikhin Aug 14, 2026
c00044b
Update
shoumikhin Aug 14, 2026
2195932
Update
shoumikhin Aug 14, 2026
38da743
Update
shoumikhin Aug 14, 2026
fbcf0d2
Update
shoumikhin Aug 14, 2026
9fdbe7c
Update
shoumikhin Aug 14, 2026
5d3cd08
Update
shoumikhin Aug 14, 2026
4aa4232
Update
shoumikhin Aug 14, 2026
5b10334
Update
shoumikhin Aug 14, 2026
c2083f4
Update
shoumikhin Aug 14, 2026
8578758
Update
shoumikhin Aug 15, 2026
1ccd490
Update
shoumikhin Aug 15, 2026
9158960
Update
shoumikhin Aug 15, 2026
c8b7a33
Update
shoumikhin Aug 15, 2026
6ac9b78
Update
shoumikhin Aug 15, 2026
0751b10
Update
shoumikhin Aug 15, 2026
dc3f82b
Update
shoumikhin Aug 15, 2026
d5ffc96
Update
shoumikhin Aug 15, 2026
e8cb6b3
Update
shoumikhin Aug 15, 2026
83f7e6a
Update
shoumikhin Aug 15, 2026
65f7d7d
Update
shoumikhin Aug 15, 2026
e745645
Update
shoumikhin Aug 15, 2026
ad123cf
Update
shoumikhin Aug 15, 2026
03ac518
Update
shoumikhin Aug 16, 2026
2ba456a
Update
shoumikhin Aug 16, 2026
98c7d61
Update
shoumikhin Aug 16, 2026
09039cb
Update
shoumikhin Aug 16, 2026
39dff0c
Update
shoumikhin Aug 16, 2026
c87296d
Update
shoumikhin Aug 16, 2026
9cdc309
Update
shoumikhin Aug 16, 2026
078f880
Update
shoumikhin Aug 16, 2026
58f713f
Update
shoumikhin Aug 16, 2026
7b1d70b
Update
shoumikhin Aug 16, 2026
a665241
Update
shoumikhin Aug 16, 2026
b88f22c
Update
shoumikhin Aug 16, 2026
a126fbd
Update
shoumikhin Aug 17, 2026
bf87f01
Update
shoumikhin Aug 17, 2026
e848370
Update
shoumikhin Aug 17, 2026
5ad26f6
Update
shoumikhin Aug 17, 2026
3f556c3
Update
shoumikhin Aug 17, 2026
114ff80
Update
shoumikhin Aug 17, 2026
0686430
Update
shoumikhin Aug 17, 2026
f057c67
Update
shoumikhin Aug 17, 2026
4450c47
Update
shoumikhin Aug 17, 2026
37eaef2
Update
shoumikhin Aug 17, 2026
da74b60
Update
shoumikhin Aug 17, 2026
91f5bc0
Update
shoumikhin Aug 17, 2026
cb4d82f
Update
shoumikhin Aug 17, 2026
4be34ef
Update
shoumikhin Aug 17, 2026
8cbc05b
Update
shoumikhin Aug 17, 2026
ff7929a
Update
shoumikhin Aug 18, 2026
33cb5b2
Update
shoumikhin Aug 18, 2026
1e12cfe
Update
shoumikhin Aug 18, 2026
6f65530
Update
shoumikhin Aug 18, 2026
e2f579a
Update
shoumikhin Aug 18, 2026
3408f55
Update
shoumikhin Aug 19, 2026
765de9e
Update
shoumikhin Aug 19, 2026
65c1f20
Update
shoumikhin Aug 19, 2026
8489fb9
Update
shoumikhin Aug 19, 2026
6b94708
Update
shoumikhin Aug 19, 2026
ef3ddf9
Update
shoumikhin Aug 19, 2026
da00a25
Update
shoumikhin Aug 19, 2026
0f09d3c
Update
shoumikhin Aug 19, 2026
65ce38e
Update
shoumikhin Aug 19, 2026
b2450fc
Update
shoumikhin Aug 19, 2026
ab3acb9
Update
shoumikhin Aug 19, 2026
1857293
Update
shoumikhin Aug 19, 2026
81a94c4
Update
shoumikhin Aug 20, 2026
b31645d
Update
shoumikhin Aug 20, 2026
6a91c18
Update
shoumikhin Aug 20, 2026
9887bb3
Update
shoumikhin Aug 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions .ci/scripts/wheel/test_shared_libraries.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@
# have exactly one owner while the bundled code underneath them does not. That is
# the same failure the split exists to prevent, reached by a different route.
_BUNDLED_THREADPOOL_SYMBOLS = ("pthreadpool_create", "cpuinfo_initialize")
# The delegate's own entry points. A second definer means the delegate is compiled
# into the Python extension as well, which would register it twice in one process.
_OPENVINO_BACKEND_SYMBOLS = ("executorch::backends::openvino::OpenvinoBackend",)

_BUNDLED_XNNPACK_SYMBOLS = ("xnn_create_runtime_v4",)

# A representative symbol from the profiler. A second definer means two event
Expand Down Expand Up @@ -464,6 +468,18 @@ def _wheel_cuda_train() -> str:
# wheel is inspected, and a row cannot call that at import time.
_REQUIRED_ON_A_CUDA_WHEEL = "cuda-wheel-only"

# Marker for a row whose owner every Linux wheel carries and no macOS wheel does.
_REQUIRED_ON_LINUX = "linux-only"


def _resolve_required(required):
"""Turn a row's requirement marker into the answer for the installed wheel."""
if required == _REQUIRED_ON_A_CUDA_WHEEL:
return bool(_wheel_cuda_train())
if required == _REQUIRED_ON_LINUX:
return sys.platform == "linux"
return required


# The exact dependency names packaging declares per CUDA train, mirroring
# _CUDA_RUNTIME_PACKAGES in setup.py. Listed here rather than imported because setup.py
Expand Down Expand Up @@ -569,6 +585,16 @@ def _wheel_cuda_train() -> str:
"libexecutorch_backend_xnnpack.so",
True,
),
# Required on Linux, where packaging turns the backend on for every non-minimal
# build. A fixed False passed a wheel that had compiled the delegate back into the
# extension: no library ships, so the row skips, and one definer inside the
# extension is exactly the monolithic layout a definer count cannot distinguish.
(
"OpenVINO delegate",
_OPENVINO_BACKEND_SYMBOLS,
"libexecutorch_backend_openvino.so",
_REQUIRED_ON_LINUX,
),
)

# The one component that legitimately exists twice. The quantized kernels are compiled into the runtime
Expand All @@ -592,13 +618,11 @@ def test_each_component_has_one_owner() -> None:
# ships under backends/cuda/, and scanning lib/ alone reported it as absent, which each row
# treats as an acceptable state and so would have skipped the check entirely.
shipped = {path.name for path in _shipped_shared_objects(_installed_package_dir())}
on_a_cuda_wheel = bool(_wheel_cuda_train())
for what, symbols, owner, required in _OWNED_COMPONENTS:
if required == _REQUIRED_ON_A_CUDA_WHEEL:
# Resolved here rather than in the table, because it depends on the installed
# wheel. A fixed False let a wheel tagged +cu126 ship with no CUDA library at
# all and still pass, which is the whole point of these three rows.
required = on_a_cuda_wheel
# Resolved here rather than in the table, because it depends on the installed
# wheel. A fixed False let a wheel tagged +cu126 ship with no CUDA library at
# all and still pass, which is the whole point of the conditional rows.
required = _resolve_required(required)
present = any(name.startswith(owner) for name in shipped)
assert present or not required, (
f"the wheel ships no {owner}, which owns the {what}. Either packaging "
Expand Down Expand Up @@ -1641,6 +1665,7 @@ def test_shipped_library_names_are_expected() -> None:
# dependency, so both have to ship and both are expected here.
"libextension_cuda",
"libexecutorch_backend_xnnpack",
"libexecutorch_backend_openvino",
"libexecutorch_threadpool",
"libexecutorch_etdump",
)
Expand Down
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,9 @@ if(EXECUTORCH_BUILD_PYBIND)
endif()

if(EXECUTORCH_BUILD_OPENVINO)
# Under a shared build this resolves to the shipped library rather than a
# static archive, so the delegate is not copied into the extension and the
# process registers it once.
list(APPEND _dep_libs openvino_backend)
endif()

Expand Down
29 changes: 26 additions & 3 deletions backends/openvino/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,13 @@ include(${EXECUTORCH_ROOT}/tools/cmake/Utils.cmake)
# The backend resolves OpenVINO C API symbols via dlopen/dlsym at runtime, so
# there is no build-time dependency on the OpenVINO SDK.

# Define OpenVINO backend as a static library
add_library(openvino_backend STATIC)
# Shared when the wheel build asks for it, static otherwise.
if(EXECUTORCH_BUILD_SHARED)
set(_openvino_backend_library_type SHARED)
else()
set(_openvino_backend_library_type STATIC)
endif()
add_library(openvino_backend ${_openvino_backend_library_type})

# Enable exceptions and RTTI for OpenVINO backend
target_compile_options(openvino_backend PRIVATE -frtti -fexceptions)
Expand All @@ -47,7 +52,25 @@ target_sources(
target_include_directories(openvino_backend PRIVATE ${COMMON_INCLUDE_DIRS})

# Link ExecuteTorch core and dynamic loading libraries
target_link_libraries(openvino_backend PRIVATE executorch_core ${CMAKE_DL_LIBS})
target_link_libraries(openvino_backend PRIVATE ${CMAKE_DL_LIBS})

if(EXECUTORCH_BUILD_SHARED)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we were not doing all the backends? what made you include this one?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right that this stack is not doing every backend. OpenVINO came in because it was the last delegate still compiled into the Python extension, so it was the one remaining case of the exact problem the stack fixes: only Python could use it, and a C++ app had no way to link it.

It is also cheap to include, since it needed the same treatment already applied to XNNPACK and CUDA. The backends left out are the ones that need real work, not just splitting.

# Named after what the library provides rather than after the target that
# produces it, matching the other shipped delegates, so the file reads as
# libexecutorch_backend_openvino.so.
set_target_properties(
openvino_backend PROPERTIES OUTPUT_NAME executorch_backend_openvino
)
executorch_target_soname_policy(openvino_backend)
# The shared runtime, never also the static core: linking both would compile
# the backend registry into this library as well and give the process two of
# them.
target_link_libraries(openvino_backend PUBLIC executorch_shared)
# Ships beside the runtime in the wheel's lib/ directory.
executorch_target_shipped_runtime_path(openvino_backend)
else()
target_link_libraries(openvino_backend PUBLIC executorch_core)
endif()

executorch_target_link_options_shared_lib(openvino_backend)

Expand Down
16 changes: 15 additions & 1 deletion docs/source/using-executorch-cpp.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,15 @@ These are the components the Linux package provides:
| `kernels_quantized` | The quantized operator kernels | Linux |
| `backend_cuda` | The CUDA delegate | Linux |
| `extension_cuda` | The CUDA stream extension | Linux |
| `backend_openvino` | The OpenVINO delegate | Linux |

To see what your own install offers, ask CMake:

```cmake
find_package(executorch REQUIRED)
foreach(_component
runtime kernels_optimized kernels_quantized backend_xnnpack
backend_cuda extension_cuda threadpool etdump)
backend_cuda extension_cuda backend_openvino threadpool etdump)
if(TARGET executorch::${_component})
message(STATUS "have ${_component}")
endif()
Expand All @@ -213,6 +214,19 @@ The quantized kernels are deliberately left out of that variable, because loadin
`executorch.kernels.quantized` in Python registers the same operators and a duplicate registration
stops the runtime.

The OpenVINO delegate needs one more step. The wheel ships the adapter, not the OpenVINO runtime
itself, and the adapter opens `libopenvino_c.so` by name when the model is loaded. Python callers
get that path set for them on import; a standalone C++ program does not, so install the runtime and
point the program at it:

```bash
pip install "executorch[openvino]"
export OPENVINO_LIB_PATH="$(python -c 'import glob, openvino, os; print(sorted(glob.glob(os.path.join(os.path.dirname(openvino.__file__), "libs", "libopenvino_c.so*")))[0])')"
```

Without it the delegate still registers and the program still links, and the failure arrives later,
when the model is loaded.

#### When something does not work

- `find_package` could not find executorch: the `-DCMAKE_PREFIX_PATH=...` argument is missing or
Expand Down
12 changes: 12 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2149,6 +2149,18 @@ def run(self): # noqa C901
"EXECUTORCH_BUILD_KERNELS_QUANTIZED",
],
),
# The OpenVINO delegate, so a C++ application can link it from the
# wheel. Only the adapter ships here: the OpenVINO runtime itself is
# loaded at run time and comes from the openvino extra.
BuiltFile(
src_dir="%CMAKE_CACHE_DIR%/backends/openvino/%BUILD_TYPE%/",
src_name="*executorch_backend_openvino" + _dynamic_lib_suffix(),
dst="executorch/lib/",
dependent_cmake_flags=[
"EXECUTORCH_BUILD_SHARED",
"EXECUTORCH_BUILD_OPENVINO",
],
),
# Install the XNNPACK delegate beside them, so a process has one
# copy of it instead of one per component that uses it.
BuiltFile(
Expand Down
14 changes: 11 additions & 3 deletions tools/cmake/executorch-wheel-config.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@
# executorch::backend_xnnpack The XNNPACK delegate.
# executorch::backend_cuda The CUDA delegate. Linux only.
# executorch::extension_cuda The CUDA stream extension. Linux only.
# executorch::backend_openvino The OpenVINO delegate. Linux only. Opens the
# OpenVINO runtime by name, which a C++ program
# installs and points OPENVINO_LIB_PATH at.
# executorch::threadpool The shared thread pool.
# executorch::etdump The profiler.
# ~~~
Expand Down Expand Up @@ -311,9 +314,13 @@ if(_executorch_runtime_library AND NOT _executorch_targets_supported)
# links that as well.
foreach(
_executorch_component IN
ITEMS libexecutorch_kernels_optimized libexecutorch_backend_xnnpack
libexecutorch_backend_cuda libexecutorch_extension_cuda
libexecutorch_threadpool libexecutorch_etdump
ITEMS libexecutorch_kernels_optimized
libexecutorch_backend_xnnpack
libexecutorch_backend_cuda
libexecutorch_extension_cuda
libexecutorch_backend_openvino
libexecutorch_threadpool
libexecutorch_etdump
)
_executorch_find_library(
_executorch_component_library "${_executorch_component}"
Expand Down Expand Up @@ -603,6 +610,7 @@ if(TARGET executorch::runtime AND TARGET executorch::threadpool)
endif()

_executorch_define_component(backend_xnnpack executorch_backend_xnnpack)
_executorch_define_component(backend_openvino executorch_backend_openvino)
# The CUDA delegate and its stream helper, present only in a wheel built from a
# CUDA index. A CPU wheel defines neither, so a consumer asking for one is told
# while configuring.
Expand Down
Loading