From 9013904ce633d37ee1ccac12493d157ccba34048 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:48:49 -0700 Subject: [PATCH 01/29] webgpu plugin pipeline --- cmake/onnxruntime_providers_webgpu.cmake | 8 + .../core/providers/webgpu/ep/factory.cc | 2 +- .../plugin-webgpu-pipeline.yml | 94 +++++++++++ .../stages/plugin-linux-webgpu-stage.yml | 77 +++++++++ .../stages/plugin-mac-webgpu-stage.yml | 84 ++++++++++ .../stages/plugin-webgpu-packaging-stage.yml | 92 ++++++++++ .../stages/plugin-win-webgpu-stage.yml | 157 ++++++++++++++++++ .../set-plugin-build-variables-step.yml | 146 ++++++++++++++++ 8 files changed, 659 insertions(+), 1 deletion(-) create mode 100644 tools/ci_build/github/azure-pipelines/plugin-webgpu-pipeline.yml create mode 100644 tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml create mode 100644 tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml create mode 100644 tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml create mode 100644 tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml create mode 100644 tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml diff --git a/cmake/onnxruntime_providers_webgpu.cmake b/cmake/onnxruntime_providers_webgpu.cmake index cd29e4dad0a17..25d786b681019 100644 --- a/cmake/onnxruntime_providers_webgpu.cmake +++ b/cmake/onnxruntime_providers_webgpu.cmake @@ -84,6 +84,14 @@ add_definitions("-DONNX_NAMESPACE=onnx") add_definitions("-DONNX_USE_LITE_PROTO=1") + # Default plugin EP version to ORT_VERSION if not explicitly provided. + if(NOT DEFINED onnxruntime_PLUGIN_EP_VERSION) + set(onnxruntime_PLUGIN_EP_VERSION "${ORT_VERSION}-dev") + endif() + + # Set preprocessor definition for plugin EP version + target_compile_definitions(onnxruntime_providers_webgpu PRIVATE ORT_PLUGIN_EP_VERSION="${onnxruntime_PLUGIN_EP_VERSION}") + # Set preprocessor definitions used in onnxruntime_providers_webgpu.rc if(WIN32) set(WEBGPU_DLL_FILE_DESCRIPTION "ONNX Runtime WebGPU Provider") diff --git a/onnxruntime/core/providers/webgpu/ep/factory.cc b/onnxruntime/core/providers/webgpu/ep/factory.cc index 99dd0c68f6954..6d8e8724f72d9 100644 --- a/onnxruntime/core/providers/webgpu/ep/factory.cc +++ b/onnxruntime/core/providers/webgpu/ep/factory.cc @@ -66,7 +66,7 @@ uint32_t ORT_API_CALL Factory::GetVendorIdImpl(const OrtEpFactory* /*this_ptr*/) } const char* ORT_API_CALL Factory::GetVersionImpl(const OrtEpFactory* /*this_ptr*/) noexcept { - return "0.1.0"; + return ORT_PLUGIN_EP_VERSION; } OrtStatus* ORT_API_CALL Factory::GetSupportedDevicesImpl( diff --git a/tools/ci_build/github/azure-pipelines/plugin-webgpu-pipeline.yml b/tools/ci_build/github/azure-pipelines/plugin-webgpu-pipeline.yml new file mode 100644 index 0000000000000..8c80838aea752 --- /dev/null +++ b/tools/ci_build/github/azure-pipelines/plugin-webgpu-pipeline.yml @@ -0,0 +1,94 @@ +trigger: none + +resources: + repositories: + - repository: 1esPipelines + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release + +parameters: +- name: build_windows_x64 + displayName: 'Build Windows x64' + type: boolean + default: true + +- name: build_windows_arm64 + displayName: 'Build Windows ARM64' + type: boolean + default: false + +- name: build_linux_x64 + displayName: 'Build Linux x64' + type: boolean + default: false + +- name: build_macos_arm64 + displayName: 'Build macOS ARM64' + type: boolean + default: false + +- name: api_version + displayName: 'API Version' + type: number + values: + - 24 + - 25 + default: 24 + +- name: package_version + displayName: 'Package Version' + type: string + values: + - release + - RC + - dev + default: dev + +- name: cmake_build_type + type: string + default: 'Release' + values: + - Debug + - Release + - RelWithDebInfo + - MinSizeRel + +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines + parameters: + settings: + networkIsolationPolicy: Permissive + sdl: + componentgovernance: + ignoreDirectories: '$(Build.Repository.LocalPath)/cmake/external/emsdk/upstream/emscripten/tests,$(Build.Repository.LocalPath)/cmake/external/onnx/third_party/benchmark,$(Build.Repository.LocalPath)/cmake/external/onnx/third_party/pybind11,$(Build.Repository.LocalPath)/cmake/external/onnx/third_party/pybind11/tests,$(Build.Repository.LocalPath)/cmake/external/onnxruntime-extensions,$(Build.Repository.LocalPath)/js/react_native/e2e/node_modules,$(Build.Repository.LocalPath)/js/node_modules,$(Build.Repository.LocalPath)/onnxruntime-inference-examples,$(Build.SourcesDirectory)/cmake/external/emsdk/upstream/emscripten/tests,$(Build.SourcesDirectory)/cmake/external/onnx/third_party/benchmark,$(Build.SourcesDirectory)/cmake/external/onnx/third_party/pybind11,$(Build.SourcesDirectory)/cmake/external/onnx/third_party/pybind11/tests,$(Build.SourcesDirectory)/cmake/external/onnxruntime-extensions,$(Build.SourcesDirectory)/js/react_native/e2e/node_modules,$(Build.SourcesDirectory)/js/node_modules,$(Build.SourcesDirectory)/onnxruntime-inference-examples,$(Build.BinariesDirectory)' + alertWarningLevel: High + failOnAlert: false + verbosity: Normal + timeout: 3600 + tsa: + enabled: true + codeSignValidation: + enabled: true + break: true + policheck: + enabled: true + exclusionsFile: '$(Build.SourcesDirectory)\tools\ci_build\policheck_exclusions.xml' + codeql: + compiled: + enabled: false + justificationForDisabling: 'CodeQL is taking nearly 6 hours resulting in timeouts in our production pipelines' + pool: + name: 'onnxruntime-Win-CPU-VS2022-Latest' + os: windows + + stages: + - template: stages/plugin-webgpu-packaging-stage.yml + parameters: + build_windows_x64: ${{ parameters.build_windows_x64 }} + build_windows_arm64: ${{ parameters.build_windows_arm64 }} + build_linux_x64: ${{ parameters.build_linux_x64 }} + build_macos_arm64: ${{ parameters.build_macos_arm64 }} + api_version: ${{ parameters.api_version }} + package_version: ${{ parameters.package_version }} + cmake_build_type: ${{ parameters.cmake_build_type }} diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml new file mode 100644 index 0000000000000..4610c678b126b --- /dev/null +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml @@ -0,0 +1,77 @@ +parameters: +- name: machine_pool + type: string + default: 'onnxruntime-Ubuntu2404-AMD-CPU' + +- name: api_version + type: number + default: 24 + +- name: package_version + type: string + default: dev + +- name: cmake_build_type + type: string + default: 'Release' + values: + - Debug + - Release + - RelWithDebInfo + - MinSizeRel + +- name: docker_base_image + type: string + default: 'onnxruntimebuildcache.azurecr.io/internal/azureml/onnxruntime/build/cuda12_x64_almalinux8_gcc14:20251017.1' + +stages: +- stage: Linux_plugin_webgpu_x64_Build + dependsOn: [] + jobs: + - job: Linux_plugin_webgpu_x64_Build + timeoutInMinutes: 240 + workspace: + clean: all + pool: + name: ${{ parameters.machine_pool }} + os: linux + templateContext: + outputs: + - output: pipelineArtifact + targetPath: $(Build.ArtifactStagingDirectory) + artifactName: webgpu_plugin_linux_x64 + variables: + - template: ../templates/common-variables.yml + steps: + - checkout: self + clean: true + submodules: recursive + + - template: ../templates/set-nightly-build-option-variable-step.yml + + - template: ../templates/set-plugin-build-variables-step.yml + parameters: + api_version: ${{ parameters.api_version }} + package_version: ${{ parameters.package_version }} + + - template: ../templates/get-docker-image-steps.yml + parameters: + Dockerfile: tools/ci_build/github/linux/docker/inference/x86_64/python/cuda/Dockerfile + Context: tools/ci_build/github/linux/docker/inference/x86_64/python/cuda + DockerBuildArgs: "--build-arg BASEIMAGE=${{ parameters.docker_base_image }} --build-arg BUILD_UID=$( id -u )" + Repository: onnxruntimewebgpuplugin + + - task: Bash@3 + displayName: 'Build WebGPU Plugin' + inputs: + targetType: filePath + filePath: tools/ci_build/github/linux/run_python_dockerbuild.sh + arguments: -i onnxruntimewebgpuplugin -d "WEBGPU" -c ${{ parameters.cmake_build_type }} + env: + EXTRA_CMAKE_DEFINES: $(PluginEpVersionDefine) + + - script: | + set -e -x + mkdir -p $(Build.ArtifactStagingDirectory)/plugin + cp $(Build.BinariesDirectory)/${{ parameters.cmake_build_type }}/libonnxruntime_providers_webgpu.so $(Build.ArtifactStagingDirectory)/plugin/ || true + displayName: 'Copy plugin binaries' diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml new file mode 100644 index 0000000000000..479182ff37c1d --- /dev/null +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml @@ -0,0 +1,84 @@ +parameters: +- name: api_version + type: number + default: 24 + +- name: package_version + type: string + default: dev + +- name: cmake_build_type + type: string + default: 'Release' + values: + - Debug + - Release + - RelWithDebInfo + - MinSizeRel + +stages: +- stage: MacOS_plugin_webgpu_arm64_Build + dependsOn: [] + jobs: + - job: MacOS_plugin_webgpu_arm64_Build + timeoutInMinutes: 240 + workspace: + clean: all + pool: + name: AcesShared + os: macOS + demands: + - ImageOverride -equals ACES_VM_SharedPool_Sequoia + templateContext: + outputs: + - output: pipelineArtifact + targetPath: $(Build.ArtifactStagingDirectory) + artifactName: webgpu_plugin_macos_arm64 + variables: + - name: MACOSX_DEPLOYMENT_TARGET + value: '14.0' + - template: ../templates/common-variables.yml + steps: + - checkout: self + clean: true + submodules: none + + - template: ../templates/use-xcode-version.yml + parameters: + xcodeVersion: '16.4' + + - template: ../templates/setup-build-tools.yml + parameters: + host_cpu_arch: 'arm64' + + - template: ../templates/set-nightly-build-option-variable-step.yml + + - template: ../templates/set-plugin-build-variables-step.yml + parameters: + api_version: ${{ parameters.api_version }} + package_version: ${{ parameters.package_version }} + + - script: | + set -e -x + python3 -m pip install -r '$(Build.SourcesDirectory)/tools/ci_build/github/linux/docker/scripts/requirements.txt' + python3 $(Build.SourcesDirectory)/tools/ci_build/build.py \ + --build_dir $(Build.SourcesDirectory)/build \ + --use_vcpkg --use_vcpkg_ms_internal_asset_cache \ + --use_binskim_compliant_compile_flags \ + --config ${{ parameters.cmake_build_type }} \ + --enable_onnx_tests \ + --use_webgpu shared_lib \ + --wgsl_template static \ + --disable_rtti \ + --enable_lto \ + --cmake_extra_defines CMAKE_OSX_ARCHITECTURES=arm64 onnxruntime_BUILD_UNIT_TESTS=ON $(PluginEpVersionDefine) \ + --update --skip_submodule_sync --build --parallel + displayName: 'Build WebGPU Plugin' + + - task: CopyFiles@2 + displayName: 'Copy plugin binaries to staging directory' + inputs: + SourceFolder: '$(Build.SourcesDirectory)/build/${{ parameters.cmake_build_type }}' + Contents: | + libonnxruntime_providers_webgpu.dylib + TargetFolder: '$(Build.ArtifactStagingDirectory)' diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml new file mode 100644 index 0000000000000..0a2cbfa05f0dd --- /dev/null +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml @@ -0,0 +1,92 @@ +parameters: +- name: build_windows_x64 + displayName: 'Build Windows x64' + type: boolean + default: true + +- name: build_windows_arm64 + displayName: 'Build Windows ARM64' + type: boolean + default: false + +- name: build_linux_x64 + displayName: 'Build Linux x64' + type: boolean + default: false + +- name: build_macos_arm64 + displayName: 'Build macOS ARM64' + type: boolean + default: false + +- name: api_version + displayName: 'API Version' + type: number + default: 24 + +- name: package_version + displayName: 'Package Version' + type: string + default: dev + +- name: cmake_build_type + type: string + displayName: 'CMake build type' + default: 'Release' + values: + - Debug + - Release + - RelWithDebInfo + - MinSizeRel + +stages: + # Validate parameter combinations + - ${{ if and(eq(parameters.build_windows_arm64, true), eq(parameters.build_windows_x64, false)) }}: + - stage: Validate_Parameters + displayName: 'Validate Parameters' + dependsOn: [] + jobs: + - job: Validate + displayName: 'Validate parameter combinations' + pool: + name: 'onnxruntime-Win-CPU-VS2022-Latest' + os: windows + steps: + - script: | + echo "##vso[task.logissue type=error]Windows ARM64 build requires Windows x64 build to be enabled." + exit 1 + displayName: 'ERROR: Windows ARM64 requires Windows x64' + + # Windows x64 + - ${{ if eq(parameters.build_windows_x64, true) }}: + - template: plugin-win-webgpu-stage.yml + parameters: + arch: 'x64' + api_version: ${{ parameters.api_version }} + package_version: ${{ parameters.package_version }} + cmake_build_type: ${{ parameters.cmake_build_type }} + + # Windows ARM64 + - ${{ if and(eq(parameters.build_windows_arm64, true), eq(parameters.build_windows_x64, true)) }}: + - template: plugin-win-webgpu-stage.yml + parameters: + arch: 'arm64' + api_version: ${{ parameters.api_version }} + package_version: ${{ parameters.package_version }} + cmake_build_type: ${{ parameters.cmake_build_type }} + + # Linux x64 + - ${{ if eq(parameters.build_linux_x64, true) }}: + - template: plugin-linux-webgpu-stage.yml + parameters: + api_version: ${{ parameters.api_version }} + package_version: ${{ parameters.package_version }} + cmake_build_type: ${{ parameters.cmake_build_type }} + + # macOS ARM64 + - ${{ if eq(parameters.build_macos_arm64, true) }}: + - template: plugin-mac-webgpu-stage.yml + parameters: + api_version: ${{ parameters.api_version }} + package_version: ${{ parameters.package_version }} + cmake_build_type: ${{ parameters.cmake_build_type }} diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml new file mode 100644 index 0000000000000..2daced826222b --- /dev/null +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml @@ -0,0 +1,157 @@ +parameters: +- name: arch + type: string + values: + - x64 + - arm64 + +- name: api_version + type: number + default: 24 + +- name: package_version + type: string + default: dev + +- name: cmake_build_type + type: string + default: 'Release' + values: + - Debug + - Release + - RelWithDebInfo + - MinSizeRel + +- name: npm_registry_url + type: string + default: 'https://pkgs.dev.azure.com/aiinfra/_packaging/Guardian1ESPTUpstreamOrgFeed/npm/registry/' + +stages: + - stage: Win_plugin_webgpu_${{ parameters.arch }}_Build + ${{ if eq(parameters.arch, 'arm64') }}: + dependsOn: Win_plugin_webgpu_x64_Build + ${{ else }}: + dependsOn: [] + jobs: + - job: Win_plugin_webgpu_${{ parameters.arch }}_Build + timeoutInMinutes: 360 + workspace: + clean: all + pool: + name: onnxruntime-Win-CPU-VS2022-Latest + os: windows + templateContext: + sdl: + codeSignValidation: + enabled: true + break: true + psscriptanalyzer: + enabled: true + binskim: + enabled: true + scanOutputDirectoryOnly: true + outputs: + - output: pipelineArtifact + targetPath: $(Build.ArtifactStagingDirectory) + artifactName: webgpu_plugin_win_${{ parameters.arch }} + variables: + - template: ../templates/common-variables.yml + - name: GRADLE_OPTS + value: '-Dorg.gradle.daemon=false' + - name: VSGenerator + value: 'Visual Studio 17 2022' + steps: + - checkout: self + clean: true + submodules: none + + - template: ../templates/setup-build-tools.yml + parameters: + host_cpu_arch: 'x64' + + - template: ../templates/set-nightly-build-option-variable-step.yml + + - template: ../templates/set-plugin-build-variables-step.yml + parameters: + api_version: ${{ parameters.api_version }} + package_version: ${{ parameters.package_version }} + + - script: | + python -m pip install -r "$(Build.SourcesDirectory)\tools\ci_build\github\windows\python\requirements.txt" + displayName: 'Install Python build dependencies' + env: + TMPDIR: "$(Agent.TempDirectory)" + + - task: PowerShell@2 + displayName: '[WebGPU] Create .npmrc for WebGPU build' + inputs: + targetType: 'inline' + pwsh: true + script: | + $npmrcPath = Join-Path "$(Build.SourcesDirectory)" "onnxruntime/core/providers/webgpu/wgsl_templates/.npmrc" + $npmrcDir = Split-Path -Parent $npmrcPath + $packageJsonPath = Join-Path $npmrcDir "package.json" + + # Check if package.json exists + if (-not (Test-Path $packageJsonPath)) { + Write-Error "package.json not found at: $packageJsonPath" + throw "package.json does not exist in $npmrcDir" + } + + # Create .npmrc file with required content + $npmrcContent = @" + registry=${{ parameters.npm_registry_url }} + + always-auth=true + "@ + + Set-Content -Path $npmrcPath -Value $npmrcContent -Encoding UTF8 + Write-Host "Created .npmrc at: $npmrcPath" + + - task: npmAuthenticate@0 + displayName: '[WebGPU] Authenticate npm for WebGPU build' + inputs: + workingFile: '$(Build.SourcesDirectory)/onnxruntime/core/providers/webgpu/wgsl_templates/.npmrc' + + - task: PythonScript@0 + displayName: 'Build' + inputs: + scriptPath: '$(Build.SourcesDirectory)\tools\ci_build\build.py' + arguments: >- + --config ${{ parameters.cmake_build_type }} + --build_dir $(Build.BinariesDirectory) + --skip_submodule_sync + --cmake_generator "$(VSGenerator)" + --parallel + --use_vcpkg + --use_vcpkg_ms_internal_asset_cache + --use_binskim_compliant_compile_flags + --update + --build + --enable_onnx_tests + --use_webgpu shared_lib + --wgsl_template static + --disable_rtti + --enable_lto + --cmake_extra_defines onnxruntime_BUILD_UNIT_TESTS=ON onnxruntime_ENABLE_DAWN_BACKEND_D3D12=1 onnxruntime_ENABLE_DAWN_BACKEND_VULKAN=1 $(PluginEpVersionDefine) + $(TelemetryOption) + workingDirectory: '$(Build.BinariesDirectory)' + + # Esrp signing + - template: ../templates/win-esrp-dll.yml + parameters: + FolderPath: '$(Build.BinariesDirectory)\${{ parameters.cmake_build_type }}\${{ parameters.cmake_build_type }}' + DisplayName: 'ESRP - Sign Native dlls' + DoEsrp: true + Pattern: '*.dll' + + - task: CopyFiles@2 + displayName: 'Copy plugin binaries to staging directory' + inputs: + SourceFolder: '$(Build.BinariesDirectory)\${{ parameters.cmake_build_type }}\${{ parameters.cmake_build_type }}' + Contents: | + onnxruntime_providers_webgpu.dll + onnxruntime_providers_webgpu.pdb + dxcompiler.dll + dxil.dll + TargetFolder: '$(Build.ArtifactStagingDirectory)' diff --git a/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml b/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml new file mode 100644 index 0000000000000..eae6e294a0828 --- /dev/null +++ b/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml @@ -0,0 +1,146 @@ +# This file is used to set variables for plugin build stages. It does the following: +# +# 1. Verify and override the target ORT_API_VERSION if necessary. +# +# 2. Set the package version variable based on the build type (nightly, official, or dev). + +parameters: +- name: api_version + type: number + +- name: package_version + type: string + +steps: +# Step 1: Verify and override ORT_API_VERSION +- task: PythonScript@0 + displayName: 'Verify and override ORT_API_VERSION' + inputs: + scriptSource: inline + script: | + import os + import re + import sys + + raw_value = "${{ parameters.api_version }}" + if not raw_value.strip(): + print("##vso[task.logissue type=error]api_version parameter is empty.") + sys.exit(1) + try: + api_version = int(raw_value) + except ValueError: + print("##vso[task.logissue type=error]api_version must be an integer, got '{}'".format(raw_value)) + sys.exit(1) + + if api_version < 24: + print("##vso[task.logissue type=error]api_version must be >= 24, got {}".format(api_version)) + sys.exit(1) + + src_root = os.environ.get("BUILD_SOURCESDIRECTORY", "") + header_path = os.path.join(src_root, "include", "onnxruntime", "core", "session", "onnxruntime_c_api.h") + + if not os.path.isfile(header_path): + print("##vso[task.logissue type=error]Cannot find onnxruntime_c_api.h at: {}".format(header_path)) + sys.exit(1) + + with open(header_path, "r", encoding="utf-8") as f: + content = f.read() + + pattern = r"(#define\s+ORT_API_VERSION\s+)(\d+)" + matches = list(re.finditer(pattern, content)) + if len(matches) == 0: + print("##vso[task.logissue type=error]Cannot find '#define ORT_API_VERSION' in {}".format(header_path)) + sys.exit(1) + if len(matches) > 1: + print("##vso[task.logissue type=error]Found multiple '#define ORT_API_VERSION' in {}. Expected exactly one.".format(header_path)) + sys.exit(1) + + current_version = int(matches[0].group(2)) + print("Current ORT_API_VERSION: {}".format(current_version)) + print("Requested api_version: {}".format(api_version)) + + if current_version != api_version: + new_content = re.sub(pattern, r"\g<1>{}".format(api_version), content, count=1) + with open(header_path, "w", encoding="utf-8") as f: + f.write(new_content) + print("ORT_API_VERSION overridden from {} to {}".format(current_version, api_version)) + print("##vso[task.setvariable variable=OrtApiVersionOverridden]true") + else: + print("ORT_API_VERSION already matches. No change needed.") + print("##vso[task.setvariable variable=OrtApiVersionOverridden]false") + +# Step 2: Set package version string +- task: PythonScript@0 + displayName: 'Set plugin package version string' + inputs: + scriptSource: inline + script: | + import os + import re + import subprocess + import sys + + package_version = "${{ parameters.package_version }}" + api_version = "${{ parameters.api_version }}" + api_overridden = os.environ.get("ORTAPIVERSIONOVERRIDDEN", "false").lower() == "true" + + src_root = os.environ.get("BUILD_SOURCESDIRECTORY", "") + version_file = os.path.join(src_root, "VERSION_NUMBER") + if not os.path.isfile(version_file): + print("##vso[task.logissue type=error]Cannot find VERSION_NUMBER at: {}".format(version_file)) + sys.exit(1) + + with open(version_file, "r") as f: + original_ver = f.read().strip() + + if not original_ver: + print("##vso[task.logissue type=error]VERSION_NUMBER is empty.") + sys.exit(1) + + print("Original version: {}".format(original_ver)) + print("Package version type: {}".format(package_version)) + print("API version: {}".format(api_version)) + print("API overridden: {}".format(api_overridden)) + + if package_version == "release": + if api_overridden: + version_string = "{}+api{}".format(original_ver, api_version) + else: + version_string = original_ver + + elif package_version == "RC": + # TODO: implement RC versioning + version_string = "{}-rc".format(original_ver) + print("##vso[task.logissue type=warning]RC versioning is not yet fully implemented.") + + elif package_version == "dev": + try: + commit_sha = subprocess.check_output( + ["git", "rev-parse", "--short=8", "HEAD"], + cwd=src_root + ).decode("utf-8").strip() + date_str = subprocess.check_output( + ["git", "show", "-s", "--format=%cd", "--date=format:%Y%m%d", "HEAD"], + cwd=src_root + ).decode("utf-8").strip() + except Exception as e: + print("##vso[task.logissue type=error]Failed to get git info: {}".format(e)) + sys.exit(1) + version_string = "{}-dev.{}+{}".format(original_ver, date_str, commit_sha) + if api_overridden: + version_string += "-api{}".format(api_version) + + else: + print("##vso[task.logissue type=error]Unknown package_version '{}'. Must be 'release', 'RC', or 'dev'.".format(package_version)) + sys.exit(1) + + print("Plugin package version string: {}".format(version_string)) + + # Validate semver 2.0.0 format + semver_pattern = r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$" + if not re.match(semver_pattern, version_string): + print("##vso[task.logissue type=error]Version string '{}' is not valid semver 2.0.0.".format(version_string)) + sys.exit(1) + + print("##vso[task.setvariable variable=PluginPackageVersion]{}".format(version_string)) + print("##vso[task.setvariable variable=PluginEpVersionDefine]onnxruntime_PLUGIN_EP_VERSION={}".format(version_string)) From 23782b980df864e7ebcab9dfc3132b1dc0d65cfe Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Tue, 24 Mar 2026 16:43:46 -0700 Subject: [PATCH 02/29] 2 --- .../stages/plugin-win-webgpu-stage.yml | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml index 2daced826222b..091cdb69772ec 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml @@ -51,6 +51,10 @@ stages: enabled: true scanOutputDirectoryOnly: true outputs: + - ${{ if eq(parameters.arch, 'x64') }}: + - output: pipelineArtifact + targetPath: '$(Build.ArtifactStagingDirectory)\WebGPU_BuildTools' + artifactName: WebGPU_BuildTools_x64 - output: pipelineArtifact targetPath: $(Build.ArtifactStagingDirectory) artifactName: webgpu_plugin_win_${{ parameters.arch }} @@ -60,6 +64,8 @@ stages: value: '-Dorg.gradle.daemon=false' - name: VSGenerator value: 'Visual Studio 17 2022' + - name: CrossCompileDefines + value: '' steps: - checkout: self clean: true @@ -113,6 +119,22 @@ stages: inputs: workingFile: '$(Build.SourcesDirectory)/onnxruntime/core/providers/webgpu/wgsl_templates/.npmrc' + - ${{ if eq(parameters.arch, 'arm64') }}: + - task: DownloadPipelineArtifact@2 + displayName: 'Download WebGPU build tools from x64 build' + inputs: + artifactName: 'WebGPU_BuildTools_x64' + targetPath: '$(Build.BinariesDirectory)\WebGPU_BuildTools' + - script: | + @echo ##vso[task.setvariable variable=LLVM_TABLEGEN_PATH]$(Build.BinariesDirectory)\WebGPU_BuildTools\llvm-tblgen.exe + @echo ##vso[task.setvariable variable=CLANG_TABLEGEN_PATH]$(Build.BinariesDirectory)\WebGPU_BuildTools\clang-tblgen.exe + displayName: 'Set tablegen paths' + - powershell: | + Write-Host "Using LLVM_TABLEGEN_PATH: $(LLVM_TABLEGEN_PATH)" + Write-Host "Using CLANG_TABLEGEN_PATH: $(CLANG_TABLEGEN_PATH)" + Write-Host "##vso[task.setvariable variable=CrossCompileDefines]LLVM_TABLEGEN=$(LLVM_TABLEGEN_PATH) CLANG_TABLEGEN=$(CLANG_TABLEGEN_PATH)" + displayName: 'Set build flags for WebGPU cross-compilation' + - task: PythonScript@0 displayName: 'Build' inputs: @@ -133,10 +155,17 @@ stages: --wgsl_template static --disable_rtti --enable_lto - --cmake_extra_defines onnxruntime_BUILD_UNIT_TESTS=ON onnxruntime_ENABLE_DAWN_BACKEND_D3D12=1 onnxruntime_ENABLE_DAWN_BACKEND_VULKAN=1 $(PluginEpVersionDefine) + --cmake_extra_defines onnxruntime_BUILD_UNIT_TESTS=ON onnxruntime_ENABLE_DAWN_BACKEND_D3D12=1 onnxruntime_ENABLE_DAWN_BACKEND_VULKAN=1 $(PluginEpVersionDefine) $(CrossCompileDefines) $(TelemetryOption) workingDirectory: '$(Build.BinariesDirectory)' + - ${{ if eq(parameters.arch, 'x64') }}: + - script: | + mkdir $(Build.ArtifactStagingDirectory)\WebGPU_BuildTools + copy $(Build.BinariesDirectory)\${{ parameters.cmake_build_type }}\_deps\dawn-build\third_party\dxc\RelWithDebInfo\bin\llvm-tblgen.exe $(Build.ArtifactStagingDirectory)\WebGPU_BuildTools + copy $(Build.BinariesDirectory)\${{ parameters.cmake_build_type }}\_deps\dawn-build\third_party\dxc\RelWithDebInfo\bin\clang-tblgen.exe $(Build.ArtifactStagingDirectory)\WebGPU_BuildTools + displayName: 'Copy WebGPU build tools' + # Esrp signing - template: ../templates/win-esrp-dll.yml parameters: From 0a8b2403aecb893831317f0b2019589c40fc9038 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Tue, 24 Mar 2026 16:53:13 -0700 Subject: [PATCH 03/29] linux --- .../stages/plugin-linux-webgpu-stage.yml | 7 +-- .../linux/build_webgpu_plugin_package.sh | 46 +++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 tools/ci_build/github/linux/build_webgpu_plugin_package.sh diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml index 4610c678b126b..3a80d13f52b36 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml @@ -61,12 +61,9 @@ stages: DockerBuildArgs: "--build-arg BASEIMAGE=${{ parameters.docker_base_image }} --build-arg BUILD_UID=$( id -u )" Repository: onnxruntimewebgpuplugin - - task: Bash@3 + - script: $(Build.SourcesDirectory)/tools/ci_build/github/linux/build_webgpu_plugin_package.sh -i onnxruntimewebgpuplugin -c ${{ parameters.cmake_build_type }} + workingDirectory: $(Build.SourcesDirectory) displayName: 'Build WebGPU Plugin' - inputs: - targetType: filePath - filePath: tools/ci_build/github/linux/run_python_dockerbuild.sh - arguments: -i onnxruntimewebgpuplugin -d "WEBGPU" -c ${{ parameters.cmake_build_type }} env: EXTRA_CMAKE_DEFINES: $(PluginEpVersionDefine) diff --git a/tools/ci_build/github/linux/build_webgpu_plugin_package.sh b/tools/ci_build/github/linux/build_webgpu_plugin_package.sh new file mode 100644 index 0000000000000..48d29a801182f --- /dev/null +++ b/tools/ci_build/github/linux/build_webgpu_plugin_package.sh @@ -0,0 +1,46 @@ +#!/bin/bash +set -e -x + +# Build WebGPU plugin shared library for Linux inside Docker. +# This script follows the same pattern as build_nodejs_package.sh. + +BUILD_CONFIG="Release" +DOCKER_IMAGE="onnxruntimewebgpuplugin" + +while getopts "i:c:" parameter_Option +do case "${parameter_Option}" +in +i) DOCKER_IMAGE=${OPTARG};; +c) BUILD_CONFIG=${OPTARG};; +*) echo "Usage: $0 -i [-c ]" + exit 1;; +esac +done + +mkdir -p "${HOME}/.onnx" + +docker run --rm \ + --volume /data/onnx:/data/onnx:ro \ + --volume "${BUILD_SOURCESDIRECTORY}:/onnxruntime_src" \ + --volume "${BUILD_BINARIESDIRECTORY}:/build" \ + --volume /data/models:/build/models:ro \ + --volume "${HOME}/.onnx:/home/onnxruntimedev/.onnx" \ + -e NIGHTLY_BUILD \ + -e BUILD_BUILDNUMBER \ + "$DOCKER_IMAGE" \ + /bin/bash -c "/usr/bin/python3 /onnxruntime_src/tools/ci_build/build.py \ + --build_dir /build \ + --config ${BUILD_CONFIG} \ + --skip_submodule_sync \ + --parallel \ + --use_binskim_compliant_compile_flags \ + --use_webgpu shared_lib \ + --wgsl_template static \ + --disable_rtti \ + --enable_lto \ + --enable_onnx_tests \ + --use_vcpkg \ + --use_vcpkg_ms_internal_asset_cache \ + --update \ + --build \ + --cmake_extra_defines onnxruntime_BUILD_UNIT_TESTS=ON ${EXTRA_CMAKE_DEFINES}" From f7c6c34a4b2e2d9d530bf9be4934bbb399029d5e Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Tue, 24 Mar 2026 17:13:58 -0700 Subject: [PATCH 04/29] change npm feed --- .../github/azure-pipelines/stages/plugin-win-webgpu-stage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml index 091cdb69772ec..40171b398966e 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml @@ -24,7 +24,7 @@ parameters: - name: npm_registry_url type: string - default: 'https://pkgs.dev.azure.com/aiinfra/_packaging/Guardian1ESPTUpstreamOrgFeed/npm/registry/' + default: 'https://pkgs.dev.azure.com/aiinfra/_packaging/ONNXRuntime_WebGPU_BuildDependencies/npm/registry/' stages: - stage: Win_plugin_webgpu_${{ parameters.arch }}_Build From 46393457ff10da1a0faefa085d729a292aa62f7a Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Wed, 25 Mar 2026 00:44:35 +0000 Subject: [PATCH 05/29] fix linux build 2 --- tools/ci_build/github/linux/build_webgpu_plugin_package.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tools/ci_build/github/linux/build_webgpu_plugin_package.sh diff --git a/tools/ci_build/github/linux/build_webgpu_plugin_package.sh b/tools/ci_build/github/linux/build_webgpu_plugin_package.sh old mode 100644 new mode 100755 From 8cd15903a53fd09d9c17cd798726c844e6154a1c Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Tue, 24 Mar 2026 17:55:48 -0700 Subject: [PATCH 06/29] include version info --- .../stages/plugin-linux-webgpu-stage.yml | 10 ++++++++-- .../azure-pipelines/stages/plugin-mac-webgpu-stage.yml | 8 +++++++- .../azure-pipelines/stages/plugin-win-webgpu-stage.yml | 7 ++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml index 3a80d13f52b36..2f637945feb2a 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml @@ -69,6 +69,12 @@ stages: - script: | set -e -x - mkdir -p $(Build.ArtifactStagingDirectory)/plugin - cp $(Build.BinariesDirectory)/${{ parameters.cmake_build_type }}/libonnxruntime_providers_webgpu.so $(Build.ArtifactStagingDirectory)/plugin/ || true + mkdir -p $(Build.ArtifactStagingDirectory)/bin + cp $(Build.BinariesDirectory)/${{ parameters.cmake_build_type }}/libonnxruntime_providers_webgpu.so $(Build.ArtifactStagingDirectory)/bin/ || true displayName: 'Copy plugin binaries' + + - script: | + set -e -x + mkdir -p "$(Build.ArtifactStagingDirectory)/version" + touch "$(Build.ArtifactStagingDirectory)/version/$(PluginPackageVersion)" + displayName: 'Create version marker file' diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml index 479182ff37c1d..b3ab300739ad3 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml @@ -81,4 +81,10 @@ stages: SourceFolder: '$(Build.SourcesDirectory)/build/${{ parameters.cmake_build_type }}' Contents: | libonnxruntime_providers_webgpu.dylib - TargetFolder: '$(Build.ArtifactStagingDirectory)' + TargetFolder: '$(Build.ArtifactStagingDirectory)/bin' + + - script: | + set -e -x + mkdir -p "$(Build.ArtifactStagingDirectory)/version" + touch "$(Build.ArtifactStagingDirectory)/version/$(PluginPackageVersion)" + displayName: 'Create version marker file' diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml index 40171b398966e..c75b8cff14f5f 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml @@ -183,4 +183,9 @@ stages: onnxruntime_providers_webgpu.pdb dxcompiler.dll dxil.dll - TargetFolder: '$(Build.ArtifactStagingDirectory)' + TargetFolder: '$(Build.ArtifactStagingDirectory)\bin' + + - script: | + mkdir "$(Build.ArtifactStagingDirectory)\version" + type nul > "$(Build.ArtifactStagingDirectory)\version\$(PluginPackageVersion)" + displayName: 'Create version marker file' From 794c6a3b2114d7ca7f80a8a84fc0d8264ba7ede9 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Tue, 24 Mar 2026 18:00:03 -0700 Subject: [PATCH 07/29] fix build tool path --- .../github/azure-pipelines/stages/plugin-win-webgpu-stage.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml index c75b8cff14f5f..d6df24c887bd0 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml @@ -162,8 +162,8 @@ stages: - ${{ if eq(parameters.arch, 'x64') }}: - script: | mkdir $(Build.ArtifactStagingDirectory)\WebGPU_BuildTools - copy $(Build.BinariesDirectory)\${{ parameters.cmake_build_type }}\_deps\dawn-build\third_party\dxc\RelWithDebInfo\bin\llvm-tblgen.exe $(Build.ArtifactStagingDirectory)\WebGPU_BuildTools - copy $(Build.BinariesDirectory)\${{ parameters.cmake_build_type }}\_deps\dawn-build\third_party\dxc\RelWithDebInfo\bin\clang-tblgen.exe $(Build.ArtifactStagingDirectory)\WebGPU_BuildTools + copy $(Build.BinariesDirectory)\${{ parameters.cmake_build_type }}\_deps\dawn-build\third_party\dxc\${{ parameters.cmake_build_type }}\bin\llvm-tblgen.exe $(Build.ArtifactStagingDirectory)\WebGPU_BuildTools + copy $(Build.BinariesDirectory)\${{ parameters.cmake_build_type }}\_deps\dawn-build\third_party\dxc\${{ parameters.cmake_build_type }}\bin\clang-tblgen.exe $(Build.ArtifactStagingDirectory)\WebGPU_BuildTools displayName: 'Copy WebGPU build tools' # Esrp signing From 1492f574c733b186ca346862007a4fcb16270286 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Wed, 25 Mar 2026 01:17:02 +0000 Subject: [PATCH 08/29] fix linux build 3 --- include/onnxruntime/ep/adapter/allocator.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/onnxruntime/ep/adapter/allocator.h b/include/onnxruntime/ep/adapter/allocator.h index 4f107ae72c0e9..c1d4bcaf77017 100644 --- a/include/onnxruntime/ep/adapter/allocator.h +++ b/include/onnxruntime/ep/adapter/allocator.h @@ -7,6 +7,8 @@ #error "This header should not be included directly. Include ep/adapters.h instead." #endif +#include + #include "core/framework/allocator.h" namespace onnxruntime { From ad6d885266bd186ad8ca18f38f079213f9a52a5f Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Wed, 25 Mar 2026 02:57:53 +0000 Subject: [PATCH 09/29] exclude build tools from code sign --- .../github/azure-pipelines/stages/plugin-win-webgpu-stage.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml index d6df24c887bd0..52006b42654db 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml @@ -45,6 +45,7 @@ stages: codeSignValidation: enabled: true break: true + additionalTargetsGlobPattern: '-|**\clang-tblgen.exe;-|**\llvm-tblgen.exe' psscriptanalyzer: enabled: true binskim: From 781627ec37ee0d337e9159ed4f75383e570ac938 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Wed, 25 Mar 2026 01:13:25 -0700 Subject: [PATCH 10/29] dxc copy --- .../stages/plugin-win-webgpu-stage.yml | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml index 52006b42654db..664e0120f84d4 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml @@ -167,6 +167,40 @@ stages: copy $(Build.BinariesDirectory)\${{ parameters.cmake_build_type }}\_deps\dawn-build\third_party\dxc\${{ parameters.cmake_build_type }}\bin\clang-tblgen.exe $(Build.ArtifactStagingDirectory)\WebGPU_BuildTools displayName: 'Copy WebGPU build tools' + - powershell: | + $dxcZipUrl = "https://github.com/microsoft/DirectXShaderCompiler/releases/download/v1.8.2502/dxc_2025_02_20.zip" + $dxcZipPath = "$(Build.BinariesDirectory)\dxc.zip" + $dxcExtractPath = "$(Build.BinariesDirectory)\dxc_extracted" + $targetArch = "${{ parameters.arch }}" + + # Download the DXC package + Write-Host "Downloading DXC release from $dxcZipUrl" + Invoke-WebRequest -Uri $dxcZipUrl -OutFile $dxcZipPath + + # Create extraction directory + if (-not (Test-Path $dxcExtractPath)) { + New-Item -Path $dxcExtractPath -ItemType Directory -Force + } + + # Extract the zip file + Write-Host "Extracting DXC package to $dxcExtractPath" + Expand-Archive -Path $dxcZipPath -DestinationPath $dxcExtractPath -Force + + # Copy the necessary DLLs to the target directory + $sourcePath = Join-Path $dxcExtractPath "bin\$targetArch" + $targetPath = "$(Build.ArtifactStagingDirectory)\bin" + + if (-not (Test-Path $targetPath)) { + New-Item -Path $targetPath -ItemType Directory -Force + } + + Write-Host "Copying dxil.dll and dxcompiler.dll from $sourcePath to $targetPath" + Copy-Item -Path "$sourcePath\dxil.dll" -Destination $targetPath -Force + Copy-Item -Path "$sourcePath\dxcompiler.dll" -Destination $targetPath -Force + + Write-Host "DXC DLLs successfully copied to the target directory" + displayName: 'Download and Copy DXC Binaries' + # Esrp signing - template: ../templates/win-esrp-dll.yml parameters: @@ -182,8 +216,6 @@ stages: Contents: | onnxruntime_providers_webgpu.dll onnxruntime_providers_webgpu.pdb - dxcompiler.dll - dxil.dll TargetFolder: '$(Build.ArtifactStagingDirectory)\bin' - script: | From 7334ea98757f337c9cf004de6d662d74fd5764e4 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Wed, 25 Mar 2026 01:46:15 -0700 Subject: [PATCH 11/29] publish U package --- .../stages/plugin-linux-webgpu-stage.yml | 16 ++++++++++++++++ .../stages/plugin-mac-webgpu-stage.yml | 16 ++++++++++++++++ .../stages/plugin-win-webgpu-stage.yml | 17 +++++++++++++++++ .../set-plugin-build-variables-step.yml | 11 +++++++++++ 4 files changed, 60 insertions(+) diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml index 2f637945feb2a..2eb3a50bc57d3 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml @@ -78,3 +78,19 @@ stages: mkdir -p "$(Build.ArtifactStagingDirectory)/version" touch "$(Build.ArtifactStagingDirectory)/version/$(PluginPackageVersion)" displayName: 'Create version marker file' + + - script: | + set -e -x + mkdir -p "$(Build.BinariesDirectory)/universal_package" + cp -R "$(Build.ArtifactStagingDirectory)/bin/"* "$(Build.BinariesDirectory)/universal_package/" + displayName: 'Stage binaries for universal package' + + - task: UniversalPackages@0 + displayName: 'Publish universal package' + inputs: + command: publish + publishDirectory: '$(Build.BinariesDirectory)/universal_package' + vstsFeedPublish: 'PublicPackages/ORT-Nightly' + vstsFeedPackagePublish: 'onnxruntime-plugin-ep-webgpu-linux-x64-api${{ parameters.api_version }}' + versionOption: custom + versionPublish: '$(PluginUniversalPackageVersion)' diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml index b3ab300739ad3..49c6ba28e0c0c 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml @@ -88,3 +88,19 @@ stages: mkdir -p "$(Build.ArtifactStagingDirectory)/version" touch "$(Build.ArtifactStagingDirectory)/version/$(PluginPackageVersion)" displayName: 'Create version marker file' + + - script: | + set -e -x + mkdir -p "$(Build.BinariesDirectory)/universal_package" + cp -R "$(Build.ArtifactStagingDirectory)/bin/"* "$(Build.BinariesDirectory)/universal_package/" + displayName: 'Stage binaries for universal package' + + - task: UniversalPackages@0 + displayName: 'Publish universal package' + inputs: + command: publish + publishDirectory: '$(Build.BinariesDirectory)/universal_package' + vstsFeedPublish: 'PublicPackages/ORT-Nightly' + vstsFeedPackagePublish: 'onnxruntime-plugin-ep-webgpu-macos-arm64-api${{ parameters.api_version }}' + versionOption: custom + versionPublish: '$(PluginUniversalPackageVersion)' diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml index 664e0120f84d4..9ba0cbe2ed70a 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml @@ -222,3 +222,20 @@ stages: mkdir "$(Build.ArtifactStagingDirectory)\version" type nul > "$(Build.ArtifactStagingDirectory)\version\$(PluginPackageVersion)" displayName: 'Create version marker file' + + - task: CopyFiles@2 + displayName: 'Stage binaries for universal package' + inputs: + SourceFolder: '$(Build.ArtifactStagingDirectory)\bin' + Contents: '**' + TargetFolder: '$(Build.BinariesDirectory)\universal_package' + + - task: UniversalPackages@0 + displayName: 'Publish universal package' + inputs: + command: publish + publishDirectory: '$(Build.BinariesDirectory)\universal_package' + vstsFeedPublish: 'PublicPackages/ORT-Nightly' + vstsFeedPackagePublish: 'onnxruntime-plugin-ep-webgpu-win-${{ parameters.arch }}-api${{ parameters.api_version }}' + versionOption: custom + versionPublish: '$(PluginUniversalPackageVersion)' diff --git a/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml b/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml index eae6e294a0828..41148466ea50c 100644 --- a/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml +++ b/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml @@ -107,10 +107,12 @@ steps: version_string = "{}+api{}".format(original_ver, api_version) else: version_string = original_ver + universal_version = original_ver elif package_version == "RC": # TODO: implement RC versioning version_string = "{}-rc".format(original_ver) + universal_version = version_string print("##vso[task.logissue type=warning]RC versioning is not yet fully implemented.") elif package_version == "dev": @@ -129,12 +131,14 @@ steps: version_string = "{}-dev.{}+{}".format(original_ver, date_str, commit_sha) if api_overridden: version_string += "-api{}".format(api_version) + universal_version = "{}-dev.{}.{}".format(original_ver, date_str, commit_sha) else: print("##vso[task.logissue type=error]Unknown package_version '{}'. Must be 'release', 'RC', or 'dev'.".format(package_version)) sys.exit(1) print("Plugin package version string: {}".format(version_string)) + print("Plugin universal package version string: {}".format(universal_version)) # Validate semver 2.0.0 format semver_pattern = r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$" @@ -142,5 +146,12 @@ steps: print("##vso[task.logissue type=error]Version string '{}' is not valid semver 2.0.0.".format(version_string)) sys.exit(1) + # Validate universal version (SemVer 1.0.0 - no build metadata) + universal_semver_pattern = r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?$" + if not re.match(universal_semver_pattern, universal_version): + print("##vso[task.logissue type=error]Universal version string '{}' is not valid semver 1.0.0.".format(universal_version)) + sys.exit(1) + print("##vso[task.setvariable variable=PluginPackageVersion]{}".format(version_string)) + print("##vso[task.setvariable variable=PluginUniversalPackageVersion]{}".format(universal_version)) print("##vso[task.setvariable variable=PluginEpVersionDefine]onnxruntime_PLUGIN_EP_VERSION={}".format(version_string)) From fe278cd5f61574b86fb588e4643b81ddd495c378 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Wed, 25 Mar 2026 02:08:07 -0700 Subject: [PATCH 12/29] validate params --- .../plugin-webgpu-pipeline.yml | 50 +++++++++++++++---- .../stages/plugin-webgpu-packaging-stage.yml | 17 ------- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/tools/ci_build/github/azure-pipelines/plugin-webgpu-pipeline.yml b/tools/ci_build/github/azure-pipelines/plugin-webgpu-pipeline.yml index 8c80838aea752..f8192486caf78 100644 --- a/tools/ci_build/github/azure-pipelines/plugin-webgpu-pipeline.yml +++ b/tools/ci_build/github/azure-pipelines/plugin-webgpu-pipeline.yml @@ -54,6 +54,14 @@ parameters: - RelWithDebInfo - MinSizeRel +variables: + # Windows ARM64 build requires Windows x64 build to be enabled (ARM64 cross-compilation depends on x64 build artifacts) + - name: invalidARM64Config + value: ${{ and(eq(parameters.build_windows_arm64, true), eq(parameters.build_windows_x64, false)) }} + # Non-dev package versions (release, RC) must use Release build type + - name: invalidBuildTypeConfig + value: ${{ and(ne(parameters.package_version, 'dev'), ne(parameters.cmake_build_type, 'Release')) }} + extends: template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines parameters: @@ -83,12 +91,36 @@ extends: os: windows stages: - - template: stages/plugin-webgpu-packaging-stage.yml - parameters: - build_windows_x64: ${{ parameters.build_windows_x64 }} - build_windows_arm64: ${{ parameters.build_windows_arm64 }} - build_linux_x64: ${{ parameters.build_linux_x64 }} - build_macos_arm64: ${{ parameters.build_macos_arm64 }} - api_version: ${{ parameters.api_version }} - package_version: ${{ parameters.package_version }} - cmake_build_type: ${{ parameters.cmake_build_type }} + # Validate parameter combinations + - ${{ if or(eq(variables['invalidARM64Config'], 'True'), eq(variables['invalidBuildTypeConfig'], 'True')) }}: + - stage: Validate_Parameters + displayName: 'Validate Parameters' + dependsOn: [] + jobs: + - job: Validate + displayName: 'Validate parameter combinations' + pool: + name: 'onnxruntime-Win-CPU-VS2022-Latest' + os: windows + steps: + - checkout: none + - ${{ if eq(variables['invalidARM64Config'], 'True') }}: + - script: | + echo "##vso[task.logissue type=error]Windows ARM64 build requires Windows x64 build to be enabled." + exit 1 + displayName: 'ERROR: Windows ARM64 requires Windows x64' + - ${{ if eq(variables['invalidBuildTypeConfig'], 'True') }}: + - script: | + echo "##vso[task.logissue type=error]Non-dev package version requires Release build type." + exit 1 + displayName: 'ERROR: Non-dev package version requires Release build type' + - ${{ else }}: + - template: stages/plugin-webgpu-packaging-stage.yml + parameters: + build_windows_x64: ${{ parameters.build_windows_x64 }} + build_windows_arm64: ${{ parameters.build_windows_arm64 }} + build_linux_x64: ${{ parameters.build_linux_x64 }} + build_macos_arm64: ${{ parameters.build_macos_arm64 }} + api_version: ${{ parameters.api_version }} + package_version: ${{ parameters.package_version }} + cmake_build_type: ${{ parameters.cmake_build_type }} diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml index 0a2cbfa05f0dd..dad6ece326a15 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml @@ -40,23 +40,6 @@ parameters: - MinSizeRel stages: - # Validate parameter combinations - - ${{ if and(eq(parameters.build_windows_arm64, true), eq(parameters.build_windows_x64, false)) }}: - - stage: Validate_Parameters - displayName: 'Validate Parameters' - dependsOn: [] - jobs: - - job: Validate - displayName: 'Validate parameter combinations' - pool: - name: 'onnxruntime-Win-CPU-VS2022-Latest' - os: windows - steps: - - script: | - echo "##vso[task.logissue type=error]Windows ARM64 build requires Windows x64 build to be enabled." - exit 1 - displayName: 'ERROR: Windows ARM64 requires Windows x64' - # Windows x64 - ${{ if eq(parameters.build_windows_x64, true) }}: - template: plugin-win-webgpu-stage.yml From e4f654db4aa1c30b029ca2624ba7c593bd19897a Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Sat, 28 Mar 2026 12:32:03 -0700 Subject: [PATCH 13/29] use version string to detect ORT API version --- include/onnxruntime/ep/api.h | 56 ++++++++++++++++++- onnxruntime/core/session/onnxruntime_c_api.cc | 30 ++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/include/onnxruntime/ep/api.h b/include/onnxruntime/ep/api.h index 36d99e5d44d45..acd765285868f 100644 --- a/include/onnxruntime/ep/api.h +++ b/include/onnxruntime/ep/api.h @@ -22,8 +22,35 @@ struct ApiPtrs { namespace detail { inline std::optional g_api_ptrs; + +inline bool TryGetAPIVersionFromVersionString(const char* version_str, uint32_t& api_version) { + // A valid version string should always be in the format of "1.{API_VERSION}.*". + if (!version_str || version_str[0] != '1' || version_str[1] != '.') { + return false; + } + const char* p = version_str + 2; + if (*p < '0' || *p > '9') { + return false; + } + uint32_t version = 0; + constexpr uint32_t kMaxBeforeMultiply = UINT32_MAX / 10; + while (*p >= '0' && *p <= '9') { + uint32_t digit = static_cast(*p - '0'); + if (version > kMaxBeforeMultiply || (version == kMaxBeforeMultiply && digit > UINT32_MAX % 10)) { + return false; + } + version = version * 10 + digit; + ++p; + } + if (*p != '.' && *p != '\0') { + return false; + } + api_version = version; + return true; } +} // namespace detail + /// /// Get the global instance of ApiPtrs. /// @@ -35,10 +62,35 @@ inline const ApiPtrs& Api() { /// Initialize the EP API pointers and global OrtEnv if not already done. /// inline void ApiInit(const OrtApiBase* ort_api_base) { - // Manual init for the C++ API - const OrtApi* ort_api = ort_api_base->GetApi(ORT_API_VERSION); + // The following initialization process is composed of 3 steps: + // 1) Get the ORT API version string + // 2) Try to parse the ORT API version from the version string. If parsing fails, we assume the version is 24. + // 3) Get the ORT API for the parsed version and initialize the global API instance with it. + constexpr uint32_t ORT_BASE_API_VERSION = 24; + const char* version_str = ort_api_base->GetVersionString(); + if (!version_str) { + version_str = "unknown"; + } + uint32_t current_ort_version = 0; + if (!detail::TryGetAPIVersionFromVersionString(version_str, current_ort_version)) { + // If we fail to parse the version string, we can still try to get the API for the base version and hope it works. + current_ort_version = ORT_BASE_API_VERSION; + } + if (current_ort_version < ORT_BASE_API_VERSION) { + throw std::runtime_error("Failed to initialize EP API: the minimum required ORT API version is " + std::to_string(ORT_BASE_API_VERSION) + + ", but the current version is \"" + version_str + + "\" (parsed API version: " + std::to_string(current_ort_version) + ")."); + } + + const OrtApi* ort_api = ort_api_base->GetApi(current_ort_version); + if (!ort_api) { + throw std::runtime_error("Failed to initialize EP API: the current ORT version is \"" + std::string(version_str) + + "\" but it does not support the parsed API version " + std::to_string(current_ort_version) + "."); + } const OrtEpApi* ep_api = ort_api->GetEpApi(); const OrtModelEditorApi* model_editor_api = ort_api->GetModelEditorApi(); + + // Manual init for the C++ API Ort::InitApi(ort_api); // Initialize the global API instance diff --git a/onnxruntime/core/session/onnxruntime_c_api.cc b/onnxruntime/core/session/onnxruntime_c_api.cc index 37a74a5de22a6..48f4f0c1153b3 100644 --- a/onnxruntime/core/session/onnxruntime_c_api.cc +++ b/onnxruntime/core/session/onnxruntime_c_api.cc @@ -4875,7 +4875,37 @@ ORT_API(const OrtApi*, OrtApis::GetApi, uint32_t version) { return nullptr; // Unsupported version } +namespace { +consteval bool IsOrtVersionValid() { + // This is a consteval function to validate the format of ORT_VERSION at compile time. + // It should be in the format "X.Y.Z" where X == 1, Y and Z are non-negative integers. + std::string_view version(ORT_VERSION); + size_t first_dot = version.find('.'); + if (first_dot == std::string_view::npos || first_dot == 0 || first_dot == version.size() - 1) { + return false; // Must have two dots and cannot start or end with a dot + } + size_t second_dot = version.find('.', first_dot + 1); + if (second_dot == std::string_view::npos || second_dot == first_dot + 1 || second_dot == version.size() - 1) { + return false; // Must have two dots and cannot be adjacent or end with a dot + } + std::string_view major = version.substr(0, first_dot); + std::string_view minor = version.substr(first_dot + 1, second_dot - first_dot - 1); + std::string_view patch = version.substr(second_dot + 1); + if (major != "1") { + return false; // Major version must be 1 + } + auto is_non_negative_integer = [](std::string_view str) { + return !str.empty() && std::all_of(str.begin(), str.end(), ::isdigit); + }; + if (!is_non_negative_integer(minor) || !is_non_negative_integer(patch)) { + return false; // Minor and patch versions must be non-negative integers + } + return true; +} +} // namespace + ORT_API(const char*, OrtApis::GetVersionString) { + static_assert(IsOrtVersionValid(), "ORT_VERSION must be in the format '1.Y.Z' where Y and Z are non-negative integers"); return ORT_VERSION; } From f6d38f5f7e0caf841cbd656dc9db0561d7f1db75 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Sat, 28 Mar 2026 12:45:40 -0700 Subject: [PATCH 14/29] update packaging pipeline --- .../plugin-webgpu-pipeline.yml | 9 --- .../stages/plugin-linux-webgpu-stage.yml | 7 +- .../stages/plugin-mac-webgpu-stage.yml | 7 +- .../stages/plugin-webgpu-packaging-stage.yml | 9 --- .../stages/plugin-win-webgpu-stage.yml | 7 +- .../set-plugin-build-variables-step.yml | 80 +------------------ 6 files changed, 7 insertions(+), 112 deletions(-) diff --git a/tools/ci_build/github/azure-pipelines/plugin-webgpu-pipeline.yml b/tools/ci_build/github/azure-pipelines/plugin-webgpu-pipeline.yml index f8192486caf78..a9cfc2139fb95 100644 --- a/tools/ci_build/github/azure-pipelines/plugin-webgpu-pipeline.yml +++ b/tools/ci_build/github/azure-pipelines/plugin-webgpu-pipeline.yml @@ -28,14 +28,6 @@ parameters: type: boolean default: false -- name: api_version - displayName: 'API Version' - type: number - values: - - 24 - - 25 - default: 24 - - name: package_version displayName: 'Package Version' type: string @@ -121,6 +113,5 @@ extends: build_windows_arm64: ${{ parameters.build_windows_arm64 }} build_linux_x64: ${{ parameters.build_linux_x64 }} build_macos_arm64: ${{ parameters.build_macos_arm64 }} - api_version: ${{ parameters.api_version }} package_version: ${{ parameters.package_version }} cmake_build_type: ${{ parameters.cmake_build_type }} diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml index 2eb3a50bc57d3..825fb88e019b0 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml @@ -3,10 +3,6 @@ parameters: type: string default: 'onnxruntime-Ubuntu2404-AMD-CPU' -- name: api_version - type: number - default: 24 - - name: package_version type: string default: dev @@ -51,7 +47,6 @@ stages: - template: ../templates/set-plugin-build-variables-step.yml parameters: - api_version: ${{ parameters.api_version }} package_version: ${{ parameters.package_version }} - template: ../templates/get-docker-image-steps.yml @@ -91,6 +86,6 @@ stages: command: publish publishDirectory: '$(Build.BinariesDirectory)/universal_package' vstsFeedPublish: 'PublicPackages/ORT-Nightly' - vstsFeedPackagePublish: 'onnxruntime-plugin-ep-webgpu-linux-x64-api${{ parameters.api_version }}' + vstsFeedPackagePublish: 'onnxruntime-plugin-ep-webgpu-linux-x64' versionOption: custom versionPublish: '$(PluginUniversalPackageVersion)' diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml index 49c6ba28e0c0c..6852954346e4b 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml @@ -1,8 +1,4 @@ parameters: -- name: api_version - type: number - default: 24 - - name: package_version type: string default: dev @@ -55,7 +51,6 @@ stages: - template: ../templates/set-plugin-build-variables-step.yml parameters: - api_version: ${{ parameters.api_version }} package_version: ${{ parameters.package_version }} - script: | @@ -101,6 +96,6 @@ stages: command: publish publishDirectory: '$(Build.BinariesDirectory)/universal_package' vstsFeedPublish: 'PublicPackages/ORT-Nightly' - vstsFeedPackagePublish: 'onnxruntime-plugin-ep-webgpu-macos-arm64-api${{ parameters.api_version }}' + vstsFeedPackagePublish: 'onnxruntime-plugin-ep-webgpu-macos-arm64' versionOption: custom versionPublish: '$(PluginUniversalPackageVersion)' diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml index dad6ece326a15..d3a9cf3fbb133 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml @@ -19,11 +19,6 @@ parameters: type: boolean default: false -- name: api_version - displayName: 'API Version' - type: number - default: 24 - - name: package_version displayName: 'Package Version' type: string @@ -45,7 +40,6 @@ stages: - template: plugin-win-webgpu-stage.yml parameters: arch: 'x64' - api_version: ${{ parameters.api_version }} package_version: ${{ parameters.package_version }} cmake_build_type: ${{ parameters.cmake_build_type }} @@ -54,7 +48,6 @@ stages: - template: plugin-win-webgpu-stage.yml parameters: arch: 'arm64' - api_version: ${{ parameters.api_version }} package_version: ${{ parameters.package_version }} cmake_build_type: ${{ parameters.cmake_build_type }} @@ -62,7 +55,6 @@ stages: - ${{ if eq(parameters.build_linux_x64, true) }}: - template: plugin-linux-webgpu-stage.yml parameters: - api_version: ${{ parameters.api_version }} package_version: ${{ parameters.package_version }} cmake_build_type: ${{ parameters.cmake_build_type }} @@ -70,6 +62,5 @@ stages: - ${{ if eq(parameters.build_macos_arm64, true) }}: - template: plugin-mac-webgpu-stage.yml parameters: - api_version: ${{ parameters.api_version }} package_version: ${{ parameters.package_version }} cmake_build_type: ${{ parameters.cmake_build_type }} diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml index 9ba0cbe2ed70a..687288cd9647b 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml @@ -5,10 +5,6 @@ parameters: - x64 - arm64 -- name: api_version - type: number - default: 24 - - name: package_version type: string default: dev @@ -80,7 +76,6 @@ stages: - template: ../templates/set-plugin-build-variables-step.yml parameters: - api_version: ${{ parameters.api_version }} package_version: ${{ parameters.package_version }} - script: | @@ -236,6 +231,6 @@ stages: command: publish publishDirectory: '$(Build.BinariesDirectory)\universal_package' vstsFeedPublish: 'PublicPackages/ORT-Nightly' - vstsFeedPackagePublish: 'onnxruntime-plugin-ep-webgpu-win-${{ parameters.arch }}-api${{ parameters.api_version }}' + vstsFeedPackagePublish: 'onnxruntime-plugin-ep-webgpu-win-${{ parameters.arch }}' versionOption: custom versionPublish: '$(PluginUniversalPackageVersion)' diff --git a/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml b/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml index 41148466ea50c..b761d5dbf4583 100644 --- a/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml +++ b/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml @@ -1,75 +1,12 @@ -# This file is used to set variables for plugin build stages. It does the following: -# -# 1. Verify and override the target ORT_API_VERSION if necessary. -# -# 2. Set the package version variable based on the build type (nightly, official, or dev). +# This file is used to set variables for plugin build stages. It sets the package version +# variable based on the build type (nightly, official, or dev). parameters: -- name: api_version - type: number - - name: package_version type: string steps: -# Step 1: Verify and override ORT_API_VERSION -- task: PythonScript@0 - displayName: 'Verify and override ORT_API_VERSION' - inputs: - scriptSource: inline - script: | - import os - import re - import sys - - raw_value = "${{ parameters.api_version }}" - if not raw_value.strip(): - print("##vso[task.logissue type=error]api_version parameter is empty.") - sys.exit(1) - try: - api_version = int(raw_value) - except ValueError: - print("##vso[task.logissue type=error]api_version must be an integer, got '{}'".format(raw_value)) - sys.exit(1) - - if api_version < 24: - print("##vso[task.logissue type=error]api_version must be >= 24, got {}".format(api_version)) - sys.exit(1) - - src_root = os.environ.get("BUILD_SOURCESDIRECTORY", "") - header_path = os.path.join(src_root, "include", "onnxruntime", "core", "session", "onnxruntime_c_api.h") - - if not os.path.isfile(header_path): - print("##vso[task.logissue type=error]Cannot find onnxruntime_c_api.h at: {}".format(header_path)) - sys.exit(1) - - with open(header_path, "r", encoding="utf-8") as f: - content = f.read() - - pattern = r"(#define\s+ORT_API_VERSION\s+)(\d+)" - matches = list(re.finditer(pattern, content)) - if len(matches) == 0: - print("##vso[task.logissue type=error]Cannot find '#define ORT_API_VERSION' in {}".format(header_path)) - sys.exit(1) - if len(matches) > 1: - print("##vso[task.logissue type=error]Found multiple '#define ORT_API_VERSION' in {}. Expected exactly one.".format(header_path)) - sys.exit(1) - - current_version = int(matches[0].group(2)) - print("Current ORT_API_VERSION: {}".format(current_version)) - print("Requested api_version: {}".format(api_version)) - - if current_version != api_version: - new_content = re.sub(pattern, r"\g<1>{}".format(api_version), content, count=1) - with open(header_path, "w", encoding="utf-8") as f: - f.write(new_content) - print("ORT_API_VERSION overridden from {} to {}".format(current_version, api_version)) - print("##vso[task.setvariable variable=OrtApiVersionOverridden]true") - else: - print("ORT_API_VERSION already matches. No change needed.") - print("##vso[task.setvariable variable=OrtApiVersionOverridden]false") - -# Step 2: Set package version string +# Set package version string - task: PythonScript@0 displayName: 'Set plugin package version string' inputs: @@ -81,8 +18,6 @@ steps: import sys package_version = "${{ parameters.package_version }}" - api_version = "${{ parameters.api_version }}" - api_overridden = os.environ.get("ORTAPIVERSIONOVERRIDDEN", "false").lower() == "true" src_root = os.environ.get("BUILD_SOURCESDIRECTORY", "") version_file = os.path.join(src_root, "VERSION_NUMBER") @@ -99,14 +34,9 @@ steps: print("Original version: {}".format(original_ver)) print("Package version type: {}".format(package_version)) - print("API version: {}".format(api_version)) - print("API overridden: {}".format(api_overridden)) if package_version == "release": - if api_overridden: - version_string = "{}+api{}".format(original_ver, api_version) - else: - version_string = original_ver + version_string = original_ver universal_version = original_ver elif package_version == "RC": @@ -129,8 +59,6 @@ steps: print("##vso[task.logissue type=error]Failed to get git info: {}".format(e)) sys.exit(1) version_string = "{}-dev.{}+{}".format(original_ver, date_str, commit_sha) - if api_overridden: - version_string += "-api{}".format(api_version) universal_version = "{}-dev.{}.{}".format(original_ver, date_str, commit_sha) else: From 113c823ce65f813222f026fccaeb8bc2993224d2 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Sat, 28 Mar 2026 14:56:39 -0700 Subject: [PATCH 15/29] fix build --- onnxruntime/core/session/onnxruntime_c_api.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/core/session/onnxruntime_c_api.cc b/onnxruntime/core/session/onnxruntime_c_api.cc index 48f4f0c1153b3..b2efa61043cb4 100644 --- a/onnxruntime/core/session/onnxruntime_c_api.cc +++ b/onnxruntime/core/session/onnxruntime_c_api.cc @@ -4895,7 +4895,7 @@ consteval bool IsOrtVersionValid() { return false; // Major version must be 1 } auto is_non_negative_integer = [](std::string_view str) { - return !str.empty() && std::all_of(str.begin(), str.end(), ::isdigit); + return !str.empty() && std::all_of(str.begin(), str.end(), [](char c) { return c >= '0' && c <= '9'; }); }; if (!is_non_negative_integer(minor) || !is_non_negative_integer(patch)) { return false; // Minor and patch versions must be non-negative integers From 1b3cee6b870a24c261303d6df0f28bdf2cc217bd Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Mon, 30 Mar 2026 17:24:35 -0700 Subject: [PATCH 16/29] resolve comments --- cmake/onnxruntime_providers_webgpu.cmake | 2 +- .../azure-pipelines/stages/plugin-linux-webgpu-stage.yml | 7 ++++++- .../azure-pipelines/stages/plugin-win-webgpu-stage.yml | 6 +----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cmake/onnxruntime_providers_webgpu.cmake b/cmake/onnxruntime_providers_webgpu.cmake index 25d786b681019..ff586bef9470e 100644 --- a/cmake/onnxruntime_providers_webgpu.cmake +++ b/cmake/onnxruntime_providers_webgpu.cmake @@ -84,7 +84,7 @@ add_definitions("-DONNX_NAMESPACE=onnx") add_definitions("-DONNX_USE_LITE_PROTO=1") - # Default plugin EP version to ORT_VERSION if not explicitly provided. + # Default plugin EP version to ORT_VERSION with "-dev" suffix if not explicitly provided. if(NOT DEFINED onnxruntime_PLUGIN_EP_VERSION) set(onnxruntime_PLUGIN_EP_VERSION "${ORT_VERSION}-dev") endif() diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml index 825fb88e019b0..00e716ff3af26 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-linux-webgpu-stage.yml @@ -65,7 +65,12 @@ stages: - script: | set -e -x mkdir -p $(Build.ArtifactStagingDirectory)/bin - cp $(Build.BinariesDirectory)/${{ parameters.cmake_build_type }}/libonnxruntime_providers_webgpu.so $(Build.ArtifactStagingDirectory)/bin/ || true + plugin_path="$(Build.BinariesDirectory)/${{ parameters.cmake_build_type }}/libonnxruntime_providers_webgpu.so" + if [ ! -f "$plugin_path" ]; then + echo "Error: Expected plugin binary not found at '$plugin_path'. Failing build to avoid publishing an invalid package." + exit 1 + fi + cp "$plugin_path" "$(Build.ArtifactStagingDirectory)/bin/" displayName: 'Copy plugin binaries' - script: | diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml index 687288cd9647b..c5929e778f028 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml @@ -101,11 +101,7 @@ stages: } # Create .npmrc file with required content - $npmrcContent = @" - registry=${{ parameters.npm_registry_url }} - - always-auth=true - "@ + $npmrcContent = "registry=${{ parameters.npm_registry_url }}`n`nalways-auth=true" Set-Content -Path $npmrcPath -Value $npmrcContent -Encoding UTF8 Write-Host "Created .npmrc at: $npmrcPath" From 2a1ffff205fca4c6395a2f573ec0fc71599621da Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Wed, 1 Apr 2026 14:37:04 -0700 Subject: [PATCH 17/29] fix build flag for arm64 --- .../azure-pipelines/stages/plugin-win-webgpu-stage.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml index c5929e778f028..c90015309d75f 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml @@ -63,6 +63,11 @@ stages: value: 'Visual Studio 17 2022' - name: CrossCompileDefines value: '' + - name: ArchFlag + ${{ if eq(parameters.arch, 'arm64') }}: + value: '--arm64' + ${{ else }}: + value: '' steps: - checkout: self clean: true @@ -132,6 +137,7 @@ stages: inputs: scriptPath: '$(Build.SourcesDirectory)\tools\ci_build\build.py' arguments: >- + $(ArchFlag) --config ${{ parameters.cmake_build_type }} --build_dir $(Build.BinariesDirectory) --skip_submodule_sync From 31c4f4c5021e9a4de8f83dd6a70cee7f5ba7ef96 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Wed, 1 Apr 2026 15:08:32 -0700 Subject: [PATCH 18/29] apply fix according to comments --- onnxruntime/core/session/onnxruntime_c_api.cc | 50 +++++++++++++------ onnxruntime/test/shared_lib/test_version.cc | 31 ------------ 2 files changed, 34 insertions(+), 47 deletions(-) delete mode 100644 onnxruntime/test/shared_lib/test_version.cc diff --git a/onnxruntime/core/session/onnxruntime_c_api.cc b/onnxruntime/core/session/onnxruntime_c_api.cc index 2474dddbcd2ed..d8d42bf38cebb 100644 --- a/onnxruntime/core/session/onnxruntime_c_api.cc +++ b/onnxruntime/core/session/onnxruntime_c_api.cc @@ -4830,36 +4830,54 @@ ORT_API(const OrtApi*, OrtApis::GetApi, uint32_t version) { } namespace { -consteval bool IsOrtVersionValid() { - // This is a consteval function to validate the format of ORT_VERSION at compile time. - // It should be in the format "X.Y.Z" where X == 1, Y and Z are non-negative integers. - std::string_view version(ORT_VERSION); +// Parse a non-negative integer from a string_view without leading zeros. +// Returns -1 on failure (empty, leading zero, non-digit, or overflow). +consteval int64_t ParseUint(std::string_view str) { + if (str.empty()) return -1; + // Leading zeros are not allowed (except "0" itself). + if (str.size() > 1 && str[0] == '0') return -1; + int64_t result = 0; + for (char c : str) { + if (c < '0' || c > '9') return -1; + result = result * 10 + (c - '0'); + if (result > UINT32_MAX) return -1; + } + return result; +} + +consteval bool IsOrtVersionValid(std::string_view version) { + // Validates ORT_VERSION at compile time. + // It must be in the format "1.Y.Z" where: + // - Major version is 1 + // - Y and Z are non-negative integers without leading zeros + // - Y (minor version) must equal ORT_API_VERSION size_t first_dot = version.find('.'); - if (first_dot == std::string_view::npos || first_dot == 0 || first_dot == version.size() - 1) { - return false; // Must have two dots and cannot start or end with a dot - } + if (first_dot == std::string_view::npos) return false; size_t second_dot = version.find('.', first_dot + 1); - if (second_dot == std::string_view::npos || second_dot == first_dot + 1 || second_dot == version.size() - 1) { - return false; // Must have two dots and cannot be adjacent or end with a dot - } + if (second_dot == std::string_view::npos) return false; + if (version.find('.', second_dot + 1) != std::string_view::npos) return false; // Exactly two dots std::string_view major = version.substr(0, first_dot); std::string_view minor = version.substr(first_dot + 1, second_dot - first_dot - 1); std::string_view patch = version.substr(second_dot + 1); if (major != "1") { return false; // Major version must be 1 } - auto is_non_negative_integer = [](std::string_view str) { - return !str.empty() && std::all_of(str.begin(), str.end(), [](char c) { return c >= '0' && c <= '9'; }); - }; - if (!is_non_negative_integer(minor) || !is_non_negative_integer(patch)) { - return false; // Minor and patch versions must be non-negative integers + int64_t minor_val = ParseUint(minor); + int64_t patch_val = ParseUint(patch); + if (minor_val < 0 || patch_val < 0) { + return false; // Minor and patch must be valid non-negative integers without leading zeros + } + if (static_cast(minor_val) != ORT_API_VERSION) { + return false; // Minor version must match ORT_API_VERSION } return true; } } // namespace ORT_API(const char*, OrtApis::GetVersionString) { - static_assert(IsOrtVersionValid(), "ORT_VERSION must be in the format '1.Y.Z' where Y and Z are non-negative integers"); + static_assert(IsOrtVersionValid(ORT_VERSION), + "ORT_VERSION must be in the format '1.Y.Z' where Y and Z are non-negative integers without leading " + "zeros, and Y must equal ORT_API_VERSION"); return ORT_VERSION; } diff --git a/onnxruntime/test/shared_lib/test_version.cc b/onnxruntime/test/shared_lib/test_version.cc deleted file mode 100644 index 5427378e7788a..0000000000000 --- a/onnxruntime/test/shared_lib/test_version.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "core/session/onnxruntime_cxx_api.h" - -#include -#include -#include -#include -#include - -#include "absl/strings/str_split.h" -#include "gtest/gtest.h" - -TEST(CApiTest, VersionConsistencyWithApiVersion) { - const auto version_string = Ort::GetVersionString(); - const std::vector version_string_components = absl::StrSplit(version_string, '.'); - ASSERT_EQ(version_string_components.size(), size_t{3}); - - auto to_uint32_t = [](const std::string& s) -> std::optional { - uint32_t result{}; - if (std::from_chars(s.data(), s.data() + s.size(), result).ec == std::errc{}) { - return result; - } - return std::nullopt; - }; - - ASSERT_NE(to_uint32_t(version_string_components[0]), std::nullopt); - ASSERT_EQ(to_uint32_t(version_string_components[1]), uint32_t{ORT_API_VERSION}); - ASSERT_NE(to_uint32_t(version_string_components[0]), std::nullopt); -} From 4b97ea18c4dd3a7db00255ec3171c1896e4b518e Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Wed, 1 Apr 2026 15:21:39 -0700 Subject: [PATCH 19/29] save current ort api version --- include/onnxruntime/ep/adapter/op_kernel.h | 4 ++++ include/onnxruntime/ep/api.h | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/onnxruntime/ep/adapter/op_kernel.h b/include/onnxruntime/ep/adapter/op_kernel.h index 273461b36e75f..12cd25f0d3e8c 100644 --- a/include/onnxruntime/ep/adapter/op_kernel.h +++ b/include/onnxruntime/ep/adapter/op_kernel.h @@ -155,7 +155,11 @@ struct OpKernelContext { } bool GetUseDeterministicCompute() const { // TODO(fs-eire): Implement GetUseDeterministicCompute(). + // if (CurrentOrtApiVersion() >= 25) { + // return /* TBD: wait for GetUseDeterministicCompute to be added in ORT API v25 */; + // else { return false; + // } } void* GetGPUComputeStream() const { return context_.GetGPUComputeStream(); diff --git a/include/onnxruntime/ep/api.h b/include/onnxruntime/ep/api.h index 256a386e695d6..fd2924ab59adb 100644 --- a/include/onnxruntime/ep/api.h +++ b/include/onnxruntime/ep/api.h @@ -53,6 +53,8 @@ inline bool TryGetAPIVersionFromVersionString(const char* version_str, uint32_t& return true; } +inline uint32_t g_current_ort_api_version{}; + } // namespace detail /// @@ -97,6 +99,9 @@ inline void ApiInit(const OrtApiBase* ort_api_base) { throw std::runtime_error("Failed to initialize EP API: the current ORT version is \"" + std::string(version_str) + "\" but it does not support the parsed API version " + std::to_string(current_ort_version) + "."); } + + detail::g_current_ort_api_version = current_ort_version; + const OrtEpApi* ep_api = ort_api->GetEpApi(); const OrtModelEditorApi* model_editor_api = ort_api->GetModelEditorApi(); @@ -108,5 +113,14 @@ inline void ApiInit(const OrtApiBase* ort_api_base) { }); } +/// +/// Get the current ORT API version that the EP API has been initialized with. +/// +/// This function should be called after ApiInit() to get the actual API version. +/// +inline uint32_t CurrentOrtApiVersion() { + return detail::g_current_ort_api_version; +} + } // namespace ep } // namespace onnxruntime From 85eb0700c429ab1a87524bf628ec66ee9724db0a Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Wed, 1 Apr 2026 15:35:57 -0700 Subject: [PATCH 20/29] resolve comments --- onnxruntime/core/session/onnxruntime_c_api.cc | 48 +-------------- onnxruntime/core/session/ort_version_check.h | 57 +++++++++++++++++ onnxruntime/test/shared_lib/test_version.cc | 61 +++++++++++++++++++ 3 files changed, 120 insertions(+), 46 deletions(-) create mode 100644 onnxruntime/core/session/ort_version_check.h create mode 100644 onnxruntime/test/shared_lib/test_version.cc diff --git a/onnxruntime/core/session/onnxruntime_c_api.cc b/onnxruntime/core/session/onnxruntime_c_api.cc index d8d42bf38cebb..1ff20fc9dac73 100644 --- a/onnxruntime/core/session/onnxruntime_c_api.cc +++ b/onnxruntime/core/session/onnxruntime_c_api.cc @@ -55,6 +55,7 @@ #include "core/session/onnxruntime_session_options_config_keys.h" #include "core/session/ort_apis.h" #include "core/session/ort_env.h" +#include "core/session/ort_version_check.h" #include "core/session/utils.h" #if defined(USE_CUDA) || defined(USE_CUDA_PROVIDER_INTERFACE) @@ -4829,53 +4830,8 @@ ORT_API(const OrtApi*, OrtApis::GetApi, uint32_t version) { return nullptr; // Unsupported version } -namespace { -// Parse a non-negative integer from a string_view without leading zeros. -// Returns -1 on failure (empty, leading zero, non-digit, or overflow). -consteval int64_t ParseUint(std::string_view str) { - if (str.empty()) return -1; - // Leading zeros are not allowed (except "0" itself). - if (str.size() > 1 && str[0] == '0') return -1; - int64_t result = 0; - for (char c : str) { - if (c < '0' || c > '9') return -1; - result = result * 10 + (c - '0'); - if (result > UINT32_MAX) return -1; - } - return result; -} - -consteval bool IsOrtVersionValid(std::string_view version) { - // Validates ORT_VERSION at compile time. - // It must be in the format "1.Y.Z" where: - // - Major version is 1 - // - Y and Z are non-negative integers without leading zeros - // - Y (minor version) must equal ORT_API_VERSION - size_t first_dot = version.find('.'); - if (first_dot == std::string_view::npos) return false; - size_t second_dot = version.find('.', first_dot + 1); - if (second_dot == std::string_view::npos) return false; - if (version.find('.', second_dot + 1) != std::string_view::npos) return false; // Exactly two dots - std::string_view major = version.substr(0, first_dot); - std::string_view minor = version.substr(first_dot + 1, second_dot - first_dot - 1); - std::string_view patch = version.substr(second_dot + 1); - if (major != "1") { - return false; // Major version must be 1 - } - int64_t minor_val = ParseUint(minor); - int64_t patch_val = ParseUint(patch); - if (minor_val < 0 || patch_val < 0) { - return false; // Minor and patch must be valid non-negative integers without leading zeros - } - if (static_cast(minor_val) != ORT_API_VERSION) { - return false; // Minor version must match ORT_API_VERSION - } - return true; -} -} // namespace - ORT_API(const char*, OrtApis::GetVersionString) { - static_assert(IsOrtVersionValid(ORT_VERSION), + static_assert(onnxruntime::version_check::IsOrtVersionValid(ORT_VERSION), "ORT_VERSION must be in the format '1.Y.Z' where Y and Z are non-negative integers without leading " "zeros, and Y must equal ORT_API_VERSION"); return ORT_VERSION; diff --git a/onnxruntime/core/session/ort_version_check.h b/onnxruntime/core/session/ort_version_check.h new file mode 100644 index 0000000000000..0d80061560700 --- /dev/null +++ b/onnxruntime/core/session/ort_version_check.h @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include +#include +#include + +#include "core/session/onnxruntime_c_api.h" + +namespace onnxruntime::version_check { + +// Parse a non-negative integer from a string_view without leading zeros. +// Returns -1 on failure (empty, leading zero, non-digit, or overflow). +consteval int64_t ParseUint(std::string_view str) { + if (str.empty()) return -1; + // Leading zeros are not allowed (except "0" itself). + if (str.size() > 1 && str[0] == '0') return -1; + int64_t result = 0; + for (char c : str) { + if (c < '0' || c > '9') return -1; + result = result * 10 + (c - '0'); + if (result > UINT32_MAX) return -1; + } + return result; +} + +// Validates a version string at compile time. +// It must be in the format "1.Y.Z" where: +// - Major version is 1 +// - Y and Z are non-negative integers without leading zeros +// - Y (minor version) must equal expected_api_version (defaults to ORT_API_VERSION) +consteval bool IsOrtVersionValid(std::string_view version, uint32_t expected_api_version = ORT_API_VERSION) { + size_t first_dot = version.find('.'); + if (first_dot == std::string_view::npos) return false; + size_t second_dot = version.find('.', first_dot + 1); + if (second_dot == std::string_view::npos) return false; + if (version.find('.', second_dot + 1) != std::string_view::npos) return false; // Exactly two dots + std::string_view major = version.substr(0, first_dot); + std::string_view minor = version.substr(first_dot + 1, second_dot - first_dot - 1); + std::string_view patch = version.substr(second_dot + 1); + if (major != "1") { + return false; + } + int64_t minor_val = ParseUint(minor); + int64_t patch_val = ParseUint(patch); + if (minor_val < 0 || patch_val < 0) { + return false; + } + if (static_cast(minor_val) != expected_api_version) { + return false; + } + return true; +} + +} // namespace onnxruntime::version_check diff --git a/onnxruntime/test/shared_lib/test_version.cc b/onnxruntime/test/shared_lib/test_version.cc new file mode 100644 index 0000000000000..31509463e7c6a --- /dev/null +++ b/onnxruntime/test/shared_lib/test_version.cc @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "core/session/onnxruntime_cxx_api.h" +#include "core/session/ort_version_check.h" + +#include "gtest/gtest.h" + +using onnxruntime::version_check::IsOrtVersionValid; +using onnxruntime::version_check::ParseUint; + +// Compile-time tests for ParseUint +static_assert(ParseUint("0") == 0); +static_assert(ParseUint("1") == 1); +static_assert(ParseUint("25") == 25); +static_assert(ParseUint("123") == 123); +static_assert(ParseUint("4294967295") == 4294967295); // UINT32_MAX +static_assert(ParseUint("4294967296") == -1); // UINT32_MAX + 1 overflows +static_assert(ParseUint("") == -1); // empty +static_assert(ParseUint("01") == -1); // leading zero +static_assert(ParseUint("00") == -1); // leading zero +static_assert(ParseUint("abc") == -1); // non-digit +static_assert(ParseUint("1a") == -1); // trailing non-digit +static_assert(ParseUint("-1") == -1); // negative sign +static_assert(ParseUint("1.0") == -1); // contains dot + +// Compile-time tests for IsOrtVersionValid (default expected_api_version = ORT_API_VERSION) +static_assert(IsOrtVersionValid(ORT_VERSION)); // current version must be valid + +// Invalid formats +static_assert(!IsOrtVersionValid("")); +static_assert(!IsOrtVersionValid("1")); +static_assert(!IsOrtVersionValid("1.0")); +static_assert(!IsOrtVersionValid("1.0.0.0")); // too many dots +static_assert(!IsOrtVersionValid("2.0.0")); // major != 1 +static_assert(!IsOrtVersionValid("1.02.0")); // leading zero in minor +static_assert(!IsOrtVersionValid("1.0.01")); // leading zero in patch +static_assert(!IsOrtVersionValid("1..0")); // empty minor +static_assert(!IsOrtVersionValid("1.0.")); // empty patch +static_assert(!IsOrtVersionValid(".1.0")); // empty major +static_assert(!IsOrtVersionValid("abc")); // non-numeric +static_assert(!IsOrtVersionValid("1.abc.0")); // non-numeric minor +static_assert(!IsOrtVersionValid("1.0.abc")); // non-numeric patch + +// Compile-time tests for IsOrtVersionValid with explicit expected_api_version +static_assert(IsOrtVersionValid("1.0.0", 0)); +static_assert(IsOrtVersionValid("1.1.0", 1)); +static_assert(IsOrtVersionValid("1.25.0", 25)); +static_assert(IsOrtVersionValid("1.25.3", 25)); +static_assert(IsOrtVersionValid("1.100.0", 100)); +static_assert(!IsOrtVersionValid("1.25.0", 24)); // minor doesn't match expected +static_assert(!IsOrtVersionValid("1.25.0", 26)); // minor doesn't match expected +static_assert(!IsOrtVersionValid("1.0.0", 1)); // minor 0 != expected 1 +static_assert(!IsOrtVersionValid("2.0.0", 0)); // major != 1 +static_assert(!IsOrtVersionValid("1.02.0", 2)); // leading zero in minor +static_assert(!IsOrtVersionValid("1.0.01", 0)); // leading zero in patch + +TEST(CApiTest, VersionIsValid) { + // Runtime sanity check — the version string returned by the API is the expected one. + EXPECT_STREQ(Ort::GetVersionString(), ORT_VERSION); +} From 04278d9fe0073d3170b58086ddab1a7a2076af63 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Wed, 1 Apr 2026 15:57:16 -0700 Subject: [PATCH 21/29] fix build --- onnxruntime/test/shared_lib/test_version.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/onnxruntime/test/shared_lib/test_version.cc b/onnxruntime/test/shared_lib/test_version.cc index 31509463e7c6a..4dacb0995ff0f 100644 --- a/onnxruntime/test/shared_lib/test_version.cc +++ b/onnxruntime/test/shared_lib/test_version.cc @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#include "onnxruntime_config.h" #include "core/session/onnxruntime_cxx_api.h" #include "core/session/ort_version_check.h" From a81e76d91dc4a25983f14b857765bb048ccdce6c Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Wed, 1 Apr 2026 16:02:46 -0700 Subject: [PATCH 22/29] resolve comments --- onnxruntime/core/session/ort_version_check.h | 28 ++++++++++---------- onnxruntime/test/shared_lib/test_version.cc | 28 +++++++++++--------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/onnxruntime/core/session/ort_version_check.h b/onnxruntime/core/session/ort_version_check.h index 0d80061560700..6b18383d8c9a9 100644 --- a/onnxruntime/core/session/ort_version_check.h +++ b/onnxruntime/core/session/ort_version_check.h @@ -3,8 +3,8 @@ #pragma once -#include #include +#include #include #include "core/session/onnxruntime_c_api.h" @@ -12,18 +12,18 @@ namespace onnxruntime::version_check { // Parse a non-negative integer from a string_view without leading zeros. -// Returns -1 on failure (empty, leading zero, non-digit, or overflow). -consteval int64_t ParseUint(std::string_view str) { - if (str.empty()) return -1; +// Returns std::nullopt on failure (empty, leading zero, non-digit, or overflow). +consteval std::optional ParseUint(std::string_view str) { + if (str.empty()) return std::nullopt; // Leading zeros are not allowed (except "0" itself). - if (str.size() > 1 && str[0] == '0') return -1; - int64_t result = 0; + if (str.size() > 1 && str[0] == '0') return std::nullopt; + uint64_t result = 0; for (char c : str) { - if (c < '0' || c > '9') return -1; - result = result * 10 + (c - '0'); - if (result > UINT32_MAX) return -1; + if (c < '0' || c > '9') return std::nullopt; + result = result * 10 + static_cast(c - '0'); + if (result > UINT32_MAX) return std::nullopt; } - return result; + return static_cast(result); } // Validates a version string at compile time. @@ -43,12 +43,12 @@ consteval bool IsOrtVersionValid(std::string_view version, uint32_t expected_api if (major != "1") { return false; } - int64_t minor_val = ParseUint(minor); - int64_t patch_val = ParseUint(patch); - if (minor_val < 0 || patch_val < 0) { + auto minor_val = ParseUint(minor); + auto patch_val = ParseUint(patch); + if (!minor_val || !patch_val) { return false; } - if (static_cast(minor_val) != expected_api_version) { + if (*minor_val != expected_api_version) { return false; } return true; diff --git a/onnxruntime/test/shared_lib/test_version.cc b/onnxruntime/test/shared_lib/test_version.cc index 4dacb0995ff0f..63b31372d8e36 100644 --- a/onnxruntime/test/shared_lib/test_version.cc +++ b/onnxruntime/test/shared_lib/test_version.cc @@ -11,19 +11,21 @@ using onnxruntime::version_check::IsOrtVersionValid; using onnxruntime::version_check::ParseUint; // Compile-time tests for ParseUint -static_assert(ParseUint("0") == 0); -static_assert(ParseUint("1") == 1); -static_assert(ParseUint("25") == 25); -static_assert(ParseUint("123") == 123); -static_assert(ParseUint("4294967295") == 4294967295); // UINT32_MAX -static_assert(ParseUint("4294967296") == -1); // UINT32_MAX + 1 overflows -static_assert(ParseUint("") == -1); // empty -static_assert(ParseUint("01") == -1); // leading zero -static_assert(ParseUint("00") == -1); // leading zero -static_assert(ParseUint("abc") == -1); // non-digit -static_assert(ParseUint("1a") == -1); // trailing non-digit -static_assert(ParseUint("-1") == -1); // negative sign -static_assert(ParseUint("1.0") == -1); // contains dot +static_assert(ParseUint("0") == 0u); +static_assert(ParseUint("1") == 1u); +static_assert(ParseUint("25") == 25u); +static_assert(ParseUint("123") == 123u); +static_assert(ParseUint("4294967295") == 4294967295u); // UINT32_MAX +static_assert(ParseUint("4294967296") == std::nullopt); // UINT32_MAX + 1 overflows +static_assert(ParseUint("") == std::nullopt); // empty +static_assert(ParseUint("01") == std::nullopt); // leading zero +static_assert(ParseUint("00") == std::nullopt); // leading zero +static_assert(ParseUint("abc") == std::nullopt); // non-digit +static_assert(ParseUint("1a") == std::nullopt); // trailing non-digit +static_assert(ParseUint("-1") == std::nullopt); // negative sign +static_assert(ParseUint("1.0") == std::nullopt); // contains dot +static_assert(ParseUint("0").has_value()); +static_assert(!ParseUint("").has_value()); // Compile-time tests for IsOrtVersionValid (default expected_api_version = ORT_API_VERSION) static_assert(IsOrtVersionValid(ORT_VERSION)); // current version must be valid From ea3aa72e7f7a539df3baabd8981aa9970a28e4ca Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Wed, 1 Apr 2026 17:47:53 -0700 Subject: [PATCH 23/29] resolve comments --- include/onnxruntime/ep/api.h | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/include/onnxruntime/ep/api.h b/include/onnxruntime/ep/api.h index fd2924ab59adb..d8e8274624839 100644 --- a/include/onnxruntime/ep/api.h +++ b/include/onnxruntime/ep/api.h @@ -3,6 +3,8 @@ #pragma once +#include +#include #include #include #include @@ -32,21 +34,14 @@ inline bool TryGetAPIVersionFromVersionString(const char* version_str, uint32_t& if (!version_str || version_str[0] != '1' || version_str[1] != '.') { return false; } - const char* p = version_str + 2; - if (*p < '0' || *p > '9') { + const char* begin = version_str + 2; + const char* end = std::strchr(begin, '.'); + if (!end) { return false; } uint32_t version = 0; - constexpr uint32_t kMaxBeforeMultiply = UINT32_MAX / 10; - while (*p >= '0' && *p <= '9') { - uint32_t digit = static_cast(*p - '0'); - if (version > kMaxBeforeMultiply || (version == kMaxBeforeMultiply && digit > UINT32_MAX % 10)) { - return false; - } - version = version * 10 + digit; - ++p; - } - if (*p != '.' && *p != '\0') { + auto [ptr, ec] = std::from_chars(begin, end, version); + if (ec != std::errc{} || ptr != end) { return false; } api_version = version; From 92bfc300f7f7e55d3bfb136014ff4755c702e109 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Fri, 3 Apr 2026 17:11:24 -0700 Subject: [PATCH 24/29] fix build --- onnxruntime/test/shared_lib/test_version.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/test/shared_lib/test_version.cc b/onnxruntime/test/shared_lib/test_version.cc index 63b31372d8e36..c78aa741588fe 100644 --- a/onnxruntime/test/shared_lib/test_version.cc +++ b/onnxruntime/test/shared_lib/test_version.cc @@ -60,5 +60,5 @@ static_assert(!IsOrtVersionValid("1.0.01", 0)); // leading zero in patch TEST(CApiTest, VersionIsValid) { // Runtime sanity check — the version string returned by the API is the expected one. - EXPECT_STREQ(Ort::GetVersionString(), ORT_VERSION); + EXPECT_STREQ(Ort::GetVersionString().c_str(), ORT_VERSION); } From 1214dd21c10f27578f04b3baba5b64cbe857c792 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Fri, 3 Apr 2026 17:54:30 -0700 Subject: [PATCH 25/29] suppress MSVC ICE --- onnxruntime/core/session/ort_version_check.h | 31 +++++++++++++------- onnxruntime/test/shared_lib/test_version.cc | 22 +++++++------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/onnxruntime/core/session/ort_version_check.h b/onnxruntime/core/session/ort_version_check.h index 6b18383d8c9a9..82fd757e3ce9f 100644 --- a/onnxruntime/core/session/ort_version_check.h +++ b/onnxruntime/core/session/ort_version_check.h @@ -4,26 +4,37 @@ #pragma once #include -#include #include #include "core/session/onnxruntime_c_api.h" namespace onnxruntime::version_check { +// A simple consteval-friendly result type for ParseUint. +// std::optional triggers an internal compiler error in MSVC 14.44 when used with consteval. +struct ParseUintResult { + uint32_t value; + bool has_value; + + consteval bool operator==(uint32_t other) const { return has_value && value == other; } + consteval bool operator!=(uint32_t other) const { return !(*this == other); } +}; + +inline consteval ParseUintResult ParseUintNone() { return {0, false}; } + // Parse a non-negative integer from a string_view without leading zeros. -// Returns std::nullopt on failure (empty, leading zero, non-digit, or overflow). -consteval std::optional ParseUint(std::string_view str) { - if (str.empty()) return std::nullopt; +// Returns a result with has_value == false on failure (empty, leading zero, non-digit, or overflow). +consteval ParseUintResult ParseUint(std::string_view str) { + if (str.empty()) return ParseUintNone(); // Leading zeros are not allowed (except "0" itself). - if (str.size() > 1 && str[0] == '0') return std::nullopt; + if (str.size() > 1 && str[0] == '0') return ParseUintNone(); uint64_t result = 0; for (char c : str) { - if (c < '0' || c > '9') return std::nullopt; + if (c < '0' || c > '9') return ParseUintNone(); result = result * 10 + static_cast(c - '0'); - if (result > UINT32_MAX) return std::nullopt; + if (result > UINT32_MAX) return ParseUintNone(); } - return static_cast(result); + return {static_cast(result), true}; } // Validates a version string at compile time. @@ -45,10 +56,10 @@ consteval bool IsOrtVersionValid(std::string_view version, uint32_t expected_api } auto minor_val = ParseUint(minor); auto patch_val = ParseUint(patch); - if (!minor_val || !patch_val) { + if (!minor_val.has_value || !patch_val.has_value) { return false; } - if (*minor_val != expected_api_version) { + if (minor_val.value != expected_api_version) { return false; } return true; diff --git a/onnxruntime/test/shared_lib/test_version.cc b/onnxruntime/test/shared_lib/test_version.cc index c78aa741588fe..8011132dd2b0c 100644 --- a/onnxruntime/test/shared_lib/test_version.cc +++ b/onnxruntime/test/shared_lib/test_version.cc @@ -15,17 +15,17 @@ static_assert(ParseUint("0") == 0u); static_assert(ParseUint("1") == 1u); static_assert(ParseUint("25") == 25u); static_assert(ParseUint("123") == 123u); -static_assert(ParseUint("4294967295") == 4294967295u); // UINT32_MAX -static_assert(ParseUint("4294967296") == std::nullopt); // UINT32_MAX + 1 overflows -static_assert(ParseUint("") == std::nullopt); // empty -static_assert(ParseUint("01") == std::nullopt); // leading zero -static_assert(ParseUint("00") == std::nullopt); // leading zero -static_assert(ParseUint("abc") == std::nullopt); // non-digit -static_assert(ParseUint("1a") == std::nullopt); // trailing non-digit -static_assert(ParseUint("-1") == std::nullopt); // negative sign -static_assert(ParseUint("1.0") == std::nullopt); // contains dot -static_assert(ParseUint("0").has_value()); -static_assert(!ParseUint("").has_value()); +static_assert(ParseUint("4294967295") == 4294967295u); // UINT32_MAX +static_assert(!(ParseUint("4294967296").has_value)); // UINT32_MAX + 1 overflows +static_assert(!(ParseUint("").has_value)); // empty +static_assert(!(ParseUint("01").has_value)); // leading zero +static_assert(!(ParseUint("00").has_value)); // leading zero +static_assert(!(ParseUint("abc").has_value)); // non-digit +static_assert(!(ParseUint("1a").has_value)); // trailing non-digit +static_assert(!(ParseUint("-1").has_value)); // negative sign +static_assert(!(ParseUint("1.0").has_value)); // contains dot +static_assert(ParseUint("0").has_value); +static_assert(!ParseUint("").has_value); // Compile-time tests for IsOrtVersionValid (default expected_api_version = ORT_API_VERSION) static_assert(IsOrtVersionValid(ORT_VERSION)); // current version must be valid From 70174de0133ad93f7778dbc5a281fd87b93d15f8 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:24:04 -0700 Subject: [PATCH 26/29] Address PR review feedback: null checks, binary verification, and nits --- include/onnxruntime/ep/adapter/op_kernel.h | 2 +- include/onnxruntime/ep/api.h | 6 ++++++ .../azure-pipelines/stages/plugin-mac-webgpu-stage.yml | 10 ++++++++++ .../stages/plugin-webgpu-packaging-stage.yml | 6 ++++++ .../azure-pipelines/stages/plugin-win-webgpu-stage.yml | 9 +++++++++ 5 files changed, 32 insertions(+), 1 deletion(-) diff --git a/include/onnxruntime/ep/adapter/op_kernel.h b/include/onnxruntime/ep/adapter/op_kernel.h index 12cd25f0d3e8c..06ffdca16af6d 100644 --- a/include/onnxruntime/ep/adapter/op_kernel.h +++ b/include/onnxruntime/ep/adapter/op_kernel.h @@ -157,7 +157,7 @@ struct OpKernelContext { // TODO(fs-eire): Implement GetUseDeterministicCompute(). // if (CurrentOrtApiVersion() >= 25) { // return /* TBD: wait for GetUseDeterministicCompute to be added in ORT API v25 */; - // else { + // } else { return false; // } } diff --git a/include/onnxruntime/ep/api.h b/include/onnxruntime/ep/api.h index d8e8274624839..60f6b8613bb49 100644 --- a/include/onnxruntime/ep/api.h +++ b/include/onnxruntime/ep/api.h @@ -99,6 +99,9 @@ inline void ApiInit(const OrtApiBase* ort_api_base) { const OrtEpApi* ep_api = ort_api->GetEpApi(); const OrtModelEditorApi* model_editor_api = ort_api->GetModelEditorApi(); + if (!ep_api || !model_editor_api) { + throw std::runtime_error("Failed to initialize EP API: GetEpApi or GetModelEditorApi returned null."); + } // Manual init for the C++ API Ort::InitApi(ort_api); @@ -114,6 +117,9 @@ inline void ApiInit(const OrtApiBase* ort_api_base) { /// This function should be called after ApiInit() to get the actual API version. /// inline uint32_t CurrentOrtApiVersion() { + if (!detail::g_api_ptrs.has_value()) { + throw std::logic_error("onnxruntime::ep::CurrentOrtApiVersion() called before ApiInit()."); + } return detail::g_current_ort_api_version; } diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml index 6852954346e4b..16e16e54fd236 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-mac-webgpu-stage.yml @@ -70,6 +70,16 @@ stages: --update --skip_submodule_sync --build --parallel displayName: 'Build WebGPU Plugin' + - script: | + set -e + plugin_path="$(Build.SourcesDirectory)/build/${{ parameters.cmake_build_type }}/libonnxruntime_providers_webgpu.dylib" + if [ ! -f "$plugin_path" ]; then + echo "Error: Expected plugin binary not found at '$plugin_path'. Failing build to avoid publishing an invalid package." + exit 1 + fi + echo "Verified plugin binary exists at: $plugin_path" + displayName: 'Verify plugin binary exists' + - task: CopyFiles@2 displayName: 'Copy plugin binaries to staging directory' inputs: diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml index d3a9cf3fbb133..1864bb4016bb4 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-webgpu-packaging-stage.yml @@ -23,6 +23,10 @@ parameters: displayName: 'Package Version' type: string default: dev + values: + - dev + - release + - RC - name: cmake_build_type type: string @@ -44,6 +48,8 @@ stages: cmake_build_type: ${{ parameters.cmake_build_type }} # Windows ARM64 + # ARM64 build requires the x64 tblgen.exe (used during the build), which is not correctly + # generated in a cross build. So we require x64 to be built first and download tblgen.exe from it. - ${{ if and(eq(parameters.build_windows_arm64, true), eq(parameters.build_windows_x64, true)) }}: - template: plugin-win-webgpu-stage.yml parameters: diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml index c90015309d75f..6f7c7e4d893e0 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml @@ -206,6 +206,15 @@ stages: DoEsrp: true Pattern: '*.dll' + - powershell: | + $pluginPath = "$(Build.BinariesDirectory)\${{ parameters.cmake_build_type }}\${{ parameters.cmake_build_type }}\onnxruntime_providers_webgpu.dll" + if (-not (Test-Path $pluginPath)) { + Write-Error "Expected plugin binary not found at '$pluginPath'. Failing build to avoid publishing an invalid package." + exit 1 + } + Write-Host "Verified plugin binary exists at: $pluginPath" + displayName: 'Verify plugin binary exists' + - task: CopyFiles@2 displayName: 'Copy plugin binaries to staging directory' inputs: From 1c90606f605f2c97f226053ae8186586d74a8552 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:25:19 -0700 Subject: [PATCH 27/29] Add DXC hash verification and fail on RC versioning --- .../azure-pipelines/stages/plugin-win-webgpu-stage.yml | 8 ++++++++ .../templates/set-plugin-build-variables-step.yml | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml index 6f7c7e4d893e0..ab1ae28a8b5a3 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml @@ -169,11 +169,19 @@ stages: $dxcZipPath = "$(Build.BinariesDirectory)\dxc.zip" $dxcExtractPath = "$(Build.BinariesDirectory)\dxc_extracted" $targetArch = "${{ parameters.arch }}" + $expectedHash = "70B1913A1BFCE4A3E1A5311D16246F4ECDF3A3E613ABEC8AA529E57668426F85" # Download the DXC package Write-Host "Downloading DXC release from $dxcZipUrl" Invoke-WebRequest -Uri $dxcZipUrl -OutFile $dxcZipPath + # Verify integrity of downloaded file + $actualHash = (Get-FileHash -Path $dxcZipPath -Algorithm SHA256).Hash + if ($actualHash -ne $expectedHash) { + throw "DXC zip hash mismatch! Expected: $expectedHash, Got: $actualHash. The download may be corrupted or tampered with." + } + Write-Host "DXC zip hash verified: $actualHash" + # Create extraction directory if (-not (Test-Path $dxcExtractPath)) { New-Item -Path $dxcExtractPath -ItemType Directory -Force diff --git a/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml b/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml index b761d5dbf4583..212eca44ae3ec 100644 --- a/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml +++ b/tools/ci_build/github/azure-pipelines/templates/set-plugin-build-variables-step.yml @@ -40,10 +40,10 @@ steps: universal_version = original_ver elif package_version == "RC": - # TODO: implement RC versioning - version_string = "{}-rc".format(original_ver) - universal_version = version_string - print("##vso[task.logissue type=warning]RC versioning is not yet fully implemented.") + # RC versioning is not yet implemented. Fail the build to prevent publishing + # an ambiguous version without an RC number. + print("##vso[task.logissue type=error]RC versioning is not yet implemented. Use 'dev' or 'release' instead.") + sys.exit(1) elif package_version == "dev": try: From 1b868f339f0b539d5b9caaae411d2fe82048f0cc Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Mon, 6 Apr 2026 01:51:16 -0700 Subject: [PATCH 28/29] Update tools/ci_build/github/linux/build_webgpu_plugin_package.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tools/ci_build/github/linux/build_webgpu_plugin_package.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/ci_build/github/linux/build_webgpu_plugin_package.sh b/tools/ci_build/github/linux/build_webgpu_plugin_package.sh index 48d29a801182f..a3583bf9d17a5 100755 --- a/tools/ci_build/github/linux/build_webgpu_plugin_package.sh +++ b/tools/ci_build/github/linux/build_webgpu_plugin_package.sh @@ -27,6 +27,7 @@ docker run --rm \ --volume "${HOME}/.onnx:/home/onnxruntimedev/.onnx" \ -e NIGHTLY_BUILD \ -e BUILD_BUILDNUMBER \ + -e SYSTEM_COLLECTIONURI \ "$DOCKER_IMAGE" \ /bin/bash -c "/usr/bin/python3 /onnxruntime_src/tools/ci_build/build.py \ --build_dir /build \ From e0a4ce8328641ee69be07bb9d21c53640f065ffa Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Mon, 6 Apr 2026 01:57:50 -0700 Subject: [PATCH 29/29] sign dxc dlls --- .../azure-pipelines/stages/plugin-win-webgpu-stage.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml index ab1ae28a8b5a3..352ae77544e93 100644 --- a/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml +++ b/tools/ci_build/github/azure-pipelines/stages/plugin-win-webgpu-stage.yml @@ -191,9 +191,9 @@ stages: Write-Host "Extracting DXC package to $dxcExtractPath" Expand-Archive -Path $dxcZipPath -DestinationPath $dxcExtractPath -Force - # Copy the necessary DLLs to the target directory + # Copy the necessary DLLs to the build output directory so they get ESRP-signed $sourcePath = Join-Path $dxcExtractPath "bin\$targetArch" - $targetPath = "$(Build.ArtifactStagingDirectory)\bin" + $targetPath = "$(Build.BinariesDirectory)\${{ parameters.cmake_build_type }}\${{ parameters.cmake_build_type }}" if (-not (Test-Path $targetPath)) { New-Item -Path $targetPath -ItemType Directory -Force @@ -230,6 +230,8 @@ stages: Contents: | onnxruntime_providers_webgpu.dll onnxruntime_providers_webgpu.pdb + dxil.dll + dxcompiler.dll TargetFolder: '$(Build.ArtifactStagingDirectory)\bin' - script: |