Skip to content

Commit 62945f6

Browse files
authored
Refactor dpnp includes and add extension example (#2941)
This PR proposes a refactor of dpnp's includes and the introduction of an example extension, to be tested to make sure examples using dpnp and dpnp.tensor can be written. The `usm_ndarray_constants.h` header and `dpnp4pybind11.hpp` are moved to `dpnp/include`, removing a relative include and aligning with previous dpctl behavior. Also introduces dpnp-config.cmake, which enables `find_package(Dpnp)` out of the box, which is installed with dpnp, and `--cmakedir` command line option, which leads to the CMake config.
1 parent 52a44b3 commit 62945f6

32 files changed

Lines changed: 586 additions & 17 deletions

.github/workflows/conda-package.yml

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,113 @@ jobs:
269269
270270
python -m pytest -n auto -ra --pyargs dpnp.tests.tensor
271271
272+
test_examples_linux:
273+
name: Test examples
274+
275+
needs: build
276+
277+
runs-on: ubuntu-latest
278+
timeout-minutes: 60
279+
280+
defaults:
281+
run:
282+
shell: bash -el {0}
283+
284+
strategy:
285+
matrix:
286+
python: ['3.14']
287+
288+
env:
289+
dpnp-repo-path: '${{ github.workspace }}/source/'
290+
examples-conda-pkg-env: 'source/environments/examples.yml'
291+
channel-path: '${{ github.workspace }}/channel/'
292+
pkg-path-in-channel: '${{ github.workspace }}/channel/linux-64/'
293+
ver-json-path: '${{ github.workspace }}/version.json'
294+
examples-env-name: 'examples_test'
295+
296+
steps:
297+
- name: Checkout DPNP repo
298+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
299+
with:
300+
fetch-depth: ${{ env.fetch-depth }}
301+
path: ${{ env.dpnp-repo-path }}
302+
303+
- name: Download artifact
304+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
305+
with:
306+
name: ${{ env.package-name }} ${{ runner.os }} Python ${{ matrix.python }}
307+
path: ${{ env.pkg-path-in-channel }}
308+
309+
- name: Setup miniconda
310+
id: setup_miniconda
311+
continue-on-error: true
312+
uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1
313+
with:
314+
miniforge-version: latest
315+
use-mamba: 'true'
316+
conda-remove-defaults: 'true'
317+
environment-file: ${{ env.examples-conda-pkg-env }}
318+
activate-environment: ${{ env.examples-env-name }}
319+
320+
- name: ReSetup miniconda
321+
if: steps.setup_miniconda.outcome == 'failure'
322+
uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1
323+
with:
324+
miniforge-version: latest
325+
use-mamba: 'true'
326+
conda-remove-defaults: 'true'
327+
environment-file: ${{ env.examples-conda-pkg-env }}
328+
activate-environment: ${{ env.examples-env-name }}
329+
330+
- name: Create conda channel
331+
run: |
332+
python -m conda_index ${{ env.channel-path }}
333+
334+
- name: Test conda channel
335+
run: |
336+
conda search ${{ env.package-name }} -c ${{ env.channel-path }} --override-channels --info --json > ${{ env.ver-json-path }}
337+
cat ${{ env.ver-json-path }}
338+
339+
- name: Get package version
340+
run: |
341+
PACKAGE_VERSION=$(python -c "${{ env.ver-script-part1 }} ${{ env.ver-script-part2 }}")
342+
echo "PACKAGE_VERSION=${PACKAGE_VERSION}" >> "$GITHUB_ENV"
343+
344+
- name: Install dpnp
345+
id: install_dpnp
346+
continue-on-error: true
347+
run: |
348+
mamba install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} python=${{ matrix.python }} ${{ env.TEST_CHANNELS }}
349+
env:
350+
TEST_CHANNELS: '-c ${{ env.channel-path }} ${{ env.channels-list }}'
351+
352+
- name: ReInstall dpnp
353+
if: steps.install_dpnp.outcome == 'failure'
354+
run: |
355+
mamba install ${{ env.package-name }}=${{ env.PACKAGE_VERSION }} python=${{ matrix.python }} ${{ env.TEST_CHANNELS }}
356+
env:
357+
TEST_CHANNELS: '-c ${{ env.channel-path }} ${{ env.channels-list }}'
358+
359+
- name: List installed packages
360+
run: mamba list
361+
362+
- name: Smoke test
363+
run: |
364+
python -c "import dpctl; dpctl.lsplatform()"
365+
python -c "import dpnp; print(dpnp.__version__)"
366+
367+
- name: Build and run pybind11 examples
368+
run: |
369+
cd ${{ env.dpnp-repo-path }}/examples/pybind11
370+
for d in */; do
371+
pushd "$d" > /dev/null
372+
CC=icx CXX=icpx python setup.py build_ext --inplace -G Ninja || exit 1
373+
if [ -d tests ]; then
374+
python -m pytest tests || exit 1
375+
fi
376+
popd > /dev/null
377+
done
378+
272379
test_windows:
273380
name: Test
274381

