From 00684209c47e9c485c89c5d95363080e63af66c4 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 20 Aug 2026 13:21:55 +0200 Subject: [PATCH 01/12] Add conda-forge only build and test workflow Add a 'Conda package (conda-forge)' workflow with a dedicated conda-recipe-cf recipe that builds and tests dpnp using compiler, oneMKL, dpctl and NumPy dependencies resolved solely from conda-forge. --- .github/workflows/conda-package-cf.yml | 478 ++++++++++++++++++++++++ conda-recipe-cf/bld.bat | 45 +++ conda-recipe-cf/build.sh | 47 +++ conda-recipe-cf/conda_build_config.yaml | 18 + conda-recipe-cf/meta.yaml | 93 +++++ conda-recipe-cf/run_test.bat | 16 + conda-recipe-cf/run_test.sh | 13 + 7 files changed, 710 insertions(+) create mode 100644 .github/workflows/conda-package-cf.yml create mode 100644 conda-recipe-cf/bld.bat create mode 100755 conda-recipe-cf/build.sh create mode 100644 conda-recipe-cf/conda_build_config.yaml create mode 100644 conda-recipe-cf/meta.yaml create mode 100644 conda-recipe-cf/run_test.bat create mode 100755 conda-recipe-cf/run_test.sh diff --git a/.github/workflows/conda-package-cf.yml b/.github/workflows/conda-package-cf.yml new file mode 100644 index 00000000000..6f078b6184d --- /dev/null +++ b/.github/workflows/conda-package-cf.yml @@ -0,0 +1,478 @@ +name: Conda package (conda-forge) + +on: + push: + branches: + - master + pull_request: + +permissions: read-all + +env: + package-name: dpnp + recipe-dir: conda-recipe-cf + channels-list: '-c conda-forge --override-channels' + ver-script-part1: "import json; f = open('version.json', 'r'); j = json.load(f); f.close(); " + ver-script-part2: "d = j['dpnp'][0]; print('='.join((d[s] for s in ('version', 'build'))))" + fetch-depth: 1 + python-ver-test-all-dtypes: '3.14' + test-env-name: 'test' + rerun-tests-on-failure: 'true' + rerun-tests-max-attempts: 2 + rerun-tests-timeout: 45 + +jobs: + build: + name: Build + + strategy: + fail-fast: false + matrix: + python: ['3.10', '3.11', '3.12', '3.13'] + os: [ubuntu-22.04, windows-2022] + include: + - python: '3.14' + os: ubuntu-22.04 + python_spec: '3.14.* *_cp314' + - python: '3.14' + os: windows-2022 + python_spec: '3.14.* *_cp314' + - python: '3.14' + os: ubuntu-22.04 + python_spec: '3.14.* *_cp314t' + - python: '3.14' + os: windows-2022 + python_spec: '3.14.* *_cp314t' + + permissions: + # Needed to cancel any previous runs that are not completed for a given workflow + actions: write + + runs-on: ${{ matrix.os }} + timeout-minutes: 90 + + defaults: + run: + shell: ${{ matrix.os == 'windows-2022' && 'cmd /C CALL {0}' || 'bash -el {0}' }} + + env: + build-conda-pkg-env: 'environments/build_conda_pkg.yml' + build-env-name: 'build' + python-label: ${{ endsWith(matrix.python_spec, 't') && format('{0}t', matrix.python) || matrix.python }} + python-conda-spec: ${{ matrix.python_spec || matrix.python }} + + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@d07a454dad7609a92316b57b23c9ccfd4f59af66 # 0.13.1 + with: + access_token: ${{ github.token }} + + - name: Checkout DPNP repo + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + + - name: Setup miniconda + id: setup_miniconda + continue-on-error: true + uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 + with: + miniforge-version: latest + use-mamba: 'true' + conda-remove-defaults: 'true' + environment-file: ${{ env.build-conda-pkg-env }} + activate-environment: ${{ env.build-env-name }} + + - name: ReSetup miniconda + if: steps.setup_miniconda.outcome == 'failure' + uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 + with: + miniforge-version: latest + use-mamba: 'true' + conda-remove-defaults: 'true' + environment-file: ${{ env.build-conda-pkg-env }} + activate-environment: ${{ env.build-env-name }} + + - name: List installed packages + run: mamba list + + - name: Store conda paths as envs + shell: bash -el {0} + run: | + echo "CONDA_BLD=$CONDA_PREFIX/conda-bld/${{ runner.os == 'Linux' && 'linux' || 'win' }}-64/" | tr "\\\\" '/' >> "$GITHUB_ENV" + + - name: Build conda package + id: build_conda_pkg + continue-on-error: true + run: conda-build --no-test --python "${{ env.python-conda-spec }}" --numpy 2.0 ${{ env.channels-list }} ${{ env.recipe-dir }} + + - name: ReBuild conda package + if: steps.build_conda_pkg.outcome == 'failure' + run: conda-build --no-test --python "${{ env.python-conda-spec }}" --numpy 2.0 ${{ env.channels-list }} ${{ env.recipe-dir }} + + - name: Upload artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ${{ env.package-name }} ${{ runner.os }} Python ${{ env.python-label }} + path: ${{ env.CONDA_BLD }}${{ env.package-name }}-*.conda + + test_linux: + name: Test + + needs: build + + runs-on: ${{ matrix.os }} + timeout-minutes: 100 + + defaults: + run: + shell: bash -el {0} + + strategy: + fail-fast: false + matrix: + python: ['3.10', '3.11', '3.12', '3.13'] + os: [ubuntu-latest] + include: + - python: '3.14' + os: ubuntu-latest + python_spec: '3.14.* *_cp314' + - python: '3.14' + os: ubuntu-latest + python_spec: '3.14.* *_cp314t' + + env: + dpnp-repo-path: '${{ github.workspace }}/source/' + create-conda-channel-env: 'source/environments/create_conda_channel.yml' + channel-path: '${{ github.workspace }}/channel/' + pkg-path-in-channel: '${{ github.workspace }}/channel/linux-64/' + ver-json-path: '${{ github.workspace }}/version.json' + python-label: ${{ endsWith(matrix.python_spec, 't') && format('{0}t', matrix.python) || matrix.python }} + python-install-spec: "python ${{ matrix.python_spec || format('{0}.*', matrix.python) }}" + + steps: + - name: Set Swap Space + uses: pierotofy/set-swap-space@49819abfb41bd9b44fb781159c033dba90353a7c # v1.0 + with: + swap-size-gb: 8 + + - name: Checkout DPNP repo + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: ${{ env.fetch-depth }} + path: ${{ env.dpnp-repo-path }} + + - name: Download artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ env.package-name }} ${{ runner.os }} Python ${{ env.python-label }} + path: ${{ env.pkg-path-in-channel }} + + - name: Setup miniconda + id: setup_miniconda + continue-on-error: true + uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 + with: + miniforge-version: latest + use-mamba: 'true' + conda-remove-defaults: 'true' + environment-file: ${{ env.create-conda-channel-env }} + activate-environment: ${{ env.test-env-name }} + + - name: ReSetup miniconda + if: steps.setup_miniconda.outcome == 'failure' + uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 + with: + miniforge-version: latest + use-mamba: 'true' + conda-remove-defaults: 'true' + environment-file: ${{ env.create-conda-channel-env }} + activate-environment: ${{ env.test-env-name }} + + - name: Create conda channel + run: | + python -m conda_index ${{ env.channel-path }} + + - name: Test conda channel + run: | + conda search ${{ env.package-name }} -c ${{ env.channel-path }} --override-channels --info --json > ${{ env.ver-json-path }} + cat ${{ env.ver-json-path }} + + - name: Get package version + run: | + PACKAGE_VERSION=$(python -c "${{ env.ver-script-part1 }} ${{ env.ver-script-part2 }}") + export PACKAGE_VERSION + + echo "PACKAGE_VERSION=${PACKAGE_VERSION}" + echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> "$GITHUB_ENV" + + - name: Install dpnp + id: install_dpnp + continue-on-error: true + run: | + mamba install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} pytest pytest-xdist "${{ env.python-install-spec }}" ${{ env.TEST_CHANNELS }} + env: + TEST_CHANNELS: '-c ${{ env.channel-path }} ${{ env.channels-list }}' + + - name: ReInstall dpnp + if: steps.install_dpnp.outcome == 'failure' + run: | + mamba install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} pytest pytest-xdist "${{ env.python-install-spec }}" ${{ env.TEST_CHANNELS }} + env: + TEST_CHANNELS: '-c ${{ env.channel-path }} ${{ env.channels-list }}' + + - name: List installed packages + run: mamba list + + - name: Smoke test + run: | + python -c "import dpctl; dpctl.lsplatform()" + python -c "import dpnp; print(dpnp.__version__)" + + - name: Run tests + if: env.rerun-tests-on-failure != 'true' + run: | + export SKIP_TENSOR_TESTS=1 + if [[ "${{ env.python-label }}" == "${{ env.python-ver-test-all-dtypes }}" ]]; then + export DPNP_TEST_ALL_INT_TYPES=1 + python -m pytest -ra --pyargs ${{ env.package-name }}.tests + else + python -m pytest -n auto -ra --pyargs ${{ env.package-name }}.tests + fi + + - name: Run tests + if: env.rerun-tests-on-failure == 'true' + id: run_tests_linux + uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 + with: + timeout_minutes: ${{ env.rerun-tests-timeout }} + max_attempts: ${{ env.rerun-tests-max-attempts }} + retry_on: any + command: | + . $CONDA/etc/profile.d/conda.sh + . $CONDA/etc/profile.d/mamba.sh + mamba activate ${{ env.test-env-name }} + export SKIP_TENSOR_TESTS=1 + + if [[ "${{ env.python-label }}" == "${{ env.python-ver-test-all-dtypes }}" ]]; then + export DPNP_TEST_ALL_INT_TYPES=1 + python -m pytest -ra --pyargs ${{ env.package-name }}.tests + else + python -m pytest -n auto -ra --pyargs ${{ env.package-name }}.tests + fi + + - name: Run tensor tests + if: env.rerun-tests-on-failure != 'true' + run: | + python -m pytest -n auto -ra --pyargs dpnp.tests.tensor + + - name: Run tensor tests + if: env.rerun-tests-on-failure == 'true' + id: run_tests_tensor_linux + uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 + with: + timeout_minutes: ${{ env.rerun-tests-timeout }} + max_attempts: ${{ env.rerun-tests-max-attempts }} + retry_on: any + command: | + . $CONDA/etc/profile.d/conda.sh + . $CONDA/etc/profile.d/mamba.sh + mamba activate ${{ env.test-env-name }} + + python -m pytest -n auto -ra --pyargs dpnp.tests.tensor + + test_windows: + name: Test + + needs: build + + runs-on: ${{ matrix.os }} + timeout-minutes: 120 + + defaults: + run: + shell: cmd /C CALL {0} + + strategy: + fail-fast: false + matrix: + python: ['3.10', '3.11', '3.12', '3.13'] + os: [windows-2022] + include: + - python: '3.14' + os: windows-2022 + python_spec: '3.14.* *_cp314' + - python: '3.14' + os: windows-2022 + python_spec: '3.14.* *_cp314t' + + env: + dpnp-repo-path: '${{ github.workspace }}\source' + create-conda-channel-env: 'source\environments\create_conda_channel.yml' + channel-path: '${{ github.workspace }}\channel\' + pkg-path-in-channel: '${{ github.workspace }}\channel\win-64\' + ver-json-path: '${{ github.workspace }}\version.json' + python-label: ${{ endsWith(matrix.python_spec, 't') && format('{0}t', matrix.python) || matrix.python }} + python-install-spec: "python ${{ matrix.python_spec || format('{0}.*', matrix.python) }}" + + steps: + - name: Checkout DPNP repo + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: ${{ env.fetch-depth }} + path: ${{ env.dpnp-repo-path }} + + - name: Download artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ env.package-name }} ${{ runner.os }} Python ${{ env.python-label }} + path: ${{ env.pkg-path-in-channel }} + + - name: Store a path to package archive + run: | + @echo on + + set SEARCH_SCRIPT="DIR ${{ env.pkg-path-in-channel }} /s/b | FINDSTR /r "dpnp-.*\.conda"" + FOR /F "tokens=* USEBACKQ" %%F IN (`%SEARCH_SCRIPT%`) DO ( + SET FULL_PACKAGE_PATH=%%F + ) + echo FULL_PACKAGE_PATH: %FULL_PACKAGE_PATH% + (echo FULL_PACKAGE_PATH=%FULL_PACKAGE_PATH%) >> %GITHUB_ENV% + + - name: Setup miniconda + id: setup_miniconda + continue-on-error: true + uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 + with: + miniforge-version: latest + use-mamba: 'true' + conda-remove-defaults: 'true' + environment-file: ${{ env.create-conda-channel-env }} + activate-environment: ${{ env.test-env-name }} + + - name: ReSetup miniconda + if: steps.setup_miniconda.outcome == 'failure' + uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 + with: + miniforge-version: latest + use-mamba: 'true' + conda-remove-defaults: 'true' + environment-file: ${{ env.create-conda-channel-env }} + activate-environment: ${{ env.test-env-name }} + + - name: Store conda paths as envs + run: | + @echo on + (echo CONDA_LIB_PATH=%CONDA_PREFIX%\Library\lib\) >> %GITHUB_ENV% + (echo CONDA_LIB_BIN_PATH=%CONDA_PREFIX%\Library\bin\) >> %GITHUB_ENV% + + - name: Create conda channel + run: | + @echo on + python -m conda_index ${{ env.channel-path }} + + - name: Test conda channel + run: | + @echo on + conda search ${{ env.package-name }} -c ${{ env.channel-path }} --override-channels --info --json > ${{ env.ver-json-path }} + + - name: Dump version.json + run: more ${{ env.ver-json-path }} + + - name: Get package version + run: | + @echo on + set "SCRIPT=${{ env.ver-script-part1 }} ${{ env.ver-script-part2 }}" + FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO ( + set PACKAGE_VERSION=%%F + ) + echo PACKAGE_VERSION: %PACKAGE_VERSION% + (echo PACKAGE_VERSION=%PACKAGE_VERSION%) >> %GITHUB_ENV% + + - name: Install dpnp + run: | + @echo on + mamba install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} pytest pytest-xdist "${{ env.python-install-spec }}" ${{ env.TEST_CHANNELS }} + env: + TEST_CHANNELS: '-c ${{ env.channel-path }} ${{ env.channels-list }}' + MAMBA_NO_LOW_SPEED_LIMIT: 1 + + - name: List installed packages + run: mamba list + + - name: Activate OCL CPU RT + shell: pwsh + run: | + $script_path="$env:CONDA_PREFIX\Scripts\set-intel-ocl-icd-registry.ps1" + if (Test-Path $script_path) { + &$script_path + } else { + Write-Warning "File $script_path was NOT found!" + + # The conda-forge intel-opencl-rt does not ship the registration + # helper, so register the OpenCL CPU driver with the ICD loader manually + $vendors = "HKLM:\SOFTWARE\Khronos\OpenCL\Vendors" + $drivers = @(Get-ChildItem -Recurse -ErrorAction SilentlyContinue -Filter intelocl64.dll -Path "$env:CONDA_PREFIX\Library\bin") + if ($drivers.Count -eq 0) { throw "No OpenCL CPU driver (intelocl64.dll) found under $env:CONDA_PREFIX" } + if (-not (Test-Path $vendors)) { New-Item -Path $vendors -Force | Out-Null } + foreach ($driver in $drivers) { + New-ItemProperty -Path $vendors -Name $driver.FullName -Value 0 -PropertyType DWord -Force | Out-Null + } + Get-ItemProperty -Path $vendors + } + # Check the variable assisting OpenCL CPU driver to find TBB DLLs which are not located where it expects them by default + $cl_cfg="$env:CONDA_PREFIX\Library\bin\cl.cfg" + Get-Content -Tail 5 -Path $cl_cfg + + - name: Smoke test + run: | + python -c "import dpctl; dpctl.lsplatform()" + python -c "import dpnp; print(dpnp.__version__)" + + - name: Run tests + if: env.rerun-tests-on-failure != 'true' + shell: pwsh + run: | + $env:SKIP_TENSOR_TESTS=1 + if ("${{ env.python-label }}" -eq "${{ env.python-ver-test-all-dtypes }}") { + $env:DPNP_TEST_ALL_INT_TYPES=1 + python -m pytest -ra --pyargs ${{ env.package-name }}.tests + } else { + python -m pytest -n auto -ra --pyargs ${{ env.package-name }}.tests + } + + - name: Run tests + if: env.rerun-tests-on-failure == 'true' + id: run_tests_win + uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 + with: + timeout_minutes: ${{ env.rerun-tests-timeout }} + max_attempts: ${{ env.rerun-tests-max-attempts }} + retry_on: any + shell: pwsh + command: | + $env:SKIP_TENSOR_TESTS=1 + if ( "${{ env.python-label }}" -eq "${{ env.python-ver-test-all-dtypes }}" ) { + $env:DPNP_TEST_ALL_INT_TYPES=1 + python -m pytest -ra --pyargs ${{ env.package-name }}.tests + } else { + python -m pytest -n auto -ra --pyargs ${{ env.package-name }}.tests + } + + - name: Run tensor tests + if: env.rerun-tests-on-failure != 'true' + shell: pwsh + run: | + python -m pytest -n auto -ra --pyargs dpnp.tests.tensor + + - name: Run tensor tests + if: env.rerun-tests-on-failure == 'true' + id: run_tests_tensor_win + uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 + with: + timeout_minutes: ${{ env.rerun-tests-timeout }} + max_attempts: ${{ env.rerun-tests-max-attempts }} + retry_on: any + shell: pwsh + command: | + python -m pytest -n auto -ra --pyargs dpnp.tests.tensor diff --git a/conda-recipe-cf/bld.bat b/conda-recipe-cf/bld.bat new file mode 100644 index 00000000000..beb5cdb6038 --- /dev/null +++ b/conda-recipe-cf/bld.bat @@ -0,0 +1,45 @@ +"%PYTHON%" setup.py clean --all + +set "MKLROOT=%PREFIX%/Library" +set "TBB_ROOT_HINT=%PREFIX%/Library" +set "DPL_ROOT_HINT=%PREFIX%/Library" +:: useful for building in resources constrained VMs (public CI) +set "CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=FALSE" + +:: try to detect SYCL resource/include dir from icpx +for /f "usebackq delims=" %%R in (`icpx --print-resource-dir 2^>NUL`) do ( + set "SYCL_RESOURCE_DIR=%%R" +) + +if defined SYCL_RESOURCE_DIR ( + if exist "%SYCL_RESOURCE_DIR%\include" ( + set "SYCL_INCLUDE_DIR_HINT=%SYCL_RESOURCE_DIR%" + ) +) + + +set "CC=icx" +set "CXX=icx" + +set "CMAKE_GENERATOR=Ninja" +:: make CMake verbose +set "VERBOSE=1" + +:: set CMAKE to use less threads to avoid OOM +set CMAKE_BUILD_PARALLEL_LEVEL=%CPU_COUNT% + +:: flags mean: --wheel --no-isolation --skip-dependency-check +%PYTHON% -m build -w -n -x +if %errorlevel% neq 0 exit 1 + +:: wheel file was renamed +for /f %%f in ('dir /b /S .\dist') do ( + %PYTHON% -m pip install %%f ^ + --no-build-isolation ^ + --no-deps ^ + --only-binary :all: ^ + --no-index ^ + --prefix %PREFIX% ^ + -vv + if errorlevel 1 exit 1 +) diff --git a/conda-recipe-cf/build.sh b/conda-recipe-cf/build.sh new file mode 100755 index 00000000000..fc910cdb226 --- /dev/null +++ b/conda-recipe-cf/build.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# This is necessary to help DPC++ find Intel libraries such as SVML, IRNG, etc in build prefix +export LIBRARY_PATH="$LIBRARY_PATH:${BUILD_PREFIX}/lib" + +# Intel LLVM must cooperate with compiler and sysroot from conda +echo "--gcc-toolchain=${BUILD_PREFIX} --sysroot=${BUILD_PREFIX}/${HOST}/sysroot -target ${HOST}" > icpx_for_conda.cfg + +ICPXCFG="$(pwd)/icpx_for_conda.cfg" +export ICPXCFG + +ICXCFG="$(pwd)/icpx_for_conda.cfg" +export ICXCFG + +if [ -e "_skbuild" ]; then + ${PYTHON} setup.py clean --all +fi + +export CC=icx +export CXX=icpx + +# conda-forge's gcc/g++ activation injects -fno-merge-constants into CFLAGS/CXXFLAGS +# when CONDA_BUILD==1, while icx/icpx doesn't support it and emit the warning. +# See CMPLRLLVM-19167 / CMPLRLLVM-28729 for more context and PR which added the flag: +# https://github.com/conda-forge/ctng-compiler-activation-feedstock/pull/193 +export CFLAGS="${CFLAGS//-fno-merge-constants/}" +export CXXFLAGS="${CXXFLAGS//-fno-merge-constants/}" + +export CMAKE_GENERATOR=Ninja +# Make CMake verbose +export VERBOSE=1 + +# set CMAKE to use less threads to avoid OOM +export CMAKE_BUILD_PARALLEL_LEVEL=${CPU_COUNT} + +CMAKE_ARGS="${CMAKE_ARGS} -DDPNP_WITH_REDIST:BOOL=ON" + +# -wnx flags mean: --wheel --no-isolation --skip-dependency-check +${PYTHON} -m build -w -n -x + +${PYTHON} -m pip install dist/dpnp*.whl \ + --no-build-isolation \ + --no-deps \ + --only-binary :all: \ + --no-index \ + --prefix "${PREFIX}" \ + -vv diff --git a/conda-recipe-cf/conda_build_config.yaml b/conda-recipe-cf/conda_build_config.yaml new file mode 100644 index 00000000000..408d05b438a --- /dev/null +++ b/conda-recipe-cf/conda_build_config.yaml @@ -0,0 +1,18 @@ +numpy: + - '2' +c_compiler: # [linux] + - gcc # [linux] +cxx_compiler: # [linux] + - gxx # [linux] +cxx_compiler_version: # [linux] + - '15' # [linux] +c_stdlib: # [linux] + - sysroot # [linux] +c_stdlib_version: # [linux] + - '2.28' # [linux] +c_stdlib: # [win] + - vs # [win] +cxx_compiler: # [win] + - vs2022 # [win] +c_compiler: # [win] + - vs2022 # [win] diff --git a/conda-recipe-cf/meta.yaml b/conda-recipe-cf/meta.yaml new file mode 100644 index 00000000000..de92803afe1 --- /dev/null +++ b/conda-recipe-cf/meta.yaml @@ -0,0 +1,93 @@ +{% set required_compiler_and_mkl_version = "2026.0.0" %} +{% set required_dpctl_version = "0.22.1" %} + +{% set pyproject = load_file_data('pyproject.toml') %} +{% set py_build_deps = pyproject.get('build-system', {}).get('requires', []) %} + +package: + name: dpnp + version: {{ GIT_DESCRIBE_TAG }} + +source: + path: .. + +build: + number: {{ GIT_DESCRIBE_NUMBER }} + ignore_run_exports: + - dpctl + - numpy + - python + # building with Intel DPC++ and do not import the MSVC runtime DLLs + - vc14_runtime # [win] + - ucrt # [win] + missing_dso_whitelist: + - '*DPCTLSyclInterface*' + - '*/dpnp_backend_c.dll' # [win] + skip: true # [osx or py<39] + +requirements: + build: + - {{ compiler('cxx') }} + - {{ stdlib('c') }} + - {{ compiler('dpcpp') }} >={{ required_compiler_and_mkl_version }} + - ninja >=1.11.1 # [linux] + - cmake >=3.29.0 + host: + - python + - pip >=25.0 + - pybind11 >=3.0.2 + - setuptools >=79.0.1 + - wheel >=0.45.1 + - python-build >=1.1 + - scikit-build >=0.18.1 + - cython >=3.1.0 + - numpy >=1.26.0 + - versioneer ==0.29 + # versioneer dependency + - tomli # [py<311] + - mkl-devel-dpcpp >={{ required_compiler_and_mkl_version }} + - onedpl-devel + - tbb-devel + run: + - python + - {{ pin_compatible('dpctl', min_pin='x.x.x', max_pin=None) }} + - {{ pin_compatible('dpcpp-cpp-rt', min_pin='x.x', max_pin='x') }} + - {{ pin_compatible('intel-sycl-rt', min_pin='x.x', max_pin='x') }} + - {{ pin_compatible('intel-cmplr-lib-rt', min_pin='x.x', max_pin='x') }} + - {{ pin_compatible('mkl', min_pin='x.x', max_pin='x') }} + - {{ pin_compatible('onemkl-sycl-blas', min_pin='x.x', max_pin='x') }} + - {{ pin_compatible('onemkl-sycl-dft', min_pin='x.x', max_pin='x') }} + - {{ pin_compatible('onemkl-sycl-lapack', min_pin='x.x', max_pin='x') }} + - {{ pin_compatible('onemkl-sycl-rng', min_pin='x.x', max_pin='x') }} + - {{ pin_compatible('onemkl-sycl-vm', min_pin='x.x', max_pin='x') }} + - numpy + - ocl-icd-system # [linux] + - khronos-opencl-icd-loader # [win] + +test: + requires: + - pip + - pytest + - setuptools + - scipy # [py<314] + commands: + - pip check + - python -c "import dpnp; print(dpnp.__version__)" + - python -m dpctl -f + +about: + home: https://github.com/IntelPython/dpnp + license: BSD-3-Clause + license_file: LICENSE.txt + summary: 'Data Parallel Extension for NumPy' + description: | + Data Parallel Extension for NumPy (dpnp) is a Python library that + implements a subset of NumPy that can be executed on any data parallel + device. The subset is a drop-in replacement of core NumPy functions and + numerical data types. + +extra: + recipe-maintainers: + - antonwolfy + - vlad-perevezentsev + - ndgrigorian diff --git a/conda-recipe-cf/run_test.bat b/conda-recipe-cf/run_test.bat new file mode 100644 index 00000000000..ee2e40b1607 --- /dev/null +++ b/conda-recipe-cf/run_test.bat @@ -0,0 +1,16 @@ +@echo on + +:: Skip tensor tests by default to avoid OOM in conda builds. +:: Set SKIP_TENSOR_TESTS=0 to run them on machines with enough memory. +if not defined SKIP_TENSOR_TESTS ( + set "SKIP_TENSOR_TESTS=1" +) + +"%PYTHON%" -c "import dpnp; print(dpnp.__version__)" +if %errorlevel% neq 0 exit 1 + +"%PYTHON%" -m dpctl -f +if %errorlevel% neq 0 exit 1 + +"%PYTHON%" -m pytest -ra --pyargs dpnp +if %errorlevel% neq 0 exit 1 diff --git a/conda-recipe-cf/run_test.sh b/conda-recipe-cf/run_test.sh new file mode 100755 index 00000000000..7493ee5ea25 --- /dev/null +++ b/conda-recipe-cf/run_test.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Skip tensor tests by default to avoid OOM in conda builds. +# Set SKIP_TENSOR_TESTS=0 to run them on machines with enough memory. +if [ -z "${SKIP_TENSOR_TESTS}" ]; then + export SKIP_TENSOR_TESTS=1 +fi + +set -e + +$PYTHON -c "import dpnp; print(dpnp.__version__)" +$PYTHON -m dpctl -f +$PYTHON -m pytest -ra --pyargs dpnp From 955f5c96396e10e84ca1487a361afa07553cd0d2 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 20 Aug 2026 13:59:43 +0200 Subject: [PATCH 02/12] Add dpctl to host requirements in conda-recipe-cf The host dependency on dpctl was missing, so find_package(Dpctl) had nothing to locate in the build environment. --- conda-recipe-cf/meta.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/conda-recipe-cf/meta.yaml b/conda-recipe-cf/meta.yaml index de92803afe1..6721a623510 100644 --- a/conda-recipe-cf/meta.yaml +++ b/conda-recipe-cf/meta.yaml @@ -45,6 +45,7 @@ requirements: - versioneer ==0.29 # versioneer dependency - tomli # [py<311] + - dpctl >={{ required_dpctl_version }} - mkl-devel-dpcpp >={{ required_compiler_and_mkl_version }} - onedpl-devel - tbb-devel From 569ecf77c431f779f937b6fbfdffd8a7b73734da Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 20 Aug 2026 14:11:57 +0200 Subject: [PATCH 03/12] Drop free-threaded 3.14 builds from conda-forge matrix A free-threaded (cp314t) dpctl is not yet available on conda-forge, so the free-threaded build/test entries fail to solve. Remove them from the build, test_linux and test_windows matrices with a TODO to restore them once conda-forge ships a free-threaded dpctl. --- .github/workflows/conda-package-cf.yml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/workflows/conda-package-cf.yml b/.github/workflows/conda-package-cf.yml index 6f078b6184d..39a287846a8 100644 --- a/.github/workflows/conda-package-cf.yml +++ b/.github/workflows/conda-package-cf.yml @@ -37,12 +37,8 @@ jobs: - python: '3.14' os: windows-2022 python_spec: '3.14.* *_cp314' - - python: '3.14' - os: ubuntu-22.04 - python_spec: '3.14.* *_cp314t' - - python: '3.14' - os: windows-2022 - python_spec: '3.14.* *_cp314t' + # TODO: add free-threaded builds ('3.14.* *_cp314t') once a + # free-threaded dpctl is available on conda-forge permissions: # Needed to cancel any previous runs that are not completed for a given workflow @@ -137,9 +133,8 @@ jobs: - python: '3.14' os: ubuntu-latest python_spec: '3.14.* *_cp314' - - python: '3.14' - os: ubuntu-latest - python_spec: '3.14.* *_cp314t' + # TODO: add free-threaded builds ('3.14.* *_cp314t') once a + # free-threaded dpctl is available on conda-forge env: dpnp-repo-path: '${{ github.workspace }}/source/' @@ -302,9 +297,8 @@ jobs: - python: '3.14' os: windows-2022 python_spec: '3.14.* *_cp314' - - python: '3.14' - os: windows-2022 - python_spec: '3.14.* *_cp314t' + # TODO: add free-threaded builds ('3.14.* *_cp314t') once a + # free-threaded dpctl is available on conda-forge env: dpnp-repo-path: '${{ github.workspace }}\source' From f0bec409f1f6987dc1a2d29dad479c1122763399 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 20 Aug 2026 16:26:47 +0200 Subject: [PATCH 04/12] Add build-prefix compiler lib dir to linker search path in conda-recipe-cf conda-forge folds the Intel compiler runtime libraries (libircmt.lib on Windows, libirc.a on Linux) into the build-only dpcpp_impl package, so they land in the build prefix and are off the linker's default search path. Add that directory via LIB (Windows) / LIBRARY_PATH (Linux) so icx can link. --- conda-recipe-cf/bld.bat | 5 +++++ conda-recipe-cf/build.sh | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/conda-recipe-cf/bld.bat b/conda-recipe-cf/bld.bat index beb5cdb6038..23024759aff 100644 --- a/conda-recipe-cf/bld.bat +++ b/conda-recipe-cf/bld.bat @@ -1,3 +1,8 @@ +:: conda-forge folds the Intel compiler runtime libs (libircmt.lib, ...) into the +:: build-only dpcpp_impl package (not visible from the host env), so they live +:: in the build prefix and are off the linker's default LIB +set "LIB=%BUILD_PREFIX%\Library\lib;%LIB%" + "%PYTHON%" setup.py clean --all set "MKLROOT=%PREFIX%/Library" diff --git a/conda-recipe-cf/build.sh b/conda-recipe-cf/build.sh index fc910cdb226..f5c4935f725 100755 --- a/conda-recipe-cf/build.sh +++ b/conda-recipe-cf/build.sh @@ -1,6 +1,8 @@ #!/bin/bash -# This is necessary to help DPC++ find Intel libraries such as SVML, IRNG, etc in build prefix +# conda-forge folds the Intel compiler runtime libs (libircmt.lib, ...) into the +# build-only dpcpp_impl package (not visible from the host env), so they live +# in the build prefix and are off the linker's default LIBRARY_PATH export LIBRARY_PATH="$LIBRARY_PATH:${BUILD_PREFIX}/lib" # Intel LLVM must cooperate with compiler and sysroot from conda From 41132a1d6eb04c576efdd9a8de984c09fdff126c Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 20 Aug 2026 19:14:46 +0200 Subject: [PATCH 05/12] Skip test_devices on dpctl < 0.23 in test_array_api_info dpctl.get_devices() returns a list in dpctl < 0.23 and a tuple from 0.23 onwards, so gate the tuple-equality assertion with with_requires. --- dpnp/tests/test_array_api_info.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dpnp/tests/test_array_api_info.py b/dpnp/tests/test_array_api_info.py index 60f81c0eddd..65a272038f4 100644 --- a/dpnp/tests/test_array_api_info.py +++ b/dpnp/tests/test_array_api_info.py @@ -9,6 +9,7 @@ is_win_platform, numpy_version, ) +from dpnp.tests.third_party.cupy.testing import with_requires info = dpnp.__array_namespace_info__() default_device = select_default_device() @@ -129,6 +130,7 @@ def test_dtypes_invalid_device(): info.dtypes(device="str") +@with_requires("dpctl>=0.23.0dev0") def test_devices(): devices = info.devices() assert isinstance(devices, tuple) From db01a034c531e64ec4cd27a6a85356fa9b667b1e Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 20 Aug 2026 20:30:57 +0200 Subject: [PATCH 06/12] Simplify OpenCL CPU driver registration in conda-forge workflow Everything in this lane comes from conda-forge, so the Intel-channel set-intel-ocl-icd-registry.ps1 helper and cl.cfg are never present. Drop the dead branches and keep only the manual ICD-loader registration of intelocl64.dll. --- .github/workflows/conda-package-cf.yml | 30 +++++++++----------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/.github/workflows/conda-package-cf.yml b/.github/workflows/conda-package-cf.yml index 39a287846a8..a21c66f5172 100644 --- a/.github/workflows/conda-package-cf.yml +++ b/.github/workflows/conda-package-cf.yml @@ -394,29 +394,19 @@ jobs: - name: List installed packages run: mamba list - - name: Activate OCL CPU RT + - name: Register the OpenCL CPU driver with the ICD loader shell: pwsh run: | - $script_path="$env:CONDA_PREFIX\Scripts\set-intel-ocl-icd-registry.ps1" - if (Test-Path $script_path) { - &$script_path - } else { - Write-Warning "File $script_path was NOT found!" - - # The conda-forge intel-opencl-rt does not ship the registration - # helper, so register the OpenCL CPU driver with the ICD loader manually - $vendors = "HKLM:\SOFTWARE\Khronos\OpenCL\Vendors" - $drivers = @(Get-ChildItem -Recurse -ErrorAction SilentlyContinue -Filter intelocl64.dll -Path "$env:CONDA_PREFIX\Library\bin") - if ($drivers.Count -eq 0) { throw "No OpenCL CPU driver (intelocl64.dll) found under $env:CONDA_PREFIX" } - if (-not (Test-Path $vendors)) { New-Item -Path $vendors -Force | Out-Null } - foreach ($driver in $drivers) { - New-ItemProperty -Path $vendors -Name $driver.FullName -Value 0 -PropertyType DWord -Force | Out-Null - } - Get-ItemProperty -Path $vendors + # conda-forge intel-opencl-rt does not ship a registration helper, + # so register the OpenCL CPU driver (intelocl64.dll) with the ICD loader manually + $vendors = "HKLM:\SOFTWARE\Khronos\OpenCL\Vendors" + $drivers = @(Get-ChildItem -Recurse -ErrorAction SilentlyContinue -Filter intelocl64.dll -Path "$env:CONDA_PREFIX\Library\bin") + if ($drivers.Count -eq 0) { throw "No OpenCL CPU driver (intelocl64.dll) found under $env:CONDA_PREFIX" } + if (-not (Test-Path $vendors)) { New-Item -Path $vendors -Force | Out-Null } + foreach ($driver in $drivers) { + New-ItemProperty -Path $vendors -Name $driver.FullName -Value 0 -PropertyType DWord -Force | Out-Null } - # Check the variable assisting OpenCL CPU driver to find TBB DLLs which are not located where it expects them by default - $cl_cfg="$env:CONDA_PREFIX\Library\bin\cl.cfg" - Get-Content -Tail 5 -Path $cl_cfg + Get-ItemProperty -Path $vendors - name: Smoke test run: | From 03bc78916db077c295c6819e0bfa418ee1a4b2c3 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 20 Aug 2026 22:29:08 +0200 Subject: [PATCH 07/12] Restore legal-notice description in conda-recipe-cf meta.yaml Intel internal validation scans every recipe meta.yaml in the source tree and requires the LEGAL NOTICE / EULA block in about/description, so keep it in sync with conda-recipe/meta.yaml. --- conda-recipe-cf/meta.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/conda-recipe-cf/meta.yaml b/conda-recipe-cf/meta.yaml index 6721a623510..e98144ad7cd 100644 --- a/conda-recipe-cf/meta.yaml +++ b/conda-recipe-cf/meta.yaml @@ -82,10 +82,14 @@ about: license_file: LICENSE.txt summary: 'Data Parallel Extension for NumPy' description: | - Data Parallel Extension for NumPy (dpnp) is a Python library that - implements a subset of NumPy that can be executed on any data parallel - device. The subset is a drop-in replacement of core NumPy functions and - numerical data types. + LEGAL NOTICE: Use of this software package is subject to the + software license agreement (as set forth above, in the license section of + the installed Conda package and/or the README file) and all notices, + disclaimers or license terms for third party or open source software + included in or with the software. +

