From d64ae0180bffd8500f5d13d61e599d3ae81e278e Mon Sep 17 00:00:00 2001 From: suryasidd Date: Thu, 19 Mar 2026 16:26:06 -0700 Subject: [PATCH 1/4] Added openvino backend to wheel build --- .ci/scripts/setup-openvino.sh | 11 +- .ci/scripts/test_wheel_package_openvino.sh | 151 ++++++++++++++++++ .ci/scripts/wheel/pre_build_script.sh | 8 + .ci/scripts/wheel/test_linux.py | 5 + .github/workflows/pull.yml | 27 ++++ backends/openvino/requirements.txt | 2 +- backends/openvino/scripts/install_openvino.sh | 42 +++++ setup.py | 12 ++ 8 files changed, 248 insertions(+), 10 deletions(-) create mode 100755 .ci/scripts/test_wheel_package_openvino.sh create mode 100644 backends/openvino/scripts/install_openvino.sh diff --git a/.ci/scripts/setup-openvino.sh b/.ci/scripts/setup-openvino.sh index 587494f46ac..22490d2e081 100755 --- a/.ci/scripts/setup-openvino.sh +++ b/.ci/scripts/setup-openvino.sh @@ -10,16 +10,9 @@ set -ex # shellcheck source=/dev/null source "$(dirname "${BASH_SOURCE[0]}")/utils.sh" -# Download and install OpenVINO from release packages -OPENVINO_VERSION="2025.3" -OPENVINO_BUILD="2025.3.0.19807.44526285f24" -OPENVINO_URL="https://storage.openvinotoolkit.org/repositories/openvino/packages/${OPENVINO_VERSION}/linux/openvino_toolkit_ubuntu22_${OPENVINO_BUILD}_x86_64.tgz" +source "$(dirname "${BASH_SOURCE[0]}")/../../backends/openvino/scripts/install_openvino.sh" +install_openvino -curl -Lo /tmp/openvino_toolkit.tgz --retry 3 --fail ${OPENVINO_URL} -tar -xzf /tmp/openvino_toolkit.tgz -mv openvino_toolkit_ubuntu22_${OPENVINO_BUILD}_x86_64 openvino - -source openvino/setupvars.sh cd backends/openvino pip install -r requirements.txt cd scripts diff --git a/.ci/scripts/test_wheel_package_openvino.sh b/.ci/scripts/test_wheel_package_openvino.sh new file mode 100755 index 00000000000..0e5b241dbf6 --- /dev/null +++ b/.ci/scripts/test_wheel_package_openvino.sh @@ -0,0 +1,151 @@ +#!/bin/bash +# === OpenVINO Wheel Build & Test Script === +# +# Builds the ExecuTorch wheel with OpenVINO support, installs it into both +# a conda env and a Python venv, and runs smoke tests verifying that the +# OpenVINO backend is registered and can export a simple model. +# +# Usage: +# ./.ci/scripts/test_wheel_package_openvino.sh +# +# Example: +# ./.ci/scripts/test_wheel_package_openvino.sh 3.11 + +set -e +set -x +exec > >(tee -i openvino_wheel_build.log) 2>&1 + +REPO_ROOT=$(pwd) +PYTHON_VERSION=${1:-3.11} + +# ---------------------------- +# Dynamically create test script +# ---------------------------- +cat > "/tmp/script_openvino_wheel_test.py" << 'EOF' +import torch +from torch.export import export +from executorch.backends.openvino.partitioner import OpenVINOPartitioner +from executorch.exir import to_edge_transform_and_lower +from executorch.exir.capture._config import ExecutorchBackendConfig +from executorch.extension.export_util.utils import save_pte_program + + +class LinearModule(torch.nn.Module): + def __init__(self): + super().__init__() + self.linear = torch.nn.Linear(3, 3) + + def forward(self, x): + return self.linear(x) + + def get_example_inputs(self): + return (torch.randn(3, 3),) + + +model = LinearModule().eval() +example_inputs = model.get_example_inputs() + +exported = export(model, example_inputs, strict=True) + +edge = to_edge_transform_and_lower( + exported, + partitioner=[OpenVINOPartitioner()], +) + +# Verify OpenVINO delegation occurred +graph_str = str(edge.exported_program().graph_module) +assert "executorch_call_delegate" in graph_str, \ + "Expected OpenVINO delegation but no delegate call found in graph" +print("OpenVINO delegation successful") + +executorch_program = edge.to_executorch( + config=ExecutorchBackendConfig(extract_delegate_segments=False) +) +save_pte_program(executorch_program, "linear_openvino", "") +print("linear_openvino.pte created successfully") +EOF + +source .ci/scripts/utils.sh +# ---------------------------- +# Install OpenVINO and source its setupvars.sh +# ---------------------------- +echo "=== Installing OpenVINO ===" +source "${REPO_ROOT}/backends/openvino/scripts/install_openvino.sh" +install_openvino +echo "OpenVINO_DIR=${OpenVINO_DIR}" + +# ---------------------------- +# Build the wheel +# ---------------------------- +echo "=== Building Wheel Package ===" +install_executorch + +python setup.py bdist_wheel + +WHEEL_FILE=$(ls dist/*.whl | head -n 1) +echo "Found wheel: ${WHEEL_FILE}" + + +# ---------------------------- +# Helpers +# ---------------------------- +run_core_tests() { + local PYBIN="$1" + local PIPBIN="$2" + local LABEL="$3" + + echo "=== [${LABEL}] Installing wheel ===" + "${PIPBIN}" install --upgrade pip + "${PIPBIN}" install "${WHEEL_FILE}" + + echo "=== [${LABEL}] Import smoke tests ===" + "${PYBIN}" -c "import executorch; print('executorch imported')" + "${PYBIN}" -c "import executorch.backends.openvino; print('executorch.backends.openvino imported')" + + echo "=== [${LABEL}] Verify OpenvinoBackend is registered ===" + "${PYBIN}" - <<'PY' +from executorch.extension.pybindings.portable_lib import _get_registered_backend_names +backends = _get_registered_backend_names() +print(f"Registered backends: {backends}") +assert "OpenvinoBackend" in backends, \ + f"OpenvinoBackend not found in registered backends: {backends}" +print("OpenvinoBackend is registered") +PY + + echo "=== [${LABEL}] Run export script to generate linear_openvino.pte ===" + (cd "${REPO_ROOT}" && "${PYBIN}" /tmp/script_openvino_wheel_test.py) + + if [[ -f "${REPO_ROOT}/linear_openvino.pte" ]]; then + echo "[${LABEL}] linear_openvino.pte created successfully" + rm -f "${REPO_ROOT}/linear_openvino.pte" + else + echo "ERROR: [${LABEL}] linear_openvino.pte was not created" + exit 1 + fi +} + +# ---------------------------- +# Conda environment tests +# ---------------------------- +echo "=== Testing in Conda env ===" +TEMP_ENV_DIR=$(mktemp -d) +echo "Using temporary directory for conda: $TEMP_ENV_DIR" +conda create -y -p "${TEMP_ENV_DIR}/env" python="${PYTHON_VERSION}" +CONDA_PY="${TEMP_ENV_DIR}/env/bin/python" +CONDA_PIP="${TEMP_ENV_DIR}/env/bin/pip" +run_core_tests "${CONDA_PY}" "${CONDA_PIP}" "conda" +conda env remove -p "${TEMP_ENV_DIR}/env" -y || true +rm -rf "${TEMP_ENV_DIR}" + +# ---------------------------- +# Python venv tests +# ---------------------------- +echo "=== Testing in Python venv ===" +TEMP_VENV_DIR=$(mktemp -d) +python3 -m venv "${TEMP_VENV_DIR}/venv" +VENV_PY="${TEMP_VENV_DIR}/venv/bin/python" +VENV_PIP="${TEMP_VENV_DIR}/venv/bin/pip" +run_core_tests "${VENV_PY}" "${VENV_PIP}" "venv" +rm -rf "${TEMP_VENV_DIR}" + +echo "=== All OpenVINO wheel tests completed! ===" diff --git a/.ci/scripts/wheel/pre_build_script.sh b/.ci/scripts/wheel/pre_build_script.sh index bf48d3bc29f..626094f0c08 100755 --- a/.ci/scripts/wheel/pre_build_script.sh +++ b/.ci/scripts/wheel/pre_build_script.sh @@ -62,3 +62,11 @@ if [[ "$(uname -s)" == "Linux" && "$(uname -m)" == "x86_64" ]]; then echo "QNN_SDK_ROOT=${QNN_SDK_ROOT}" >> "${GITHUB_ENV}" echo "QNN SDK downloaded to ${QNN_SDK_ROOT}" fi + +# Install OpenVINO on Linux x86_64 and source its setupvars.sh so the wheel +# build can include the OpenVINO backend. +if [[ "$(uname -s)" == "Linux" && "$(uname -m)" == "x86_64" ]]; then + source "${GITHUB_WORKSPACE}/${REPOSITORY}/backends/openvino/scripts/install_openvino.sh" + install_openvino + echo "OpenVINO_DIR=${OpenVINO_DIR}" >> "${GITHUB_ENV}" +fi diff --git a/.ci/scripts/wheel/test_linux.py b/.ci/scripts/wheel/test_linux.py index 7802c319c0f..bcb6b87cdd0 100644 --- a/.ci/scripts/wheel/test_linux.py +++ b/.ci/scripts/wheel/test_linux.py @@ -24,6 +24,11 @@ ), f"QnnBackend not found in registered backends: {registered}" print("✓ QnnBackend is registered") + assert ( + "OpenvinoBackend" in registered + ), f"OpenvinoBackend not found in registered backends: {registered}" + print("OpenvinoBackend is registered") + test_base.run_tests( model_tests=[ test_base.ModelTest( diff --git a/.github/workflows/pull.yml b/.github/workflows/pull.yml index 6c9f2c30148..534c080419d 100644 --- a/.github/workflows/pull.yml +++ b/.github/workflows/pull.yml @@ -1001,6 +1001,33 @@ jobs: PYTHON_EXECUTABLE=python bash .ci/scripts/test_model.sh "mv3" "buck2" "mediatek" # placeholder for mediatek to add more tests + test-openvino-wheel-packages-linux: + name: test-openvino-wheel-packages-linux + uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main + permissions: + id-token: write + contents: read + strategy: + fail-fast: false + matrix: + python-version: [ "3.10", "3.11", "3.12", "3.13" ] + with: + runner: linux.2xlarge + docker-image: ci-image:executorch-ubuntu-22.04-gcc11 + submodules: 'recursive' + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + timeout: 180 + script: | + # The generic Linux job chooses to use base env, not the one setup by the image + CONDA_ENV=$(conda env list --json | jq -r ".envs | .[-1]") + conda activate "${CONDA_ENV}" + + # Create a clean env for each python version + conda create -y -n test_env_${{ matrix.python-version }} python=${{ matrix.python-version }} + conda activate test_env_${{ matrix.python-version }} + + PYTHON_EXECUTABLE=python bash .ci/scripts/test_wheel_package_openvino.sh "${{ matrix.python-version }}" + test-openvino-linux: name: test-openvino-linux uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main diff --git a/backends/openvino/requirements.txt b/backends/openvino/requirements.txt index ba338416583..673925ea479 100644 --- a/backends/openvino/requirements.txt +++ b/backends/openvino/requirements.txt @@ -1 +1 @@ -nncf==3.0.0 +git+https://github.com/openvinotoolkit/nncf@b101e7e#egg=nncf \ No newline at end of file diff --git a/backends/openvino/scripts/install_openvino.sh b/backends/openvino/scripts/install_openvino.sh new file mode 100644 index 00000000000..4d29e1b8d0d --- /dev/null +++ b/backends/openvino/scripts/install_openvino.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. +# +# Downloads the OpenVINO toolkit archive and sources setupvars.sh to set +# OpenVINO_DIR and related environment variables. + +set -ex + +OPENVINO_VERSION="2025.3" +OPENVINO_BUILD="2025.3.0.19807.44526285f24" +OPENVINO_ARCHIVE="openvino_toolkit_ubuntu22_${OPENVINO_BUILD}_x86_64" +OPENVINO_URL="https://storage.openvinotoolkit.org/repositories/openvino/packages/${OPENVINO_VERSION}/linux/${OPENVINO_ARCHIVE}.tgz" + +install_openvino() { + # Skip if OpenVINO_DIR is already set and valid + if [[ -n "${OpenVINO_DIR:-}" && -d "${OpenVINO_DIR:-}" ]]; then + echo "OpenVINO already set to ${OpenVINO_DIR} - skipping installation" + return + fi + + # Skip if already extracted + if [[ -f "openvino/setupvars.sh" ]]; then + echo "OpenVINO already extracted at $(pwd)/openvino" + source openvino/setupvars.sh + return + fi + + echo "Downloading OpenVINO ${OPENVINO_VERSION}..." + curl -Lo /tmp/openvino_toolkit.tgz --retry 3 --fail "${OPENVINO_URL}" + + echo "Extracting OpenVINO archive..." + tar -xzf /tmp/openvino_toolkit.tgz + mv "${OPENVINO_ARCHIVE}" openvino + rm -f /tmp/openvino_toolkit.tgz + + source openvino/setupvars.sh + echo "OpenVINO_DIR=${OpenVINO_DIR}" +} diff --git a/setup.py b/setup.py index f05951012e3..4cd2ee39723 100644 --- a/setup.py +++ b/setup.py @@ -717,6 +717,15 @@ def run(self): # noqa C901 f"-DQNN_SDK_ROOT={qnn_sdk_root}", ] + # Check if OpenVINO is available (via OpenVINO_DIR env var set by + # setupvars.sh), and if so, enable building the OpenVINO backend by + # default. + openvino_dir = os.environ.get("OpenVINO_DIR", "").strip() + if openvino_dir and install_utils.is_cmake_option_on( + cmake_configuration_args, "EXECUTORCH_BUILD_OPENVINO", default=True + ): + cmake_configuration_args += ["-DEXECUTORCH_BUILD_OPENVINO=ON"] + with Buck2EnvironmentFixer(): # Generate the cmake cache from scratch to ensure that the cache state # is predictable. @@ -790,6 +799,9 @@ def run(self): # noqa C901 cmake_build_args += ["--target", "custom_ops_aot_lib"] cmake_build_args += ["--target", "quantized_ops_aot_lib"] + if cmake_cache.is_enabled("EXECUTORCH_BUILD_OPENVINO"): + cmake_build_args += ["--target", "openvino_backend"] + if cmake_cache.is_enabled("EXECUTORCH_BUILD_QNN"): cmake_build_args += ["--target", "qnn_executorch_backend"] cmake_build_args += ["--target", "PyQnnManagerAdaptor"] From 321e77058cbb9160c1791fe4efff136baeafeb8f Mon Sep 17 00:00:00 2001 From: suryasidd Date: Thu, 19 Mar 2026 16:32:01 -0700 Subject: [PATCH 2/4] Lint changes --- .ci/scripts/test_wheel_package_openvino.sh | 7 +------ backends/openvino/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.ci/scripts/test_wheel_package_openvino.sh b/.ci/scripts/test_wheel_package_openvino.sh index 0e5b241dbf6..2a0ec6eb836 100755 --- a/.ci/scripts/test_wheel_package_openvino.sh +++ b/.ci/scripts/test_wheel_package_openvino.sh @@ -4,12 +4,7 @@ # Builds the ExecuTorch wheel with OpenVINO support, installs it into both # a conda env and a Python venv, and runs smoke tests verifying that the # OpenVINO backend is registered and can export a simple model. -# -# Usage: -# ./.ci/scripts/test_wheel_package_openvino.sh -# -# Example: -# ./.ci/scripts/test_wheel_package_openvino.sh 3.11 + set -e set -x diff --git a/backends/openvino/requirements.txt b/backends/openvino/requirements.txt index 673925ea479..16091642eea 100644 --- a/backends/openvino/requirements.txt +++ b/backends/openvino/requirements.txt @@ -1 +1 @@ -git+https://github.com/openvinotoolkit/nncf@b101e7e#egg=nncf \ No newline at end of file +git+https://github.com/openvinotoolkit/nncf@b101e7e#egg=nncf From a116d92c7728ee69a9deb4fc5291271353af17de Mon Sep 17 00:00:00 2001 From: suryasidd Date: Thu, 19 Mar 2026 23:15:49 -0700 Subject: [PATCH 3/4] Added compile spec --- .ci/scripts/test_wheel_package_openvino.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.ci/scripts/test_wheel_package_openvino.sh b/.ci/scripts/test_wheel_package_openvino.sh index 2a0ec6eb836..6c53843fbb8 100755 --- a/.ci/scripts/test_wheel_package_openvino.sh +++ b/.ci/scripts/test_wheel_package_openvino.sh @@ -19,8 +19,10 @@ PYTHON_VERSION=${1:-3.11} cat > "/tmp/script_openvino_wheel_test.py" << 'EOF' import torch from torch.export import export -from executorch.backends.openvino.partitioner import OpenVINOPartitioner +from executorch.backends.openvino.partitioner import OpenvinoPartitioner from executorch.exir import to_edge_transform_and_lower +from executorch.exir.backend.backend_details import CompileSpec +from executorch.exir.backend.utils import format_delegated_graph from executorch.exir.capture._config import ExecutorchBackendConfig from executorch.extension.export_util.utils import save_pte_program @@ -42,14 +44,15 @@ example_inputs = model.get_example_inputs() exported = export(model, example_inputs, strict=True) +compile_spec = [CompileSpec("device", b"CPU")] edge = to_edge_transform_and_lower( exported, - partitioner=[OpenVINOPartitioner()], + partitioner=[OpenvinoPartitioner(compile_spec)], ) # Verify OpenVINO delegation occurred -graph_str = str(edge.exported_program().graph_module) -assert "executorch_call_delegate" in graph_str, \ +output_graph = format_delegated_graph(edge.exported_program().graph_module) +assert "OpenvinoBackend" in output_graph, \ "Expected OpenVINO delegation but no delegate call found in graph" print("OpenVINO delegation successful") From efaea3b4a0e184af3cc3ca56dc5cfa3d8ded80a5 Mon Sep 17 00:00:00 2001 From: suryasidd Date: Thu, 19 Mar 2026 23:31:39 -0700 Subject: [PATCH 4/4] Updated OpenVINO --- backends/openvino/scripts/install_openvino.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/openvino/scripts/install_openvino.sh b/backends/openvino/scripts/install_openvino.sh index 4d29e1b8d0d..617f405fced 100644 --- a/backends/openvino/scripts/install_openvino.sh +++ b/backends/openvino/scripts/install_openvino.sh @@ -10,8 +10,8 @@ set -ex -OPENVINO_VERSION="2025.3" -OPENVINO_BUILD="2025.3.0.19807.44526285f24" +OPENVINO_VERSION="2026.0" +OPENVINO_BUILD="2026.0.0.20965.c6d6a13a886" OPENVINO_ARCHIVE="openvino_toolkit_ubuntu22_${OPENVINO_BUILD}_x86_64" OPENVINO_URL="https://storage.openvinotoolkit.org/repositories/openvino/packages/${OPENVINO_VERSION}/linux/${OPENVINO_ARCHIVE}.tgz"