@@ -451,7 +558,7 @@ jobs:
451558
upload:
452559
name: Upload
453560

454-
needs: [test_linux, test_windows]
561+
needs: [test_linux, test_windows, test_examples_linux]
455562

456563
strategy:
457564
fail-fast: false

.gitignore

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
_skbuild
33
build_cython
44
cython_debug
5-
dpnp.egg-info
5+
*.egg-info
66

77
# Byte-compiled / optimized / DLL files
88
__pycache__/
@@ -27,9 +27,12 @@ dpnp_pytest.*
2727
# Build examples
2828
example3
2929

30+
# moved cmake scripts
31+
dpnp/resources/cmake
32+
3033
*dpnp_backend*
3134
dpnp/include/dpnp/tensor/*.h
32-
dpnp/**/*.cpython*.so
3335
dpnp/**/*.pyd
3436
*~
3537
core
38+
*.so

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This release is compatible with NumPy 2.5.
1313
* Added C API functions for `dpnp.tensor.usm_ndarray` setters and getters to avoid ABI breakage if `dpnp.tensor.usm_ndarray` is modified [#2866](https://github.com/IntelPython/dpnp/pull/2866)
1414
* Added support for buffer protocol objects as advanced index keys in `dpnp.ndarray` [#2889](https://github.com/IntelPython/dpnp/pull/2889)
1515
* Added `--includes` and `--include-dir` options to the `dpnp` CLI [#2916](https://github.com/IntelPython/dpnp/pull/2916)
16+
* Added `dpnp-config.cmake` to make `find_package(Dpnp)` work out of the box, and an example which uses it [#2941](https://github.com/IntelPython/dpnp/pull/2941)
1617

1718
### Changed
1819

CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,19 @@ else()
327327
message(FATAL_ERROR "Unsupported system.")
328328
endif()
329329

330+
# install dpnp-config.cmake for find_package() support
331+
foreach(_dest IN ITEMS dpnp/resources/cmake lib/cmake/dpnp)
332+
install(FILES ${CMAKE_SOURCE_DIR}/cmake/dpnp-config.cmake DESTINATION ${_dest})
333+
endforeach()
334+
335+
# install dpnp4pybind11.hpp and usm_ndarray_constants.h into include folder
336+
install(
337+
FILES
338+
${CMAKE_SOURCE_DIR}/dpnp/include/dpnp4pybind11.hpp
339+
${CMAKE_SOURCE_DIR}/dpnp/include/usm_ndarray_constants.h
340+
DESTINATION dpnp/include
341+
)
342+
330343
# Define flags for CMAKE_BUILD_TYPE=Coverage
331344
set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_DEBUG} -O1 -g1 -DDEBUG")
332345
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_DEBUG} -O1 -g1 -DDEBUG")

cmake/dpnp-config.cmake

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#.rst:
2+
#
3+
# Find the include directory for ``dpnp4pybind11.hpp`` and dpnp tensor kernels.
4+
#
5+
# This module sets the following variables:
6+
#
7+
# ``Dpnp_FOUND``
8+
# True if DPNP was found.
9+
# ``Dpnp_INCLUDE_DIR``
10+
# The include directory needed to use dpnp.
11+
# ``Dpnp_TENSOR_INCLUDE_DIR``
12+
# The include directory for tensor kernels implementation.
13+
# ``Dpnp_VERSION``
14+
# The version of dpnp found.
15+
#
16+
# The module will also explicitly define two cache variables:
17+
#
18+
# ``Dpnp_INCLUDE_DIR``
19+
# ``Dpnp_TENSOR_INCLUDE_DIR``
20+
#
21+
22+
if(NOT Dpnp_FOUND)
23+
find_package(Python 3.10 REQUIRED COMPONENTS Interpreter Development.Module)
24+
25+
if(Python_EXECUTABLE)
26+
execute_process(
27+
COMMAND "${Python_EXECUTABLE}" -m dpnp --include-dir
28+
OUTPUT_VARIABLE _dpnp_include_dir
29+
OUTPUT_STRIP_TRAILING_WHITESPACE
30+
ERROR_QUIET
31+
)
32+
execute_process(
33+
COMMAND "${Python_EXECUTABLE}" -c "import dpnp; print(dpnp.__version__)"
34+
OUTPUT_VARIABLE Dpnp_VERSION
35+
OUTPUT_STRIP_TRAILING_WHITESPACE
36+
ERROR_QUIET
37+
)
38+
endif()
39+
endif()
40+
41+
find_path(
42+
Dpnp_INCLUDE_DIR
43+
dpnp4pybind11.hpp
44+
PATHS "${_dpnp_include_dir}" "${Python_INCLUDE_DIRS}"
45+
PATH_SUFFIXES dpnp/include
46+
NO_DEFAULT_PATH
47+
)
48+
get_filename_component(_dpnp_dir "${Dpnp_INCLUDE_DIR}" DIRECTORY)
49+
50+
find_path(
51+
Dpnp_TENSOR_INCLUDE_DIR
52+
kernels
53+
PATHS "${_dpnp_dir}/tensor/libtensor/include"
54+
NO_DEFAULT_PATH
55+
)
56+
57+
set(Dpnp_INCLUDE_DIRS ${Dpnp_INCLUDE_DIR})
58+
59+
# handle the QUIETLY and REQUIRED arguments and set Dpnp_FOUND to TRUE if
60+
# all listed variables are TRUE
61+
include(FindPackageHandleStandardArgs)
62+
find_package_handle_standard_args(
63+
Dpnp
64+
REQUIRED_VARS Dpnp_INCLUDE_DIR Dpnp_TENSOR_INCLUDE_DIR
65+
VERSION_VAR Dpnp_VERSION
66+
)
67+
68+
mark_as_advanced(Dpnp_INCLUDE_DIR)
69+
mark_as_advanced(Dpnp_TENSOR_INCLUDE_DIR)

dpnp/__main__.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def _dpnp_dir() -> str:
4141

4242
def get_include_dir() -> str:
4343
"""Returns path to dpnp include directory containing dpnp4pybind11.hpp"""
44-
return os.path.join(_dpnp_dir(), "backend", "include")
44+
return os.path.join(_dpnp_dir(), "include")
4545

4646

4747
def print_include_flags() -> None:
@@ -61,6 +61,13 @@ def print_tensor_include_flags() -> None:
6161
print("-I " + libtensor_dir)
6262

6363

64+
def print_cmake_dir() -> None:
65+
"""Prints directory with dpnp-config.cmake"""
66+
dpnp_dir = _dpnp_dir()
67+
cmake_dir = os.path.join(dpnp_dir, "resources", "cmake")
68+
print(cmake_dir)
69+
70+
6471
def main() -> None:
6572
"""Main entry-point."""
6673
parser = argparse.ArgumentParser()
@@ -84,6 +91,11 @@ def main() -> None:
8491
action="store_true",
8592
help="Path to dpnp libtensor include directory.",
8693
)
94+
parser.add_argument(
95+
"--cmakedir",
96+
action="store_true",
97+
help="CMake module directory, ideal for setting -DDpnp_ROOT in CMake.",
98+
)
8799
args = parser.parse_args()
88100
if not sys.argv[1:]:
89101
parser.print_help()
@@ -95,6 +107,8 @@ def main() -> None:
95107
print_tensor_include_flags()
96108
if args.tensor_include_dir:
97109
print(get_tensor_include_dir())
110+
if args.cmakedir:
111+
print_cmake_dir()
98112

99113

100114
if __name__ == "__main__":

dpnp/backend/extensions/blas/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ target_include_directories(
7070
${python_module_name}
7171
PRIVATE
7272
${CMAKE_CURRENT_SOURCE_DIR}/../common
73-
${CMAKE_SOURCE_DIR}/dpnp/backend/include
73+
${CMAKE_SOURCE_DIR}/dpnp/include
7474
${CMAKE_SOURCE_DIR}/dpnp/tensor/libtensor/include
7575
)
7676

dpnp/backend/extensions/blas/dot_common.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232

3333
#include <pybind11/pybind11.h>
3434

35+
#include "dpnp4pybind11.hpp"
36+
3537
// dpnp tensor headers
3638
#include "utils/memory_overlap.hpp"
3739
#include "utils/output_validation.hpp"

dpnp/backend/extensions/fft/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ set_target_properties(
6363
target_include_directories(
6464
${python_module_name}
6565
PRIVATE
66-
${CMAKE_SOURCE_DIR}/dpnp/backend/include
66+
${CMAKE_SOURCE_DIR}/dpnp/include
6767
${CMAKE_SOURCE_DIR}/dpnp/tensor/libtensor/include
6868
)
6969

dpnp/backend/extensions/indexing/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ target_include_directories(
6868
PRIVATE
6969
${CMAKE_CURRENT_SOURCE_DIR}/../../
7070
${CMAKE_CURRENT_SOURCE_DIR}/../common
71-
${CMAKE_SOURCE_DIR}/dpnp/backend/include
71+
${CMAKE_SOURCE_DIR}/dpnp/include
7272
${CMAKE_SOURCE_DIR}/dpnp/tensor/libtensor/include
7373
)
7474

0 commit comments

Comments
 (0)