+ EULA: BSD-3-Clause +

extra: recipe-maintainers: From 9fb2988483e8976d022c4d3619ffeaf1f5ac117a Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 20 Aug 2026 22:34:51 +0200 Subject: [PATCH 08/12] Skip tensor tests requiring dpctl >= 0.23 device tuple return types dpctl 0.23 changed get_devices() and SyclDevice.create_sub_devices() to return tuples instead of lists. Gate the affected tensor tests with with_requires so they skip on older dpctl (e.g. conda-forge 0.22.1). --- dpnp/tests/tensor/test_tensor_array_api_inspection.py | 2 ++ dpnp/tests/tensor/test_usm_ndarray_dlpack.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/dpnp/tests/tensor/test_tensor_array_api_inspection.py b/dpnp/tests/tensor/test_tensor_array_api_inspection.py index a04bccaf5db..8608878d932 100644 --- a/dpnp/tests/tensor/test_tensor_array_api_inspection.py +++ b/dpnp/tests/tensor/test_tensor_array_api_inspection.py @@ -36,6 +36,7 @@ default_device_index_type, default_device_int_type, ) +from dpnp.tests.third_party.cupy.testing import with_requires _dtypes_no_fp16_fp64 = { "bool": dpt.bool, @@ -72,6 +73,7 @@ def test_array_api_inspection_default_device(): assert dpt.__array_namespace_info__().default_device().sycl_device == dev +@with_requires("dpctl>=0.23.0dev0") def test_array_api_inspection_devices(): try: devices2 = dpctl.get_devices() diff --git a/dpnp/tests/tensor/test_usm_ndarray_dlpack.py b/dpnp/tests/tensor/test_usm_ndarray_dlpack.py index 7d374199562..f9c3f811a55 100644 --- a/dpnp/tests/tensor/test_usm_ndarray_dlpack.py +++ b/dpnp/tests/tensor/test_usm_ndarray_dlpack.py @@ -36,6 +36,7 @@ import dpnp.tensor as dpt import dpnp.tensor._dlpack as _dlp import dpnp.tensor._usmarray as dpt_arr +from dpnp.tests.third_party.cupy.testing import with_requires from .helper import ( get_queue_or_skip, @@ -232,6 +233,7 @@ def test_from_dlpack_fortran_contig_array_roundtripping(): assert dpt.all(dpt.equal(ar2d_c, ar2d_r)) +@with_requires("dpctl>=0.23.0dev0") def test_dlpack_from_subdevice(): """ This test checks that array allocated on a sub-device, @@ -584,6 +586,7 @@ def test_from_dlpack_to_kdlcpu_strides(mod, typestr): assert V.strides == W.strides +@with_requires("dpctl>=0.23.0dev0") def test_dlpack_from_subdevice_to_kdlcpu(): """ Check that array allocated on a sub-device can be From 5a2bee7c7cfe0dce6ec71217fded3148ad0448cf Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Fri, 21 Aug 2026 12:26:36 +0200 Subject: [PATCH 09/12] Combine conda-forge test_linux and test_windows into one job Unify the two test jobs into a single matrix job (ubuntu-latest, windows-2022) using bash for both OSes, keeping only Set Swap Space (Linux) and the OpenCL CPU driver registration (Windows) OS-specific. Also drop the unused cmd-only steps carried over from the legacy layout. --- .github/workflows/conda-package-cf.yml | 220 ++++--------------------- 1 file changed, 29 insertions(+), 191 deletions(-) diff --git a/.github/workflows/conda-package-cf.yml b/.github/workflows/conda-package-cf.yml index a21c66f5172..db0f53f6a35 100644 --- a/.github/workflows/conda-package-cf.yml +++ b/.github/workflows/conda-package-cf.yml @@ -112,13 +112,13 @@ jobs: name: ${{ env.package-name }} ${{ runner.os }} Python ${{ env.python-label }} path: ${{ env.CONDA_BLD }}${{ env.package-name }}-*.conda - test_linux: + test: name: Test needs: build runs-on: ${{ matrix.os }} - timeout-minutes: 100 + timeout-minutes: 120 defaults: run: @@ -128,11 +128,14 @@ jobs: fail-fast: false matrix: python: ['3.10', '3.11', '3.12', '3.13'] - os: [ubuntu-latest] + os: [ubuntu-latest, windows-2022] include: - python: '3.14' os: ubuntu-latest python_spec: '3.14.* *_cp314' + - python: '3.14' + os: windows-2022 + python_spec: '3.14.* *_cp314' # TODO: add free-threaded builds ('3.14.* *_cp314t') once a # free-threaded dpctl is available on conda-forge @@ -140,13 +143,14 @@ jobs: dpnp-repo-path: '${{ github.workspace }}/source/' create-conda-channel-env: 'source/environments/create_conda_channel.yml' channel-path: '${{ github.workspace }}/channel/' - pkg-path-in-channel: '${{ github.workspace }}/channel/linux-64/' + pkg-path-in-channel: "${{ github.workspace }}/channel/${{ matrix.os == 'ubuntu-latest' && 'linux-64' || 'win-64' }}/" ver-json-path: '${{ github.workspace }}/version.json' python-label: ${{ endsWith(matrix.python_spec, 't') && format('{0}t', matrix.python) || matrix.python }} python-install-spec: "python ${{ matrix.python_spec || format('{0}.*', matrix.python) }}" steps: - name: Set Swap Space + if: runner.os == 'Linux' uses: pierotofy/set-swap-space@49819abfb41bd9b44fb781159c033dba90353a7c # v1.0 with: swap-size-gb: 8 @@ -208,6 +212,7 @@ jobs: mamba install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} pytest pytest-xdist "${{ env.python-install-spec }}" ${{ env.TEST_CHANNELS }} env: TEST_CHANNELS: '-c ${{ env.channel-path }} ${{ env.channels-list }}' + MAMBA_NO_LOW_SPEED_LIMIT: 1 - name: ReInstall dpnp if: steps.install_dpnp.outcome == 'failure' @@ -215,10 +220,26 @@ jobs: mamba install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} pytest pytest-xdist "${{ env.python-install-spec }}" ${{ env.TEST_CHANNELS }} env: TEST_CHANNELS: '-c ${{ env.channel-path }} ${{ env.channels-list }}' + MAMBA_NO_LOW_SPEED_LIMIT: 1 - name: List installed packages run: mamba list + - name: Register the OpenCL CPU driver with the ICD loader + if: runner.os == 'Windows' + shell: pwsh + run: | + # conda-forge intel-opencl-rt does not ship a registration helper, + # so register the OpenCL CPU driver (intelocl64.dll) with the ICD loader manually + $vendors = "HKLM:\SOFTWARE\Khronos\OpenCL\Vendors" + $drivers = @(Get-ChildItem -Recurse -ErrorAction SilentlyContinue -Filter intelocl64.dll -Path "$env:CONDA_PREFIX\Library\bin") + if ($drivers.Count -eq 0) { throw "No OpenCL CPU driver (intelocl64.dll) found under $env:CONDA_PREFIX" } + if (-not (Test-Path $vendors)) { New-Item -Path $vendors -Force | Out-Null } + foreach ($driver in $drivers) { + New-ItemProperty -Path $vendors -Name $driver.FullName -Value 0 -PropertyType DWord -Force | Out-Null + } + Get-ItemProperty -Path $vendors + - name: Smoke test run: | python -c "import dpctl; dpctl.lsplatform()" @@ -237,12 +258,13 @@ jobs: - name: Run tests if: env.rerun-tests-on-failure == 'true' - id: run_tests_linux + id: run_tests uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 with: timeout_minutes: ${{ env.rerun-tests-timeout }} max_attempts: ${{ env.rerun-tests-max-attempts }} retry_on: any + shell: bash command: | . $CONDA/etc/profile.d/conda.sh . $CONDA/etc/profile.d/mamba.sh @@ -263,200 +285,16 @@ jobs: - name: Run tensor tests if: env.rerun-tests-on-failure == 'true' - id: run_tests_tensor_linux + id: run_tests_tensor uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 with: timeout_minutes: ${{ env.rerun-tests-timeout }} max_attempts: ${{ env.rerun-tests-max-attempts }} retry_on: any + shell: bash command: | . $CONDA/etc/profile.d/conda.sh . $CONDA/etc/profile.d/mamba.sh mamba activate ${{ env.test-env-name }} python -m pytest -n auto -ra --pyargs dpnp.tests.tensor - - test_windows: - name: Test - - needs: build - - runs-on: ${{ matrix.os }} - timeout-minutes: 120 - - defaults: - run: - shell: cmd /C CALL {0} - - strategy: - fail-fast: false - matrix: - python: ['3.10', '3.11', '3.12', '3.13'] - os: [windows-2022] - include: - - python: '3.14' - os: windows-2022 - python_spec: '3.14.* *_cp314' - # TODO: add free-threaded builds ('3.14.* *_cp314t') once a - # free-threaded dpctl is available on conda-forge - - env: - dpnp-repo-path: '${{ github.workspace }}\source' - create-conda-channel-env: 'source\environments\create_conda_channel.yml' - channel-path: '${{ github.workspace }}\channel\' - pkg-path-in-channel: '${{ github.workspace }}\channel\win-64\' - ver-json-path: '${{ github.workspace }}\version.json' - python-label: ${{ endsWith(matrix.python_spec, 't') && format('{0}t', matrix.python) || matrix.python }} - python-install-spec: "python ${{ matrix.python_spec || format('{0}.*', matrix.python) }}" - - steps: - - name: Checkout DPNP repo - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: ${{ env.fetch-depth }} - path: ${{ env.dpnp-repo-path }} - - - name: Download artifact - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: ${{ env.package-name }} ${{ runner.os }} Python ${{ env.python-label }} - path: ${{ env.pkg-path-in-channel }} - - - name: Store a path to package archive - run: | - @echo on - - set SEARCH_SCRIPT="DIR ${{ env.pkg-path-in-channel }} /s/b | FINDSTR /r "dpnp-.*\.conda"" - FOR /F "tokens=* USEBACKQ" %%F IN (`%SEARCH_SCRIPT%`) DO ( - SET FULL_PACKAGE_PATH=%%F - ) - echo FULL_PACKAGE_PATH: %FULL_PACKAGE_PATH% - (echo FULL_PACKAGE_PATH=%FULL_PACKAGE_PATH%) >> %GITHUB_ENV% - - - name: Setup miniconda - id: setup_miniconda - continue-on-error: true - uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 - with: - miniforge-version: latest - use-mamba: 'true' - conda-remove-defaults: 'true' - environment-file: ${{ env.create-conda-channel-env }} - activate-environment: ${{ env.test-env-name }} - - - name: ReSetup miniconda - if: steps.setup_miniconda.outcome == 'failure' - uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 - with: - miniforge-version: latest - use-mamba: 'true' - conda-remove-defaults: 'true' - environment-file: ${{ env.create-conda-channel-env }} - activate-environment: ${{ env.test-env-name }} - - - name: Store conda paths as envs - run: | - @echo on - (echo CONDA_LIB_PATH=%CONDA_PREFIX%\Library\lib\) >> %GITHUB_ENV% - (echo CONDA_LIB_BIN_PATH=%CONDA_PREFIX%\Library\bin\) >> %GITHUB_ENV% - - - name: Create conda channel - run: | - @echo on - python -m conda_index ${{ env.channel-path }} - - - name: Test conda channel - run: | - @echo on - conda search ${{ env.package-name }} -c ${{ env.channel-path }} --override-channels --info --json > ${{ env.ver-json-path }} - - - name: Dump version.json - run: more ${{ env.ver-json-path }} - - - name: Get package version - run: | - @echo on - set "SCRIPT=${{ env.ver-script-part1 }} ${{ env.ver-script-part2 }}" - FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO ( - set PACKAGE_VERSION=%%F - ) - echo PACKAGE_VERSION: %PACKAGE_VERSION% - (echo PACKAGE_VERSION=%PACKAGE_VERSION%) >> %GITHUB_ENV% - - - name: Install dpnp - run: | - @echo on - mamba install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} pytest pytest-xdist "${{ env.python-install-spec }}" ${{ env.TEST_CHANNELS }} - env: - TEST_CHANNELS: '-c ${{ env.channel-path }} ${{ env.channels-list }}' - MAMBA_NO_LOW_SPEED_LIMIT: 1 - - - name: List installed packages - run: mamba list - - - name: Register the OpenCL CPU driver with the ICD loader - shell: pwsh - run: | - # conda-forge intel-opencl-rt does not ship a registration helper, - # so register the OpenCL CPU driver (intelocl64.dll) with the ICD loader manually - $vendors = "HKLM:\SOFTWARE\Khronos\OpenCL\Vendors" - $drivers = @(Get-ChildItem -Recurse -ErrorAction SilentlyContinue -Filter intelocl64.dll -Path "$env:CONDA_PREFIX\Library\bin") - if ($drivers.Count -eq 0) { throw "No OpenCL CPU driver (intelocl64.dll) found under $env:CONDA_PREFIX" } - if (-not (Test-Path $vendors)) { New-Item -Path $vendors -Force | Out-Null } - foreach ($driver in $drivers) { - New-ItemProperty -Path $vendors -Name $driver.FullName -Value 0 -PropertyType DWord -Force | Out-Null - } - Get-ItemProperty -Path $vendors - - - name: Smoke test - run: | - python -c "import dpctl; dpctl.lsplatform()" - python -c "import dpnp; print(dpnp.__version__)" - - - name: Run tests - if: env.rerun-tests-on-failure != 'true' - shell: pwsh - run: | - $env:SKIP_TENSOR_TESTS=1 - if ("${{ env.python-label }}" -eq "${{ env.python-ver-test-all-dtypes }}") { - $env:DPNP_TEST_ALL_INT_TYPES=1 - python -m pytest -ra --pyargs ${{ env.package-name }}.tests - } else { - python -m pytest -n auto -ra --pyargs ${{ env.package-name }}.tests - } - - - name: Run tests - if: env.rerun-tests-on-failure == 'true' - id: run_tests_win - uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 - with: - timeout_minutes: ${{ env.rerun-tests-timeout }} - max_attempts: ${{ env.rerun-tests-max-attempts }} - retry_on: any - shell: pwsh - command: | - $env:SKIP_TENSOR_TESTS=1 - if ( "${{ env.python-label }}" -eq "${{ env.python-ver-test-all-dtypes }}" ) { - $env:DPNP_TEST_ALL_INT_TYPES=1 - python -m pytest -ra --pyargs ${{ env.package-name }}.tests - } else { - python -m pytest -n auto -ra --pyargs ${{ env.package-name }}.tests - } - - - name: Run tensor tests - if: env.rerun-tests-on-failure != 'true' - shell: pwsh - run: | - python -m pytest -n auto -ra --pyargs dpnp.tests.tensor - - - name: Run tensor tests - if: env.rerun-tests-on-failure == 'true' - id: run_tests_tensor_win - uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 - with: - timeout_minutes: ${{ env.rerun-tests-timeout }} - max_attempts: ${{ env.rerun-tests-max-attempts }} - retry_on: any - shell: pwsh - command: | - python -m pytest -n auto -ra --pyargs dpnp.tests.tensor From cc8cc39fcf15eb60177006a46ccde89020e487b9 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Fri, 21 Aug 2026 12:33:08 +0200 Subject: [PATCH 10/12] Extract conda-forge CI into a reusable build/test workflow Move the build and test jobs into a reusable workflow_call workflow (conda-build-test.yml) parameterized by runner, conda subdir, channels and recipe dir. Reduce conda-package-cf.yml to thin per-OS callers with a concurrency group replacing the cancel-workflow action. --- .github/workflows/conda-build-test.yml | 293 +++++++++++++++++++++++ .github/workflows/conda-package-cf.yml | 308 ++----------------------- 2 files changed, 313 insertions(+), 288 deletions(-) create mode 100644 .github/workflows/conda-build-test.yml diff --git a/.github/workflows/conda-build-test.yml b/.github/workflows/conda-build-test.yml new file mode 100644 index 00000000000..0a447ec7ba6 --- /dev/null +++ b/.github/workflows/conda-build-test.yml @@ -0,0 +1,293 @@ +name: Reusable conda build and test + +on: + workflow_call: + inputs: + runs-on: + description: Runner image to execute the jobs on + required: true + type: string + conda-subdir: + description: Conda platform subdir (e.g. linux-64, win-64) + required: true + type: string + channels-list: + description: Conda channels to build and test against + required: true + type: string + recipe-dir: + description: Path to the conda recipe directory + required: true + type: string + +permissions: read-all + +env: + package-name: dpnp + module-name: dpnp + ver-script-part1: "import json; f = open('version.json', 'r'); j = json.load(f); f.close(); " + ver-script-part2: "d = j['dpnp'][0]; print('='.join((d[s] for s in ('version', 'build'))))" + fetch-depth: 1 + python-ver-test-all-dtypes: '3.14' + test-env-name: 'test' + rerun-tests-on-failure: 'true' + rerun-tests-max-attempts: 2 + rerun-tests-timeout: 45 + +jobs: + build: + name: Build + + runs-on: ${{ inputs.runs-on }} + timeout-minutes: 90 + + defaults: + run: + shell: bash -el {0} + + strategy: + fail-fast: false + matrix: + python: ['3.10', '3.11', '3.12', '3.13'] + include: + - python: '3.14' + python_spec: '3.14.* *_cp314' + # TODO: add free-threaded builds ('3.14.* *_cp314t') once a + # free-threaded dpctl is available on conda-forge + + env: + build-conda-pkg-env: 'environments/build_conda_pkg.yml' + build-env-name: 'build' + python-label: ${{ endsWith(matrix.python_spec, 't') && format('{0}t', matrix.python) || matrix.python }} + python-conda-spec: ${{ matrix.python_spec || matrix.python }} + + steps: + - name: Checkout DPNP repo + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + + - name: Setup miniconda + id: setup_miniconda + continue-on-error: true + uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 + with: + miniforge-version: latest + use-mamba: 'true' + conda-remove-defaults: 'true' + environment-file: ${{ env.build-conda-pkg-env }} + activate-environment: ${{ env.build-env-name }} + + - name: ReSetup miniconda + if: steps.setup_miniconda.outcome == 'failure' + uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 + with: + miniforge-version: latest + use-mamba: 'true' + conda-remove-defaults: 'true' + environment-file: ${{ env.build-conda-pkg-env }} + activate-environment: ${{ env.build-env-name }} + + - name: List installed packages + run: mamba list + + - name: Store conda paths as envs + run: | + echo "CONDA_BLD=$CONDA_PREFIX/conda-bld/${{ inputs.conda-subdir }}/" | tr "\\\\" '/' >> "$GITHUB_ENV" + + - name: Build conda package + id: build_conda_pkg + continue-on-error: true + run: conda-build --no-test --python "${{ env.python-conda-spec }}" --numpy 2.0 ${{ inputs.channels-list }} ${{ inputs.recipe-dir }} + + - name: ReBuild conda package + if: steps.build_conda_pkg.outcome == 'failure' + run: conda-build --no-test --python "${{ env.python-conda-spec }}" --numpy 2.0 ${{ inputs.channels-list }} ${{ inputs.recipe-dir }} + + - name: Upload artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ${{ env.package-name }} ${{ runner.os }} Python ${{ env.python-label }} + path: ${{ env.CONDA_BLD }}${{ env.package-name }}-*.conda + + test: + name: Test + + needs: build + + runs-on: ${{ inputs.runs-on }} + timeout-minutes: 120 + + defaults: + run: + shell: bash -el {0} + + strategy: + fail-fast: false + matrix: + python: ['3.10', '3.11', '3.12', '3.13'] + include: + - python: '3.14' + python_spec: '3.14.* *_cp314' + # TODO: add free-threaded builds ('3.14.* *_cp314t') once a + # free-threaded dpctl is available on conda-forge + + env: + dpnp-repo-path: '${{ github.workspace }}/source/' + create-conda-channel-env: 'source/environments/create_conda_channel.yml' + channel-path: '${{ github.workspace }}/channel/' + pkg-path-in-channel: '${{ github.workspace }}/channel/${{ inputs.conda-subdir }}/' + ver-json-path: '${{ github.workspace }}/version.json' + python-label: ${{ endsWith(matrix.python_spec, 't') && format('{0}t', matrix.python) || matrix.python }} + python-install-spec: "python ${{ matrix.python_spec || format('{0}.*', matrix.python) }}" + + steps: + - name: Set Swap Space + if: runner.os == 'Linux' + uses: pierotofy/set-swap-space@49819abfb41bd9b44fb781159c033dba90353a7c # v1.0 + with: + swap-size-gb: 8 + + - name: Checkout DPNP repo + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: ${{ env.fetch-depth }} + path: ${{ env.dpnp-repo-path }} + + - name: Download artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ env.package-name }} ${{ runner.os }} Python ${{ env.python-label }} + path: ${{ env.pkg-path-in-channel }} + + - name: Setup miniconda + id: setup_miniconda + continue-on-error: true + uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 + with: + miniforge-version: latest + use-mamba: 'true' + conda-remove-defaults: 'true' + environment-file: ${{ env.create-conda-channel-env }} + activate-environment: ${{ env.test-env-name }} + + - name: ReSetup miniconda + if: steps.setup_miniconda.outcome == 'failure' + uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 + with: + miniforge-version: latest + use-mamba: 'true' + conda-remove-defaults: 'true' + environment-file: ${{ env.create-conda-channel-env }} + activate-environment: ${{ env.test-env-name }} + + - name: Create conda channel + run: | + python -m conda_index ${{ env.channel-path }} + + - name: Test conda channel + run: | + conda search ${{ env.package-name }} -c ${{ env.channel-path }} --override-channels --info --json > ${{ env.ver-json-path }} + cat ${{ env.ver-json-path }} + + - name: Get package version + run: | + PACKAGE_VERSION=$(python -c "${{ env.ver-script-part1 }} ${{ env.ver-script-part2 }}") + export PACKAGE_VERSION + + echo "PACKAGE_VERSION=${PACKAGE_VERSION}" + echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> "$GITHUB_ENV" + + - name: Install dpnp + id: install_dpnp + continue-on-error: true + run: | + mamba install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} pytest pytest-xdist "${{ env.python-install-spec }}" ${{ env.TEST_CHANNELS }} + env: + TEST_CHANNELS: '-c ${{ env.channel-path }} ${{ inputs.channels-list }}' + MAMBA_NO_LOW_SPEED_LIMIT: 1 + + - name: ReInstall dpnp + if: steps.install_dpnp.outcome == 'failure' + run: | + mamba install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} pytest pytest-xdist "${{ env.python-install-spec }}" ${{ env.TEST_CHANNELS }} + env: + TEST_CHANNELS: '-c ${{ env.channel-path }} ${{ inputs.channels-list }}' + MAMBA_NO_LOW_SPEED_LIMIT: 1 + + - name: List installed packages + run: mamba list + + - name: Register the OpenCL CPU driver with the ICD loader + if: runner.os == 'Windows' + shell: pwsh + run: | + # conda-forge intel-opencl-rt does not ship a registration helper, + # so register the OpenCL CPU driver (intelocl64.dll) with the ICD loader manually + $vendors = "HKLM:\SOFTWARE\Khronos\OpenCL\Vendors" + $drivers = @(Get-ChildItem -Recurse -ErrorAction SilentlyContinue -Filter intelocl64.dll -Path "$env:CONDA_PREFIX\Library\bin") + if ($drivers.Count -eq 0) { throw "No OpenCL CPU driver (intelocl64.dll) found under $env:CONDA_PREFIX" } + if (-not (Test-Path $vendors)) { New-Item -Path $vendors -Force | Out-Null } + foreach ($driver in $drivers) { + New-ItemProperty -Path $vendors -Name $driver.FullName -Value 0 -PropertyType DWord -Force | Out-Null + } + Get-ItemProperty -Path $vendors + + - name: Smoke test + run: | + python -c "import dpctl; dpctl.lsplatform()" + python -c "import dpnp; print(dpnp.__version__)" + + - name: Run tests + if: env.rerun-tests-on-failure != 'true' + run: | + export SKIP_TENSOR_TESTS=1 + if [[ "${{ env.python-label }}" == "${{ env.python-ver-test-all-dtypes }}" ]]; then + export DPNP_TEST_ALL_INT_TYPES=1 + python -m pytest -ra --pyargs ${{ env.module-name }}.tests + else + python -m pytest -n auto -ra --pyargs ${{ env.module-name }}.tests + fi + + - name: Run tests + if: env.rerun-tests-on-failure == 'true' + id: run_tests + uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 + with: + timeout_minutes: ${{ env.rerun-tests-timeout }} + max_attempts: ${{ env.rerun-tests-max-attempts }} + retry_on: any + shell: bash + command: | + . $CONDA/etc/profile.d/conda.sh + . $CONDA/etc/profile.d/mamba.sh + mamba activate ${{ env.test-env-name }} + export SKIP_TENSOR_TESTS=1 + + if [[ "${{ env.python-label }}" == "${{ env.python-ver-test-all-dtypes }}" ]]; then + export DPNP_TEST_ALL_INT_TYPES=1 + python -m pytest -ra --pyargs ${{ env.module-name }}.tests + else + python -m pytest -n auto -ra --pyargs ${{ env.module-name }}.tests + fi + + - name: Run tensor tests + if: env.rerun-tests-on-failure != 'true' + run: | + python -m pytest -n auto -ra --pyargs dpnp.tests.tensor + + - name: Run tensor tests + if: env.rerun-tests-on-failure == 'true' + id: run_tests_tensor + uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 + with: + timeout_minutes: ${{ env.rerun-tests-timeout }} + max_attempts: ${{ env.rerun-tests-max-attempts }} + retry_on: any + shell: bash + command: | + . $CONDA/etc/profile.d/conda.sh + . $CONDA/etc/profile.d/mamba.sh + mamba activate ${{ env.test-env-name }} + + python -m pytest -n auto -ra --pyargs dpnp.tests.tensor diff --git a/.github/workflows/conda-package-cf.yml b/.github/workflows/conda-package-cf.yml index db0f53f6a35..7df0beafdf3 100644 --- a/.github/workflows/conda-package-cf.yml +++ b/.github/workflows/conda-package-cf.yml @@ -8,293 +8,25 @@ on: permissions: read-all -env: - package-name: dpnp - recipe-dir: conda-recipe-cf - channels-list: '-c conda-forge --override-channels' - ver-script-part1: "import json; f = open('version.json', 'r'); j = json.load(f); f.close(); " - ver-script-part2: "d = j['dpnp'][0]; print('='.join((d[s] for s in ('version', 'build'))))" - fetch-depth: 1 - python-ver-test-all-dtypes: '3.14' - test-env-name: 'test' - rerun-tests-on-failure: 'true' - rerun-tests-max-attempts: 2 - rerun-tests-timeout: 45 +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: - build: - name: Build - - strategy: - fail-fast: false - matrix: - python: ['3.10', '3.11', '3.12', '3.13'] - os: [ubuntu-22.04, windows-2022] - include: - - python: '3.14' - os: ubuntu-22.04 - python_spec: '3.14.* *_cp314' - - python: '3.14' - os: windows-2022 - python_spec: '3.14.* *_cp314' - # TODO: add free-threaded builds ('3.14.* *_cp314t') once a - # free-threaded dpctl is available on conda-forge - - permissions: - # Needed to cancel any previous runs that are not completed for a given workflow - actions: write - - runs-on: ${{ matrix.os }} - timeout-minutes: 90 - - defaults: - run: - shell: ${{ matrix.os == 'windows-2022' && 'cmd /C CALL {0}' || 'bash -el {0}' }} - - env: - build-conda-pkg-env: 'environments/build_conda_pkg.yml' - build-env-name: 'build' - python-label: ${{ endsWith(matrix.python_spec, 't') && format('{0}t', matrix.python) || matrix.python }} - python-conda-spec: ${{ matrix.python_spec || matrix.python }} - - steps: - - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@d07a454dad7609a92316b57b23c9ccfd4f59af66 # 0.13.1 - with: - access_token: ${{ github.token }} - - - name: Checkout DPNP repo - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - - - name: Setup miniconda - id: setup_miniconda - continue-on-error: true - uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 - with: - miniforge-version: latest - use-mamba: 'true' - conda-remove-defaults: 'true' - environment-file: ${{ env.build-conda-pkg-env }} - activate-environment: ${{ env.build-env-name }} - - - name: ReSetup miniconda - if: steps.setup_miniconda.outcome == 'failure' - uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 - with: - miniforge-version: latest - use-mamba: 'true' - conda-remove-defaults: 'true' - environment-file: ${{ env.build-conda-pkg-env }} - activate-environment: ${{ env.build-env-name }} - - - name: List installed packages - run: mamba list - - - name: Store conda paths as envs - shell: bash -el {0} - run: | - echo "CONDA_BLD=$CONDA_PREFIX/conda-bld/${{ runner.os == 'Linux' && 'linux' || 'win' }}-64/" | tr "\\\\" '/' >> "$GITHUB_ENV" - - - name: Build conda package - id: build_conda_pkg - continue-on-error: true - run: conda-build --no-test --python "${{ env.python-conda-spec }}" --numpy 2.0 ${{ env.channels-list }} ${{ env.recipe-dir }} - - - name: ReBuild conda package - if: steps.build_conda_pkg.outcome == 'failure' - run: conda-build --no-test --python "${{ env.python-conda-spec }}" --numpy 2.0 ${{ env.channels-list }} ${{ env.recipe-dir }} - - - name: Upload artifact - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: ${{ env.package-name }} ${{ runner.os }} Python ${{ env.python-label }} - path: ${{ env.CONDA_BLD }}${{ env.package-name }}-*.conda - - test: - name: Test - - needs: build - - runs-on: ${{ matrix.os }} - timeout-minutes: 120 - - defaults: - run: - shell: bash -el {0} - - strategy: - fail-fast: false - matrix: - python: ['3.10', '3.11', '3.12', '3.13'] - os: [ubuntu-latest, windows-2022] - include: - - python: '3.14' - os: ubuntu-latest - python_spec: '3.14.* *_cp314' - - python: '3.14' - os: windows-2022 - python_spec: '3.14.* *_cp314' - # TODO: add free-threaded builds ('3.14.* *_cp314t') once a - # free-threaded dpctl is available on conda-forge - - env: - dpnp-repo-path: '${{ github.workspace }}/source/' - create-conda-channel-env: 'source/environments/create_conda_channel.yml' - channel-path: '${{ github.workspace }}/channel/' - pkg-path-in-channel: "${{ github.workspace }}/channel/${{ matrix.os == 'ubuntu-latest' && 'linux-64' || 'win-64' }}/" - ver-json-path: '${{ github.workspace }}/version.json' - python-label: ${{ endsWith(matrix.python_spec, 't') && format('{0}t', matrix.python) || matrix.python }} - python-install-spec: "python ${{ matrix.python_spec || format('{0}.*', matrix.python) }}" - - steps: - - name: Set Swap Space - if: runner.os == 'Linux' - uses: pierotofy/set-swap-space@49819abfb41bd9b44fb781159c033dba90353a7c # v1.0 - with: - swap-size-gb: 8 - - - name: Checkout DPNP repo - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: ${{ env.fetch-depth }} - path: ${{ env.dpnp-repo-path }} - - - name: Download artifact - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: ${{ env.package-name }} ${{ runner.os }} Python ${{ env.python-label }} - path: ${{ env.pkg-path-in-channel }} - - - name: Setup miniconda - id: setup_miniconda - continue-on-error: true - uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 - with: - miniforge-version: latest - use-mamba: 'true' - conda-remove-defaults: 'true' - environment-file: ${{ env.create-conda-channel-env }} - activate-environment: ${{ env.test-env-name }} - - - name: ReSetup miniconda - if: steps.setup_miniconda.outcome == 'failure' - uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 - with: - miniforge-version: latest - use-mamba: 'true' - conda-remove-defaults: 'true' - environment-file: ${{ env.create-conda-channel-env }} - activate-environment: ${{ env.test-env-name }} - - - name: Create conda channel - run: | - python -m conda_index ${{ env.channel-path }} - - - name: Test conda channel - run: | - conda search ${{ env.package-name }} -c ${{ env.channel-path }} --override-channels --info --json > ${{ env.ver-json-path }} - cat ${{ env.ver-json-path }} - - - name: Get package version - run: | - PACKAGE_VERSION=$(python -c "${{ env.ver-script-part1 }} ${{ env.ver-script-part2 }}") - export PACKAGE_VERSION - - echo "PACKAGE_VERSION=${PACKAGE_VERSION}" - echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> "$GITHUB_ENV" - - - name: Install dpnp - id: install_dpnp - continue-on-error: true - run: | - mamba install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} pytest pytest-xdist "${{ env.python-install-spec }}" ${{ env.TEST_CHANNELS }} - env: - TEST_CHANNELS: '-c ${{ env.channel-path }} ${{ env.channels-list }}' - MAMBA_NO_LOW_SPEED_LIMIT: 1 - - - name: ReInstall dpnp - if: steps.install_dpnp.outcome == 'failure' - run: | - mamba install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} pytest pytest-xdist "${{ env.python-install-spec }}" ${{ env.TEST_CHANNELS }} - env: - TEST_CHANNELS: '-c ${{ env.channel-path }} ${{ env.channels-list }}' - MAMBA_NO_LOW_SPEED_LIMIT: 1 - - - name: List installed packages - run: mamba list - - - name: Register the OpenCL CPU driver with the ICD loader - if: runner.os == 'Windows' - shell: pwsh - run: | - # conda-forge intel-opencl-rt does not ship a registration helper, - # so register the OpenCL CPU driver (intelocl64.dll) with the ICD loader manually - $vendors = "HKLM:\SOFTWARE\Khronos\OpenCL\Vendors" - $drivers = @(Get-ChildItem -Recurse -ErrorAction SilentlyContinue -Filter intelocl64.dll -Path "$env:CONDA_PREFIX\Library\bin") - if ($drivers.Count -eq 0) { throw "No OpenCL CPU driver (intelocl64.dll) found under $env:CONDA_PREFIX" } - if (-not (Test-Path $vendors)) { New-Item -Path $vendors -Force | Out-Null } - foreach ($driver in $drivers) { - New-ItemProperty -Path $vendors -Name $driver.FullName -Value 0 -PropertyType DWord -Force | Out-Null - } - Get-ItemProperty -Path $vendors - - - name: Smoke test - run: | - python -c "import dpctl; dpctl.lsplatform()" - python -c "import dpnp; print(dpnp.__version__)" - - - name: Run tests - if: env.rerun-tests-on-failure != 'true' - run: | - export SKIP_TENSOR_TESTS=1 - if [[ "${{ env.python-label }}" == "${{ env.python-ver-test-all-dtypes }}" ]]; then - export DPNP_TEST_ALL_INT_TYPES=1 - python -m pytest -ra --pyargs ${{ env.package-name }}.tests - else - python -m pytest -n auto -ra --pyargs ${{ env.package-name }}.tests - fi - - - name: Run tests - if: env.rerun-tests-on-failure == 'true' - id: run_tests - uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 - with: - timeout_minutes: ${{ env.rerun-tests-timeout }} - max_attempts: ${{ env.rerun-tests-max-attempts }} - retry_on: any - shell: bash - command: | - . $CONDA/etc/profile.d/conda.sh - . $CONDA/etc/profile.d/mamba.sh - mamba activate ${{ env.test-env-name }} - export SKIP_TENSOR_TESTS=1 - - if [[ "${{ env.python-label }}" == "${{ env.python-ver-test-all-dtypes }}" ]]; then - export DPNP_TEST_ALL_INT_TYPES=1 - python -m pytest -ra --pyargs ${{ env.package-name }}.tests - else - python -m pytest -n auto -ra --pyargs ${{ env.package-name }}.tests - fi - - - name: Run tensor tests - if: env.rerun-tests-on-failure != 'true' - run: | - python -m pytest -n auto -ra --pyargs dpnp.tests.tensor - - - name: Run tensor tests - if: env.rerun-tests-on-failure == 'true' - id: run_tests_tensor - uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 - with: - timeout_minutes: ${{ env.rerun-tests-timeout }} - max_attempts: ${{ env.rerun-tests-max-attempts }} - retry_on: any - shell: bash - command: | - . $CONDA/etc/profile.d/conda.sh - . $CONDA/etc/profile.d/mamba.sh - mamba activate ${{ env.test-env-name }} - - python -m pytest -n auto -ra --pyargs dpnp.tests.tensor + linux: + name: linux + uses: ./.github/workflows/conda-build-test.yml + with: + runs-on: ubuntu-latest + conda-subdir: linux-64 + channels-list: '-c conda-forge --override-channels' + recipe-dir: conda-recipe-cf + + windows: + name: windows + uses: ./.github/workflows/conda-build-test.yml + with: + runs-on: windows-2022 + conda-subdir: win-64 + channels-list: '-c conda-forge --override-channels' + recipe-dir: conda-recipe-cf From 6cd670edb66e0dfe4ea8a6e00bf297145a02bb93 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Fri, 21 Aug 2026 15:26:40 +0200 Subject: [PATCH 11/12] Fix bash path handling on Windows in reusable conda workflow github.workspace and $CONDA are backslash paths on Windows that bash mangles. Use workspace-relative paths for the channel/version/repo dirs and convert $CONDA via cygpath before sourcing conda in the retry steps. --- .github/workflows/conda-build-test.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/conda-build-test.yml b/.github/workflows/conda-build-test.yml index 0a447ec7ba6..12d67116dc3 100644 --- a/.github/workflows/conda-build-test.yml +++ b/.github/workflows/conda-build-test.yml @@ -133,11 +133,11 @@ jobs: # free-threaded dpctl is available on conda-forge env: - dpnp-repo-path: '${{ github.workspace }}/source/' + dpnp-repo-path: './source' create-conda-channel-env: 'source/environments/create_conda_channel.yml' - channel-path: '${{ github.workspace }}/channel/' - pkg-path-in-channel: '${{ github.workspace }}/channel/${{ inputs.conda-subdir }}/' - ver-json-path: '${{ github.workspace }}/version.json' + channel-path: './channel' + pkg-path-in-channel: './channel/${{ inputs.conda-subdir }}' + ver-json-path: './version.json' python-label: ${{ endsWith(matrix.python_spec, 't') && format('{0}t', matrix.python) || matrix.python }} python-install-spec: "python ${{ matrix.python_spec || format('{0}.*', matrix.python) }}" @@ -259,9 +259,9 @@ jobs: retry_on: any shell: bash command: | - . $CONDA/etc/profile.d/conda.sh - . $CONDA/etc/profile.d/mamba.sh - mamba activate ${{ env.test-env-name }} + CONDA_UNIX=$(cygpath -u "$CONDA" 2>/dev/null || echo "$CONDA") + . "$CONDA_UNIX/etc/profile.d/conda.sh" + conda activate ${{ env.test-env-name }} export SKIP_TENSOR_TESTS=1 if [[ "${{ env.python-label }}" == "${{ env.python-ver-test-all-dtypes }}" ]]; then @@ -286,8 +286,8 @@ jobs: retry_on: any shell: bash command: | - . $CONDA/etc/profile.d/conda.sh - . $CONDA/etc/profile.d/mamba.sh - mamba activate ${{ env.test-env-name }} + CONDA_UNIX=$(cygpath -u "$CONDA" 2>/dev/null || echo "$CONDA") + . "$CONDA_UNIX/etc/profile.d/conda.sh" + conda activate ${{ env.test-env-name }} python -m pytest -n auto -ra --pyargs dpnp.tests.tensor From 48985180d9dd37e87008dc9c65e88a7df745ebd4 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Fri, 21 Aug 2026 16:45:50 +0200 Subject: [PATCH 12/12] Use conda instead of mamba front-end in reusable conda workflow Under Git Bash on Windows the mamba wrapper mangles the -c ./channel argument ('hannel' is not recognized), while conda parses it correctly. Switch install/list to conda; keep use-mamba for the libmamba solver. --- .github/workflows/conda-build-test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/conda-build-test.yml b/.github/workflows/conda-build-test.yml index 12d67116dc3..ad45a225bb3 100644 --- a/.github/workflows/conda-build-test.yml +++ b/.github/workflows/conda-build-test.yml @@ -89,7 +89,7 @@ jobs: activate-environment: ${{ env.build-env-name }} - name: List installed packages - run: mamba list + run: conda list - name: Store conda paths as envs run: | @@ -202,7 +202,7 @@ jobs: id: install_dpnp continue-on-error: true run: | - mamba install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} pytest pytest-xdist "${{ env.python-install-spec }}" ${{ env.TEST_CHANNELS }} + conda install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} pytest pytest-xdist "${{ env.python-install-spec }}" ${{ env.TEST_CHANNELS }} env: TEST_CHANNELS: '-c ${{ env.channel-path }} ${{ inputs.channels-list }}' MAMBA_NO_LOW_SPEED_LIMIT: 1 @@ -210,13 +210,13 @@ jobs: - name: ReInstall dpnp if: steps.install_dpnp.outcome == 'failure' run: | - mamba install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} pytest pytest-xdist "${{ env.python-install-spec }}" ${{ env.TEST_CHANNELS }} + conda install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} pytest pytest-xdist "${{ env.python-install-spec }}" ${{ env.TEST_CHANNELS }} env: TEST_CHANNELS: '-c ${{ env.channel-path }} ${{ inputs.channels-list }}' MAMBA_NO_LOW_SPEED_LIMIT: 1 - name: List installed packages - run: mamba list + run: conda list - name: Register the OpenCL CPU driver with the ICD loader if: runner.os == 'Windows'