diff --git a/.flake8 b/.flake8 index 36cf71be..84cfe007 100644 --- a/.flake8 +++ b/.flake8 @@ -12,6 +12,8 @@ extend-ignore = D102, # missing docstring in public function: D103, + # missing docstring in public package: + D104, # missing docstring in __init__: D107, # 1 blank line required between summary line and description: @@ -26,7 +28,9 @@ per-file-ignores = mkl_umath/ufunc_docstrings_numpy1.py: E501 mkl_umath/ufunc_docstrings_numpy2.py: E501 -exclude = _vendored/conv_template.py +exclude = + _vendored/conv_template.py + _vendored/process_src_template.py filename = *.py, *.pyx, *.pxi, *.pxd max_line_length = 80 diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index f58872f6..a5ec6fc8 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -5,3 +5,6 @@ # added more linters 3a3df3fae1574558973e3829368dc8a7b9c76ad9 + +# fix linting in conda-package-cf +f94f488ce98e06e5ffab1ce02ecd525b1680726c diff --git a/.github/workflows/build-with-clang.yml b/.github/workflows/build-with-clang.yml index 251747da..06f9af8e 100644 --- a/.github/workflows/build-with-clang.yml +++ b/.github/workflows/build-with-clang.yml @@ -8,7 +8,7 @@ on: permissions: read-all jobs: - build-with-clang: + build-with-intel-clang: runs-on: ubuntu-latest strategy: @@ -56,7 +56,7 @@ jobs: - name: Install mkl_umath dependencies run: | - pip install scikit-build cmake ninja cython setuptools">=77" + pip install meson-python ninja cython mkl-service pip install ${{ matrix.numpy_version }} - name: List oneAPI folder content @@ -67,7 +67,6 @@ jobs: source ${{ env.ONEAPI_ROOT }}/setvars.sh echo "$CMPLR_ROOT" export CC="$CMPLR_ROOT/bin/icx" - export CFLAGS="${CFLAGS} -fno-fast-math -O2" pip install . --no-build-isolation --no-deps --verbose - name: Run mkl_umath tests @@ -77,4 +76,4 @@ jobs: # mkl_umath cannot be installed in editable mode, we need # to change directory before importing it and running tests cd .. - python -m pytest -sv --pyargs mkl_umath/mkl_umath/tests + python -m pytest -sv --pyargs mkl_umath diff --git a/.github/workflows/build-with-standard-clang.yml b/.github/workflows/build-with-standard-clang.yml new file mode 100644 index 00000000..72998c0e --- /dev/null +++ b/.github/workflows/build-with-standard-clang.yml @@ -0,0 +1,64 @@ +name: Build project with standard clang compiler + +on: + pull_request: + push: + branches: [master] + +permissions: read-all + +jobs: + build-with-standard-clang: + runs-on: ubuntu-latest + + strategy: + matrix: + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + numpy_version: ["numpy'>=2'"] + + env: + COMPILER_ROOT: /usr/bin + + defaults: + run: + shell: bash -el {0} + + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@3155a141048f8f89c06b4cdae32e7853e97536bc # 0.13.0 + with: + access_token: ${{ github.token }} + + - name: Install Dependencies + run: | + sudo apt-get update && sudo apt-get install -y clang + + - name: Setup Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: ${{ matrix.python }} + architecture: x64 + + - name: Checkout repo + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Install mkl_umath dependencies + run: | + pip install meson-python ninja cython mkl-service + pip install mkl-devel tbb-devel + pip install ${{ matrix.numpy_version }} + + - name: Build mkl_umath + run: | + export CC=${{ env.COMPILER_ROOT }}/clang + pip install . --no-build-isolation --no-deps --verbose + + - name: Run mkl_umath tests + run: | + pip install pytest + # mkl_umath cannot be installed in editable mode, we need + # to change directory before importing it and running tests + cd .. + python -m pytest -sv --pyargs mkl_umath diff --git a/.github/workflows/build_pip.yaml b/.github/workflows/build_pip.yaml index 511a54cf..9e170066 100644 --- a/.github/workflows/build_pip.yaml +++ b/.github/workflows/build_pip.yaml @@ -48,14 +48,12 @@ jobs: - name: Build conda package run: | - pip install --no-cache-dir scikit-build cmake ninja cython + pip install --no-cache-dir meson-python ninja cython pip install --no-cache-dir numpy ${{ matrix.use_pre }} - echo "CONDA_PREFFIX is '${CONDA_PREFIX}'" - export MKLROOT=${CONDA_PREFIX} CC=icx pip install . --no-build-isolation --no-deps --verbose pip install --no-cache-dir pytest pip list # mkl_umath cannot be installed in editable mode, we need # to change directory before importing it and running tests cd .. - python -m pytest -v mkl_umath/mkl_umath/tests + python -m pytest -sv --pyargs mkl_umath diff --git a/.github/workflows/conda-package-cf.yml b/.github/workflows/conda-package-cf.yml new file mode 100644 index 00000000..2e080c74 --- /dev/null +++ b/.github/workflows/conda-package-cf.yml @@ -0,0 +1,358 @@ +name: Conda package with conda-forge channel only + +on: + push: + branches: + - master + pull_request: + +permissions: read-all + +env: + PACKAGE_NAME: mkl_umath + MODULE_NAME: mkl_umath + TEST_ENV_NAME: test_mkl_umath + VER_SCRIPT1: "import json; f = open('ver.json', 'r'); j = json.load(f); f.close(); d = j['mkl_umath'][0];" + VER_SCRIPT2: "print('='.join((d[s] for s in ('version', 'build'))))" + +jobs: + build_linux: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - python: "3.10" + numpy: "2.2" + - python: "3.11" + numpy: "2.3" + - python: "3.12" + numpy: "2.3" + - python: "3.13" + numpy: "2.3" + - python: "3.14" + numpy: "2.3" + + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@3155a141048f8f89c06b4cdae32e7853e97536bc # 0.13.0 + with: + access_token: ${{ github.token }} + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Set pkgs_dirs + run: | + echo "pkgs_dirs: [~/.conda/pkgs]" >> ~/.condarc + + - name: Cache conda packages + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + env: + CACHE_NUMBER: 0 # Increase to reset cache + with: + path: ~/.conda/pkgs + key: + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}-${{hashFiles('**/meta.yaml') }} + restore-keys: | + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}- + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}- + + - name: Add conda to system path + run: echo "$CONDA/bin" >> "$GITHUB_PATH" + + - name: Install conda-build + run: conda install conda-build + + - name: Build conda package with NumPy 2.x + run: | + CHANNELS=(-c conda-forge --override-channels) + VERSIONS=(--python "${{ matrix.python }}" --numpy "${{ matrix.numpy }}") + TEST=(--no-test) + echo "CONDA_BLD=${CONDA}/conda-bld/linux-64" >> "$GITHUB_ENV" + + conda build \ + "${TEST[@]}" \ + "${VERSIONS[@]}" \ + "${CHANNELS[@]}" \ + conda-recipe-cf + + - name: Upload artifact + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Python ${{ matrix.python }} + path: ${{ env.CONDA_BLD }}/${{ env.PACKAGE_NAME }}-*.conda + + test_linux: + needs: build_linux + runs-on: ${{ matrix.runner }} + + strategy: + matrix: + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + numpy: ['numpy">2"'] + experimental: [false] + runner: [ubuntu-latest] + continue-on-error: ${{ matrix.experimental }} + env: + CHANNELS: -c conda-forge --override-channels + + steps: + - name: Download artifact + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + with: + name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Python ${{ matrix.python }} + - name: Add conda to system path + run: echo "$CONDA/bin" >> "$GITHUB_PATH" + - name: Install conda-build + run: conda install conda-build + - name: Create conda channel + run: | + mkdir -p "$GITHUB_WORKSPACE/channel/linux-64" + mv "${PACKAGE_NAME}"-*.conda "$GITHUB_WORKSPACE/channel/linux-64" + conda index "$GITHUB_WORKSPACE/channel" + # Test channel + conda search "$PACKAGE_NAME" -c "$GITHUB_WORKSPACE/channel" --override-channels + + - name: Collect dependencies + run: | + CHANNELS=(-c "$GITHUB_WORKSPACE/channel" -c "conda-forge" --override-channels) + conda create -n "${{ env.TEST_ENV_NAME }}" "$PACKAGE_NAME" "python=${{ matrix.python }}" "${CHANNELS[@]}" --only-deps --dry-run > lockfile + - name: Display lockfile + run: cat lockfile + + - name: Set pkgs_dirs + run: | + echo "pkgs_dirs: [~/.conda/pkgs]" >> ~/.condarc + + - name: Cache conda packages + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + env: + CACHE_NUMBER: 0 # Increase to reset cache + with: + path: ~/.conda/pkgs + key: + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}-${{hashFiles('lockfile') }} + restore-keys: | + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}- + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}- + + - name: Install mkl_umath + run: | + CHANNELS=(-c "$GITHUB_WORKSPACE/channel" -c "conda-forge" --override-channels) + conda create -n "${{ env.TEST_ENV_NAME }}" "python=${{ matrix.python }}" "$PACKAGE_NAME" pytest "${CHANNELS[@]}" + # Test installed packages + conda list -n "${{ env.TEST_ENV_NAME }}" + + - name: Smoke test + run: | + source "$CONDA/etc/profile.d/conda.sh" + conda activate "${{ env.TEST_ENV_NAME }}" + python -c "import mkl_umath, numpy as np; mkl_umath.use_in_numpy(); np.sin(np.linspace(0, 1, num=10**6));" + + - name: Run tests + run: | + source "$CONDA/etc/profile.d/conda.sh" + conda activate "${{ env.TEST_ENV_NAME }}" + pytest -v --pyargs ${{ env.MODULE_NAME }} + + build_windows: + runs-on: windows-latest + + strategy: + matrix: + include: + - python: "3.10" + numpy: "2.2" + - python: "3.11" + numpy: "2.3" + - python: "3.12" + numpy: "2.3" + - python: "3.13" + numpy: "2.3" + - python: "3.14" + numpy: "2.3" + + env: + conda-bld: C:\Miniconda\conda-bld\win-64\ + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@3155a141048f8f89c06b4cdae32e7853e97536bc # 0.13.0 + with: + access_token: ${{ github.token }} + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 # v3.3.0 + with: + miniforge-variant: Miniforge3 + miniforge-version: latest + auto-activate: true + activate-environment: base + channels: conda-forge + conda-remove-defaults: true + python-version: ${{ matrix.python }} + + - name: Cache conda packages + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + env: + CACHE_NUMBER: 3 # Increase to reset cache + with: + path: /home/runner/conda_pkgs_dir + key: + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}-${{hashFiles('**/meta.yaml') }} + restore-keys: | + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}- + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}- + + - name: Store conda paths as envs + shell: bash -l {0} + run: | + echo "CONDA_BLD=$CONDA/conda-bld/win-64/" | tr "\\\\" '/' >> "$GITHUB_ENV" + + - name: Install conda build + run: | + conda activate + conda install -y conda-build + conda list -n base + + - name: Build conda package + run: | + conda activate + conda build --no-test --python ${{ matrix.python }} --numpy ${{ matrix.numpy }} -c conda-forge --override-channels conda-recipe-cf + + - name: Upload artifact + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Python ${{ matrix.python }} + path: ${{ env.CONDA_BLD }}${{ env.PACKAGE_NAME }}-*.conda + + test_windows: + needs: build_windows + runs-on: ${{ matrix.runner }} + defaults: + run: + shell: cmd /C CALL {0} + strategy: + matrix: + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + numpy: ['numpy">2"'] + experimental: [false] + runner: [windows-latest] + continue-on-error: ${{ matrix.experimental }} + env: + workdir: '${{ github.workspace }}' + CHANNELS: -c conda-forge --override-channels + + steps: + - name: Download artifact + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + with: + name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Python ${{ matrix.python }} + + - uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 # v3.3.0 + with: + miniforge-version: latest + channels: conda-forge + conda-remove-defaults: true + activate-environment: ${{ env.TEST_ENV_NAME }} + python-version: ${{ matrix.python }} + + - name: Install conda-index + run: | + conda install -n base conda-index + + - name: Create conda channel with the artifact bit + shell: cmd /C CALL {0} + run: | + echo ${{ env.workdir }} + mkdir ${{ env.workdir }}\channel + mkdir ${{ env.workdir }}\channel\win-64 + move ${{ env.PACKAGE_NAME }}-*.conda ${{ env.workdir }}\channel\win-64 + dir ${{ env.workdir }}\channel\win-64 + + - name: Index the channel + shell: cmd /C CALL {0} + run: | + conda index ${{ env.workdir }}\channel + + - name: Dump mkl_umath version info from created channel to STDOUT + shell: cmd /C CALL {0} + run: | + conda search ${{ env.PACKAGE_NAME }} -c ${{ env.workdir }}/channel --override-channels --info --json + + - name: Dump mkl_umath version info from created channel into ver.json + shell: cmd /C CALL {0} + run: | + conda search ${{ env.PACKAGE_NAME }} -c ${{ env.workdir }}/channel --override-channels --info --json > ${{ env.workdir }}\ver.json + + - name: Output content of workdir + shell: pwsh + run: Get-ChildItem -Path ${{ env.workdir }} + + - name: Output content of produced ver.json + shell: pwsh + run: Get-Content -Path ${{ env.workdir }}\ver.json + + - name: Collect dependencies + shell: cmd /C CALL {0} + run: | + @ECHO ON + IF NOT EXIST ver.json ( + copy /Y ${{ env.workdir }}\ver.json . + ) + SET "SCRIPT=%VER_SCRIPT1% %VER_SCRIPT2%" + FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO ( + SET PACKAGE_VERSION=%%F + ) + conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} --only-deps --dry-run > lockfile + + - name: Display lockfile content + shell: pwsh + run: Get-Content -Path .\lockfile + + - name: Cache conda packages + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + env: + CACHE_NUMBER: 0 # Increase to reset cache + with: + path: /home/runner/conda_pkgs_dir + key: + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}-${{hashFiles('lockfile') }} + restore-keys: | + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}- + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}- + + - name: Install mkl_umath + shell: cmd /C CALL {0} + run: | + @ECHO ON + IF NOT EXIST ver.json ( + copy /Y ${{ env.workdir }}\ver.json . + ) + set "SCRIPT=%VER_SCRIPT1% %VER_SCRIPT2%" + FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO ( + SET PACKAGE_VERSION=%%F + ) + conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% pytest python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} + + - name: Report content of test environment + shell: cmd /C CALL {0} + run: | + conda activate + echo "Value of CONDA environment variable was: " %CONDA% + echo "Value of CONDA_PREFIX environment variable was: " %CONDA_PREFIX% + conda info && conda list -n ${{ env.TEST_ENV_NAME }} + + - name: Smoke test + shell: cmd /C CALL {0} + run: | + @ECHO ON + conda activate ${{ env.TEST_ENV_NAME }} + python -c "import mkl_umath, numpy as np; mkl_umath.use_in_numpy(); np.sin(np.linspace(0, 1, num=10**6));" + + - name: Run tests + shell: cmd /C CALL {0} + run: | + conda activate ${{ env.TEST_ENV_NAME }} + python -m pytest -v -s --pyargs ${{ env.MODULE_NAME }} diff --git a/.github/workflows/conda-package.yml b/.github/workflows/conda-package.yml index 20b6c3ce..ad6d5fb7 100644 --- a/.github/workflows/conda-package.yml +++ b/.github/workflows/conda-package.yml @@ -10,6 +10,8 @@ permissions: read-all env: PACKAGE_NAME: mkl_umath + MODULE_NAME: mkl_umath + TEST_ENV_NAME: test_mkl_umath VER_SCRIPT1: "import json; f = open('ver.json', 'r'); j = json.load(f); f.close(); d = j['mkl_umath'][0];" VER_SCRIPT2: "print('='.join((d[s] for s in ('version', 'build'))))" @@ -102,7 +104,7 @@ jobs: - name: Collect dependencies run: | CHANNELS=(-c "$GITHUB_WORKSPACE/channel" -c "https://software.repos.intel.com/python/conda" -c "conda-forge" --override-channels) - conda create -n test_mkl_umath "$PACKAGE_NAME" "python=${{ matrix.python }}" "${CHANNELS[@]}" --only-deps --dry-run > lockfile + conda create -n "${{ env.TEST_ENV_NAME }}" "$PACKAGE_NAME" "python=${{ matrix.python }}" "${CHANNELS[@]}" --only-deps --dry-run > lockfile - name: Display lockfile run: cat lockfile @@ -125,20 +127,20 @@ jobs: - name: Install mkl_umath run: | CHANNELS=(-c "$GITHUB_WORKSPACE/channel" -c "https://software.repos.intel.com/python/conda" -c "conda-forge" --override-channels) - conda create -n test_mkl_umath "python=${{ matrix.python }}" "$PACKAGE_NAME" pytest "${CHANNELS[@]}" + conda create -n "${{ env.TEST_ENV_NAME }}" "python=${{ matrix.python }}" "$PACKAGE_NAME" pytest "${CHANNELS[@]}" # Test installed packages - conda list -n test_mkl_umath + conda list -n "${{ env.TEST_ENV_NAME }}" - name: Smoke test run: | source "$CONDA/etc/profile.d/conda.sh" - conda activate test_mkl_umath + conda activate "${{ env.TEST_ENV_NAME }}" python -c "import mkl_umath, numpy as np; mkl_umath.use_in_numpy(); np.sin(np.linspace(0, 1, num=10**6));" - name: Run tests run: | source "$CONDA/etc/profile.d/conda.sh" - conda activate test_mkl_umath + conda activate "${{ env.TEST_ENV_NAME }}" pytest -v --pyargs ${{ env.PACKAGE_NAME }} build_windows: @@ -162,9 +164,10 @@ jobs: with: miniforge-variant: Miniforge3 miniforge-version: latest - activate-environment: build + auto-activate: true + activate-environment: base channels: conda-forge - conda-remove-defaults: "true" + conda-remove-defaults: true python-version: ${{ matrix.python }} - name: Cache conda packages @@ -225,11 +228,10 @@ jobs: - uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 # v3.3.0 with: - miniforge-variant: Miniforge3 miniforge-version: latest - activate-environment: mkl_umath_test channels: conda-forge - conda-remove-defaults: "true" + conda-remove-defaults: true + activate-environment: ${{ env.TEST_ENV_NAME }} python-version: ${{ matrix.python }} - name: Install conda-index @@ -279,7 +281,7 @@ jobs: FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO ( SET PACKAGE_VERSION=%%F ) - conda install -n mkl_umath_test ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} --only-deps --dry-run > lockfile + conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} --only-deps --dry-run > lockfile - name: Display lockfile content shell: pwsh @@ -313,7 +315,7 @@ jobs: SET "TEST_DEPENDENCIES=pytest pytest-cov" SET "WORKAROUND_DEPENDENCIES=intel-openmp" SET "DEPENDENCIES=%TEST_DEPENDENCIES% %WORKAROUND_DEPENDENCIES%" - conda install -n mkl_umath_test ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %DEPENDENCIES% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} + conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %DEPENDENCIES% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} - name: Report content of test environment shell: cmd /C CALL {0} @@ -321,17 +323,17 @@ jobs: conda activate echo "Value of CONDA environment variable was: " %CONDA% echo "Value of CONDA_PREFIX environment variable was: " %CONDA_PREFIX% - conda info && conda list -n mkl_umath_test + conda info && conda list -n ${{ env.TEST_ENV_NAME }} - name: Smoke test shell: cmd /C CALL {0} run: | @ECHO ON - conda activate mkl_umath_test + conda activate ${{ env.TEST_ENV_NAME }} python -c "import mkl_umath, numpy as np; mkl_umath.use_in_numpy(); np.sin(np.linspace(0, 1, num=10**6));" - name: Run tests shell: cmd /C CALL {0} run: | - conda activate mkl_umath_test - python -m pytest -v -s --pyargs ${{ env.PACKAGE_NAME }} + conda activate ${{ env.TEST_ENV_NAME }} + python -m pytest -v -s --pyargs ${{ env.MODULE_NAME }} diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 4a00e86f..00000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,147 +0,0 @@ -cmake_minimum_required(VERSION 3.27...3.28 FATAL_ERROR) - -cmake_policy(SET CMP0135 NEW) - -project(mkl_umath - LANGUAGES C - DESCRIPTION "mkl_umath module" -) - -option(OPTIMIZATION_REPORT - "Whether to generate optimization vectorization report" - OFF -) - -find_package(Python COMPONENTS Interpreter Development NumPy REQUIRED) - -# Print out the discovered paths -include(CMakePrintHelpers) -cmake_print_variables(Python_INCLUDE_DIRS) -cmake_print_variables(Python_LIBRARIES) -cmake_print_variables(Python_NumPy_INCLUDE_DIRS) - -set(CYTHON_FLAGS "-t -w \"${CMAKE_SOURCE_DIR}\"") -find_package(Cython REQUIRED) - -find_package(TBB REQUIRED) -set(MKL_ARCH "intel64") -set(MKL_LINK "dynamic") -set(MKL_THREADING "tbb_thread") -set(MKL_INTERFACE "lp64") -find_package(MKL REQUIRED) - -if(WIN32) - string(CONCAT WARNING_FLAGS - "-Wall " - "-Wextra " - "-Winit-self " - "-Wunused-function " - "-Wuninitialized " - "-Wmissing-declarations " - "-Wstrict-prototypes " - "-Wno-unused-parameter " - "-Wno-implicit-function-declaration " - ) - string(CONCAT SDL_FLAGS - "/GS " - "/DynamicBase " - ) - string(CONCAT PRECISION_FLAGS - "/fp:precise " - "/Qimf-precision=high " - "/Qprotect-parens " - ) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox ${WARNING_FLAGS} ${SDL_FLAGS} ${PRECISION_FLAGS}") - set(CMAKE_C_FLAGS_DEBUG - "${CMAKE_C_FLAGS_DEBUG} ${WARNING_FLAGS} ${SDL_FLAGS} /fp:precise -O0 -g1 -DDEBUG" - ) - set(MKL_UMATH_LINKER_OPTIONS "LINKER:/NXCompat;LINKER:/DynamicBase") -elseif(UNIX) - string(CONCAT WARNING_FLAGS - "-Wall " - "-Wextra " - "-Winit-self " - "-Wunused-function " - "-Wuninitialized " - "-Wmissing-declarations " - "-Wstrict-prototypes " - "-Wno-unused-parameter " - "-fdiagnostics-color=auto " - ) - string(CONCAT SDL_FLAGS - "-fstack-protector " - "-fstack-protector-all " - "-fpic " - "-fPIC " - "-D_FORTIFY_SOURCE=2 " - "-Wformat " - "-Wformat-security " -# "-fno-strict-overflow " # no-strict-overflow is implied by -fwrapv - "-fno-delete-null-pointer-checks " - "-fwrapv " - ) - string(CONCAT CFLAGS - "${WARNING_FLAGS}" - "${SDL_FLAGS}" - ) - string(CONCAT PRECISION_FLAGS - "-fprotect-parens " - "-fimf-precision=high " - "-fno-fast-math " - ) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 ${CFLAGS} ${PRECISION_FLAGS}") - set(CMAKE_C_FLAGS_DEBUG - "${CMAKE_C_FLAGS_DEBUG} ${CFLAGS} -fno-fast-math -O0 -g1 -DDEBUG" - ) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-incompatible-function-pointer-types ${CFLAGS}") - set(MKL_UMATH_LINKER_OPTIONS "LINKER:-z,noexecstack,-z,relro,-z,now") -else() - message(FATAL_ERROR "Unsupported system.") -endif() - -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) -# set_property(GLOBAL PROPERTY GLOBAL_DEPENDS_DEBUG_MODE 1) -set(_linker_options ${MKL_UMATH_LINKER_OPTIONS}) - -set(_trgt mkl_umath_loops) -add_library(${_trgt} SHARED mkl_umath/src/mkl_umath_loops.c) -set_target_properties(${_trgt} PROPERTIES - CMAKE_POSITION_INDEPENDENT_CODE ON - C_STANDARD 99 -) -target_include_directories(${_trgt} PUBLIC mkl_umath/src/ ${Python_NumPy_INCLUDE_DIRS} ${Python_INCLUDE_DIRS}) -target_link_libraries(${_trgt} PUBLIC MKL::MKL ${Python_LIBRARIES}) -target_link_options(${_trgt} PUBLIC ${_linker_options}) -target_compile_options(${_trgt} PUBLIC -fveclib=SVML) -target_compile_options(${_trgt} PUBLIC -fvectorize) -if(OPTIMIZATION_REPORT) - target_compile_options(${_trgt} PRIVATE -qopt-report=3) -endif() -install(TARGETS ${_trgt} - LIBRARY DESTINATION mkl_umath - ARCHIVE DESTINATION mkl_umath - RUNTIME DESTINATION mkl_umath -) - -python_add_library(_ufuncs MODULE WITH_SOABI "mkl_umath/src/ufuncsmodule.c" "mkl_umath/src/__umath_generated.c") -target_include_directories(_ufuncs PRIVATE "mkl_umath/src" ${Python_NumPy_INCLUDE_DIRS} ${MKL_INCLUDE_DIR}) -target_compile_definitions(_ufuncs PUBLIC NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION) -target_link_options(_ufuncs PRIVATE ${_linker_options}) -target_link_libraries(_ufuncs PRIVATE mkl_umath_loops) -set_target_properties(_ufuncs PROPERTIES C_STANDARD 99) -if (UNIX) - set_target_properties(_ufuncs PROPERTIES INSTALL_RPATH "$ORIGIN/../..;$ORIGIN/../../..;$ORIGIN") -endif() -install(TARGETS _ufuncs LIBRARY DESTINATION mkl_umath) - -add_cython_target(_patch "mkl_umath/src/_patch.pyx" C OUTPUT_VAR _generated_src) -Python_add_library(_patch MODULE WITH_SOABI ${_generated_src}) -target_include_directories(_patch PRIVATE "mkl_umath/src/" ${Python_NumPy_INCLUDE_DIRS} ${Python_INCLUDE_DIRS}) -target_compile_definitions(_patch PUBLIC NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION) -target_link_libraries(_patch PRIVATE mkl_umath_loops) -set_target_properties(_patch PROPERTIES C_STANDARD 99) -if (UNIX) - set_target_properties(_patch PROPERTIES INSTALL_RPATH "$ORIGIN/../..;$ORIGIN/../../..;$ORIGIN") -endif() -install(TARGETS _patch LIBRARY DESTINATION mkl_umath) diff --git a/_vendored/README.md b/_vendored/README.md index b3f3a919..42ec7946 100644 --- a/_vendored/README.md +++ b/_vendored/README.md @@ -1,5 +1,3 @@ ## Vendored files -File `conv_template.py` is copied from NumPy's numpy/distutils folder, since -`numpy.distutils` is absent from the installation layout starting with -Python 3.12 +Files `conv_template.py` and `process_src_template.py` are copied from NumPy's numpy/numpy/_build_utils folder diff --git a/_vendored/conv_template.py b/_vendored/conv_template.py index c8933d1d..3f634737 100644 --- a/_vendored/conv_template.py +++ b/_vendored/conv_template.py @@ -82,8 +82,8 @@ __all__ = ['process_str', 'process_file'] import os -import sys import re +import sys # names for replacement that are already global. global_names = {} @@ -106,12 +106,12 @@ def parse_structure(astr, level): at zero. Returns an empty list if no loops found. """ - if level == 0 : + if level == 0: loopbeg = "/**begin repeat" loopend = "/**end repeat**/" - else : - loopbeg = "/**begin repeat%d" % level - loopend = "/**end repeat%d**/" % level + else: + loopbeg = f"/**begin repeat{level}" + loopend = f"/**end repeat{level}**/" ind = 0 line = 0 @@ -124,9 +124,9 @@ def parse_structure(astr, level): start2 = astr.find("\n", start2) fini1 = astr.find(loopend, start2) fini2 = astr.find("\n", fini1) - line += astr.count("\n", ind, start2+1) - spanlist.append((start, start2+1, fini1, fini2+1, line)) - line += astr.count("\n", start2+1, fini2) + line += astr.count("\n", ind, start2 + 1) + spanlist.append((start, start2 + 1, fini1, fini2 + 1, line)) + line += astr.count("\n", start2 + 1, fini2) ind = fini2 spanlist.sort() return spanlist @@ -135,10 +135,13 @@ def parse_structure(astr, level): def paren_repl(obj): torep = obj.group(1) numrep = obj.group(2) - return ','.join([torep]*int(numrep)) + return ','.join([torep] * int(numrep)) + parenrep = re.compile(r"\(([^)]*)\)\*(\d+)") plainrep = re.compile(r"([^*]+)\*(\d+)") + + def parse_values(astr): # replaces all occurrences of '(a,b,c)*4' in astr # with 'a,b,c,a,b,c,a,b,c,a,b,c'. Empty braces generate @@ -155,7 +158,7 @@ def parse_values(astr): named_re = re.compile(r"#\s*(\w*)\s*=([^#]*)#") exclude_vars_re = re.compile(r"(\w*)=(\w*)") exclude_re = re.compile(":exclude:") -def parse_loop_header(loophead) : +def parse_loop_header(loophead): """Find all named replacements in the header Returns a list of dictionaries, one for each loop iteration, @@ -179,14 +182,13 @@ def parse_loop_header(loophead) : name = rep[0] vals = parse_values(rep[1]) size = len(vals) - if nsub is None : + if nsub is None: nsub = size - elif nsub != size : + elif nsub != size: msg = "Mismatch in number of values, %d != %d\n%s = %s" raise ValueError(msg % (nsub, size, name, vals)) names.append((name, vals)) - # Find any exclude variables excludes = [] @@ -200,30 +202,33 @@ def parse_loop_header(loophead) : # generate list of dictionaries, one for each template iteration dlist = [] - if nsub is None : + if nsub is None: raise ValueError("No substitution variables found") for i in range(nsub): tmp = {name: vals[i] for name, vals in names} dlist.append(tmp) return dlist + replace_re = re.compile(r"@(\w+)@") -def parse_string(astr, env, level, line) : - lineno = "#line %d\n" % line + + +def parse_string(astr, env, level, line): + lineno = f"#line {line}\n" # local function for string replacement, uses env def replace(match): name = match.group(1) - try : + try: val = env[name] except KeyError: - msg = 'line %d: no definition of key "%s"'%(line, name) + msg = f'line {line}: no definition of key "{name}"' raise ValueError(msg) from None return val code = [lineno] struct = parse_structure(astr, level) - if struct : + if struct: # recurse over inner loops oldend = 0 newlevel = level + 1 @@ -234,18 +239,18 @@ def replace(match): oldend = sub[3] newline = line + sub[4] code.append(replace_re.sub(replace, pref)) - try : + try: envlist = parse_loop_header(head) except ValueError as e: - msg = "line %d: %s" % (newline, e) + msg = f"line {newline}: {e}" raise ValueError(msg) - for newenv in envlist : + for newenv in envlist: newenv.update(env) newcode = parse_string(text, newenv, newlevel, newline) code.extend(newcode) suff = astr[oldend:] code.append(replace_re.sub(replace, suff)) - else : + else: # replace keys code.append(replace_re.sub(replace, astr)) code.append('\n') @@ -284,8 +289,8 @@ def process_file(source): try: code = process_str(''.join(lines)) except ValueError as e: - raise ValueError('In "%s" loop at %s' % (sourcefile, e)) from None - return '#line 1 "%s"\n%s' % (sourcefile, code) + raise ValueError(f'In "{sourcefile}" loop at {e}') from None + return f'#line 1 "{sourcefile}"\n{code}' def unique_key(adict): @@ -321,9 +326,10 @@ def main(): try: writestr = process_str(allstr) except ValueError as e: - raise ValueError("In %s loop at %s" % (file, e)) from None + raise ValueError(f"In {file} loop at {e}") from None outfile.write(writestr) + if __name__ == "__main__": main() diff --git a/_vendored/process_src_template.py b/_vendored/process_src_template.py new file mode 100644 index 00000000..f934c222 --- /dev/null +++ b/_vendored/process_src_template.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +import argparse +import importlib.util +import os + + +def get_processor(): + # Convoluted because we can't import from numpy + # (numpy is not yet built) + conv_template_path = os.path.join( + os.path.dirname(__file__), + 'conv_template.py' + ) + spec = importlib.util.spec_from_file_location( + 'conv_template', conv_template_path + ) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod.process_file + + +def process_and_write_file(fromfile, outfile): + """Process tempita templated file and write out the result. + + The template file is expected to end in `.src` + (e.g., `.c.src` or `.h.src`). + Processing `npy_somefile.c.src` generates `npy_somefile.c`. + + """ + process_file = get_processor() + content = process_file(fromfile) + with open(outfile, 'w') as f: + f.write(content) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "infile", + type=str, + help="Path to the input file" + ) + parser.add_argument( + "-o", + "--outfile", + type=str, + help="Path to the output file" + ) + parser.add_argument( + "-i", + "--ignore", + type=str, + help="An ignored input - may be useful to add a " + "dependency between custom targets", + ) + args = parser.parse_args() + + if not args.infile.endswith('.src'): + raise ValueError(f"Unexpected extension: {args.infile}") + + outfile_abs = os.path.join(os.getcwd(), args.outfile) + process_and_write_file(args.infile, outfile_abs) + + +if __name__ == "__main__": + main() diff --git a/conda-recipe-cf/bld.bat b/conda-recipe-cf/bld.bat index e27318da..58178a84 100644 --- a/conda-recipe-cf/bld.bat +++ b/conda-recipe-cf/bld.bat @@ -2,24 +2,24 @@ REM A workaround for activate-dpcpp.bat issue to be addressed in 2021.4 set "LIB=%BUILD_PREFIX%\Library\lib;%BUILD_PREFIX%\compiler\lib;%LIB%" set "INCLUDE=%BUILD_PREFIX%\include;%INCLUDE%" -"%PYTHON%" setup.py clean --all -set "SKBUILD_ARGS=-G Ninja -- -DCMAKE_C_COMPILER:PATH=icx -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON" +set "CC=icx" +set "CXX=icx" -FOR %%V IN (14.0.0 14 15.0.0 15 16.0.0 16 17.0.0 17) DO @( - REM set DIR_HINT if directory exists - IF EXIST "%BUILD_PREFIX%\Library\lib\clang\%%V\" ( - SET "SYCL_INCLUDE_DIR_HINT=%BUILD_PREFIX%\Library\lib\clang\%%V" - ) +%PYTHON% -m build -w -n -x +if %ERRORLEVEL% neq 0 exit 1 + +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% neq 0 exit 1 ) if NOT "%WHEELS_OUTPUT_FOLDER%"=="" ( - rem Install and assemble wheel package from the build bits - "%PYTHON%" setup.py install bdist_wheel %SKBUILD_ARGS% - if errorlevel 1 exit 1 copy dist\mkl_umath*.whl %WHEELS_OUTPUT_FOLDER% - if errorlevel 1 exit 1 -) ELSE ( - rem Only install - "%PYTHON%" setup.py install %SKBUILD_ARGS% - if errorlevel 1 exit 1 + if %ERRORLEVEL% neq 0 exit 1 ) diff --git a/conda-recipe-cf/build.sh b/conda-recipe-cf/build.sh index 00414a1c..3b7f73d6 100644 --- a/conda-recipe-cf/build.sh +++ b/conda-recipe-cf/build.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -e # This is necessary to help DPC++ find Intel libraries such as SVML, IRNG, etc in build prefix export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${BUILD_PREFIX}/lib" @@ -11,21 +12,28 @@ export ICXCFG read -r GLIBC_MAJOR GLIBC_MINOR <<< "$(conda list '^sysroot_linux-64$' \ | tail -n 1 | awk '{print $2}' | grep -oP '\d+' | head -n 2 | tr '\n' ' ')" -export CMAKE_GENERATOR="Ninja" -SKBUILD_ARGS=( - "--" - "-DCMAKE_C_COMPILER:PATH=icx" - "-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON" -) +if [ -d "build" ]; then + rm -rf build +fi + +export CC=icx +export CXX=icpx + +${PYTHON} -m build -w -n -x + +${PYTHON} -m wheel tags --remove \ + --platform "manylinux_${GLIBC_MAJOR}_${GLIBC_MINOR}_x86_64" \ + dist/mkl_umath*.whl + +${PYTHON} -m pip install dist/mkl_umath*.whl \ + --no-build-isolation \ + --no-deps \ + --only-binary :all: \ + --no-index \ + --prefix "${PREFIX}" \ + -vv if [ -n "${WHEELS_OUTPUT_FOLDER}" ]; then - # Install packages and assemble wheel package from built bits - WHEELS_BUILD_ARGS=( - "-p" "manylinux_${GLIBC_MAJOR}_${GLIBC_MINOR}_x86_64" - ) - ${PYTHON} setup.py install bdist_wheel "${WHEELS_BUILD_ARGS[@]}" "${SKBUILD_ARGS[@]}" + mkdir -p "${WHEELS_OUTPUT_FOLDER}" cp dist/mkl_umath*.whl "${WHEELS_OUTPUT_FOLDER}" -else - # Perform regular install - ${PYTHON} setup.py install "${SKBUILD_ARGS[@]}" fi diff --git a/conda-recipe-cf/conda_build_config.yaml b/conda-recipe-cf/conda_build_config.yaml new file mode 100644 index 00000000..6f302ab0 --- /dev/null +++ b/conda-recipe-cf/conda_build_config.yaml @@ -0,0 +1,24 @@ +numpy: + - '1.26.4' +c_compiler: # [linux] + - gcc # [linux] +cxx_compiler: # [linux] + - gxx # [linux] +cxx_compiler_version: # [linux] + - '14' # [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] +CFLAGS: + - -fno-fast-math # [linux] +CXXFLAGS: + - -fno-fast-math # [linux] +CL: + - /fp:precise # [win] diff --git a/conda-recipe-cf/meta.yaml b/conda-recipe-cf/meta.yaml index 92d5a5db..1b458233 100644 --- a/conda-recipe-cf/meta.yaml +++ b/conda-recipe-cf/meta.yaml @@ -1,15 +1,14 @@ -{% set version = "0.4.0dev1" %} -{% set buildnumber = 0 %} - package: name: mkl_umath - version: {{ version }} + version: {{ GIT_DESCRIBE_TAG }} source: path: ../ build: - number: {{ buildnumber }} + number: {{ GIT_DESCRIBE_NUMBER }} + script_env: + - WHEELS_OUTPUT_FOLDER ignore_run_exports: - blas @@ -17,21 +16,28 @@ requirements: build: - {{ compiler('c') }} - {{ compiler('cxx') }} + - {{ stdlib('c') }} - {{ compiler('dpcpp') }} >=2024.2 # [not osx] - - sysroot_linux-64 >=2.28 # [linux] host: - - setuptools >=77 - - cmake + - meson-python >=0.13.0 + - meson + - pkg-config - ninja - git - cython - - scikit-build - python + - python-gil # [py>=314] + - python-build - mkl-devel - tbb-devel - numpy + - wheel >=0.41.3 + - pip run: - python + - python-gil # [py>=314] + - mkl-service + - {{ pin_compatible('numpy') }} - {{ pin_compatible('intel-cmplr-lib-rt') }} test: diff --git a/conda-recipe/bld.bat b/conda-recipe/bld.bat index 0616542f..58178a84 100644 --- a/conda-recipe/bld.bat +++ b/conda-recipe/bld.bat @@ -2,17 +2,24 @@ REM A workaround for activate-dpcpp.bat issue to be addressed in 2021.4 set "LIB=%BUILD_PREFIX%\Library\lib;%BUILD_PREFIX%\compiler\lib;%LIB%" set "INCLUDE=%BUILD_PREFIX%\include;%INCLUDE%" -"%PYTHON%" setup.py clean --all -set "SKBUILD_ARGS=-G Ninja -- -DCMAKE_C_COMPILER:PATH=icx -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON" +set "CC=icx" +set "CXX=icx" + +%PYTHON% -m build -w -n -x +if %ERRORLEVEL% neq 0 exit 1 + +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% neq 0 exit 1 +) if NOT "%WHEELS_OUTPUT_FOLDER%"=="" ( - rem Install and assemble wheel package from the build bits - "%PYTHON%" setup.py install bdist_wheel %SKBUILD_ARGS% - if errorlevel 1 exit 1 copy dist\mkl_umath*.whl %WHEELS_OUTPUT_FOLDER% - if errorlevel 1 exit 1 -) ELSE ( - rem Only install - "%PYTHON%" setup.py install %SKBUILD_ARGS% - if errorlevel 1 exit 1 + if %ERRORLEVEL% neq 0 exit 1 ) diff --git a/conda-recipe/build.sh b/conda-recipe/build.sh index 00414a1c..3b7f73d6 100644 --- a/conda-recipe/build.sh +++ b/conda-recipe/build.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -e # This is necessary to help DPC++ find Intel libraries such as SVML, IRNG, etc in build prefix export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${BUILD_PREFIX}/lib" @@ -11,21 +12,28 @@ export ICXCFG read -r GLIBC_MAJOR GLIBC_MINOR <<< "$(conda list '^sysroot_linux-64$' \ | tail -n 1 | awk '{print $2}' | grep -oP '\d+' | head -n 2 | tr '\n' ' ')" -export CMAKE_GENERATOR="Ninja" -SKBUILD_ARGS=( - "--" - "-DCMAKE_C_COMPILER:PATH=icx" - "-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON" -) +if [ -d "build" ]; then + rm -rf build +fi + +export CC=icx +export CXX=icpx + +${PYTHON} -m build -w -n -x + +${PYTHON} -m wheel tags --remove \ + --platform "manylinux_${GLIBC_MAJOR}_${GLIBC_MINOR}_x86_64" \ + dist/mkl_umath*.whl + +${PYTHON} -m pip install dist/mkl_umath*.whl \ + --no-build-isolation \ + --no-deps \ + --only-binary :all: \ + --no-index \ + --prefix "${PREFIX}" \ + -vv if [ -n "${WHEELS_OUTPUT_FOLDER}" ]; then - # Install packages and assemble wheel package from built bits - WHEELS_BUILD_ARGS=( - "-p" "manylinux_${GLIBC_MAJOR}_${GLIBC_MINOR}_x86_64" - ) - ${PYTHON} setup.py install bdist_wheel "${WHEELS_BUILD_ARGS[@]}" "${SKBUILD_ARGS[@]}" + mkdir -p "${WHEELS_OUTPUT_FOLDER}" cp dist/mkl_umath*.whl "${WHEELS_OUTPUT_FOLDER}" -else - # Perform regular install - ${PYTHON} setup.py install "${SKBUILD_ARGS[@]}" fi diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index 38ea112f..b555102a 100644 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -19,18 +19,20 @@ requirements: - {{ stdlib('c') }} - {{ compiler('dpcpp') }} >=2024.2 # [not osx] host: - - setuptools >=77 - - cmake + - meson-python >=0.13.0 + - meson + - pkg-config - ninja - git - cython - - scikit-build - python - python-gil # [py>=314] + - python-build - mkl-devel - tbb-devel - numpy-base - wheel >=0.41.3 + - pip run: - python - python-gil # [py>=314] diff --git a/meson.build b/meson.build new file mode 100644 index 00000000..c2424036 --- /dev/null +++ b/meson.build @@ -0,0 +1,191 @@ +project( + 'mkl_umath', + ['c', 'cpp', 'cython'], + version: run_command( + 'python', '-c', + 'import os; exec(open("mkl_umath/_version.py").read()); print(__version__)', + check: true + ).stdout().strip(), + default_options: [ + 'c_std=c99', + 'buildtype=release', + 'b_ndebug=if-release' + ] +) + +py = import('python').find_installation(pure: false) + +# get includes +# numpy includes +np_dir = run_command(py, + ['-c', 'import numpy; print(numpy.get_include())'], + check: true +).stdout().strip() + +inc_np = include_directories(np_dir, 'mkl_umath/src') + +# Python headers needed for loops +py_dep = py.dependency() + +# compiler/linker +cc = meson.get_compiler('c') +c_args = [] +link_args = [] + +if get_option('opt_report') + if cc.get_id().startswith('intel') + c_args += 'qopt-report=3' + else + warning('Optimization report requested with non-Intel compiler') + endif +endif + +if cc.get_argument_syntax() == 'msvc' + c_args += [ + '/Ox', '/GS', '/DynamicBase', '/fp:precise' + ] + link_args = ['/NXCompat', '/DynamicBase'] + + if cc.get_id().startswith('intel') + c_args += [ + '/Qimf-precision=high', '/Qprotect-parens', '/clang:-fveclib=SVML' + ] + endif +elif cc.get_argument_syntax() == 'gcc' + c_args += [ + '-O3', '-Wall', '-Wextra', '-Winit-self', '-Wunused-function', + '-Wuninitialized', '-Wmissing-declarations', '-Wstrict-prototypes', + '-Wno-unused-parameter', '-fdiagnostics-color=auto', + '-fstack-protector', '-fstack-protector-all', '-fpic', '-fPIC', + '-D_FORTIFY_SOURCE=2', '-Wformat', '-Wformat-security', + '-fno-delete-null-pointer-checks', '-fwrapv', '-fno-fast-math', + '-Wno-incompatible-function-pointer-types' + ] + link_args += ['-Wl,-z,noexecstack', '-Wl,-z,relro', '-Wl,-z,now'] + if cc.get_id().startswith('intel') + c_args += [ + '-fimf-precision=high', '-fprotect-parens', '-fveclib=SVML', + '-fvectorize' + ] + endif +else + error('Unsupported system.') +endif + +# dependencies +tbb_dep = dependency('tbb', required: true) + +# manually waterfall the meson deps: pkg-config to cmake to find_library +mkl_dep = dependency('mkl-dynamic-lp64-tbb', required: false) + +if not mkl_dep.found() + mkl_dep = dependency('MKL', method: 'cmake', + modules: ['MKL::MKL'], + cmake_args: [ + '-DMKL_ARCH=intel64', + '-DMKL_LINK=dynamic', + '-DMKL_THREADING=tbb_thread', + '-DMKL_INTERFACE=lp64' + ], + required: false + ) +endif + +if not mkl_dep.found() + # use static: false to emulate -DMKL_LINK=dynamic + # static: false docs are wrong, this will only consider shared libs + # see: https://github.com/mesonbuild/meson/issues/14163 + mkl_core = cc.find_library('mkl_core', required: true, static: false) + mkl_intel_lp64 = cc.find_library('mkl_intel_lp64', required: true, static: false) + mkl_tbb_thread = cc.find_library('mkl_tbb_thread', required: true, static: false) + mkl_dep = declare_dependency(dependencies: [mkl_core, mkl_intel_lp64, mkl_tbb_thread]) +endif + +# generate loops, similar to numpy +src_file_cli = find_program('_vendored/process_src_template.py') + +src_file = generator( + src_file_cli, + arguments: ['@INPUT@', '-o', '@OUTPUT@'], + output: '@BASENAME@' +) + +gen_loops_c = src_file.process('mkl_umath/src/mkl_umath_loops.c.src') + +gen_umath_c = custom_target( + '__umath_generated', + output: '__umath_generated.c', + input: 'mkl_umath/generate_umath.py', + command: [py, '@INPUT@', '-o', '@OUTPUT@'] +) + +gen_loops_h = custom_target( + 'mkl_umath_loops_h', + output: ['mkl_umath_loops.h'], + input: 'mkl_umath/src/mkl_umath_loops.h.src', + command: [src_file_cli, '@INPUT@', '-o', '@OUTPUT@'], + depends: gen_umath_c +) + +mkl_umath_loops = shared_library( + 'mkl_umath_loops', + sources: [gen_loops_c, gen_loops_h], + include_directories: inc_np, + dependencies: [mkl_dep, tbb_dep, py_dep], + c_args: c_args, + link_args: link_args, + install: true, + install_dir: py.get_install_dir() / 'mkl_umath' +) + +loops_dep = declare_dependency(link_with: mkl_umath_loops) + +# extension modules + +ext_rpath = '' +if host_machine.system() != 'windows' + ext_rpath = '$ORIGIN/../..:$ORIGIN/../../..:$ORIGIN' +endif + +# gen_loops_h is generated by custom target so must be included in sources +py.extension_module( + '_ufuncs', + sources: ['mkl_umath/src/ufuncsmodule.c', gen_loops_h], + include_directories: inc_np, + dependencies: [mkl_dep, tbb_dep, loops_dep], + c_args: c_args + ['-DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION'], + link_args: link_args, + install_rpath: ext_rpath, + install: true, + subdir: 'mkl_umath' +) + +py.extension_module( + '_patch', + sources: ['mkl_umath/src/_patch.pyx'], + include_directories: inc_np, + dependencies: [loops_dep], + c_args: c_args + ['-DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION'], + cython_args: ['-3', '--fast-fail'], + link_args: link_args, + install_rpath: ext_rpath, + install: true, + subdir: 'mkl_umath' +) + +# install python sources + +py.install_sources( + [ + 'mkl_umath/__init__.py', + 'mkl_umath/_init_helper.py', + 'mkl_umath/_version.py', + 'mkl_umath/generate_umath.py' + ], + subdir: 'mkl_umath' +) + +install_subdir( + 'mkl_umath/tests', + install_dir: py.get_install_dir() / 'mkl_umath' +) diff --git a/meson.options b/meson.options new file mode 100644 index 00000000..4117cabc --- /dev/null +++ b/meson.options @@ -0,0 +1,2 @@ +option('opt_report', type: 'boolean', value: false, + description: 'Whether to generate optimization vectorization report') diff --git a/mkl_umath/generate_umath.py b/mkl_umath/generate_umath.py index 31be813e..c1325157 100644 --- a/mkl_umath/generate_umath.py +++ b/mkl_umath/generate_umath.py @@ -1152,7 +1152,7 @@ def make_ufuncs(funcdict): if uf.signature is None: sig = "NULL" else: - sig = '"{}"'.format(uf.signature) + sig = f'"{uf.signature}"' fmt = textwrap.dedent("""\ identity = {identity_expr}; if ({has_identity} && identity == NULL) {{ diff --git a/mkl_umath/src/mkl_umath_loops.c.src b/mkl_umath/src/mkl_umath_loops.c.src index b588ca6f..5131d620 100644 --- a/mkl_umath/src/mkl_umath_loops.c.src +++ b/mkl_umath/src/mkl_umath_loops.c.src @@ -151,7 +151,7 @@ static inline npy_double spacing(npy_double x) { } static inline npy_float spacingf(npy_float x) { - if (isinff(x)) + if (isinf(x)) return ((npy_float) NAN); return copysignf(nextafterf(fabsf(x), INFINITY), x) - x; diff --git a/mkl_umath/tests/__init__.py b/mkl_umath/tests/__init__.py new file mode 100644 index 00000000..fa81adaf --- /dev/null +++ b/mkl_umath/tests/__init__.py @@ -0,0 +1 @@ +# empty file diff --git a/mkl_umath/ufunc_docstrings_numpy1.py b/mkl_umath/ufunc_docstrings_numpy1.py index 39efaee7..8e7aeb96 100644 --- a/mkl_umath/ufunc_docstrings_numpy1.py +++ b/mkl_umath/ufunc_docstrings_numpy1.py @@ -82,9 +82,9 @@ def add_newdoc(place, name, doc): ) if name[0] != "_" and name not in skip: if "\nx :" in doc: - assert "$OUT_SCALAR_1" in doc, "in {}".format(name) + assert "$OUT_SCALAR_1" in doc, f"in {name}" elif "\nx2 :" in doc or "\nx1, x2 :" in doc: - assert "$OUT_SCALAR_2" in doc, "in {}".format(name) + assert "$OUT_SCALAR_2" in doc, f"in {name}" else: raise AssertionError(f"Could not detect number of inputs in {name}") diff --git a/pyproject.toml b/pyproject.toml index af289e94..01c6ea23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,12 +24,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [build-system] -build-backend = "setuptools.build_meta" +build-backend = "mesonpy" requires = [ - "cmake", + "meson-python>=0.13.0", "ninja", - "scikit-build", - "setuptools>=77", "Cython", "numpy" ] @@ -61,7 +59,7 @@ dependencies = ["numpy >=1.26.4", "mkl-service"] description = "Intel (R) MKL-based universal functions for NumPy arrays" dynamic = ["version"] keywords = ["mkl_umath"] -license = "BSD-3-Clause" +license = {text = "BSD-3-Clause"} name = "mkl_umath" readme = {file = "README.md", content-type = "text/markdown"} requires-python = ">=3.10,<3.15" @@ -74,7 +72,8 @@ Download = "http://github.com/IntelPython/mkl_umath" Homepage = "http://github.com/IntelPython/mkl_umath" [tool.black] -exclude = "_vendored/conv_template.py" +extend-exclude = "(^|/)_vendored/" +force-exclude = "(^|/)_vendored/" line-length = 80 [tool.cython-lint] @@ -87,12 +86,5 @@ force_grid_wrap = 0 include_trailing_comma = true line_length = 80 multi_line_output = 3 -skip = ["_vendored/conv_template.py"] +skip = ["_vendored/conv_template.py", "_vendored/process_src_template.py"] use_parentheses = true - -[tool.setuptools] -include-package-data = true -packages = ["mkl_umath"] - -[tool.setuptools.dynamic] -version = {attr = "mkl_umath._version.__version__"} diff --git a/setup.py b/setup.py deleted file mode 100644 index 2eef2e14..00000000 --- a/setup.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) 2019, Intel Corporation -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Intel Corporation nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import importlib.machinery -import sys -from os import makedirs -from os.path import dirname, exists, join - -import skbuild -from setuptools.modified import newer - -sys.path.insert(0, dirname(__file__)) # Ensures local imports work -from _vendored.conv_template import process_file as process_c_file # noqa: E402 - - -# TODO: rewrite generation in CMake, see NumPy meson implementation -# https://github.com/numpy/numpy/blob/c6fb3357541fd8cf6e4faeaeda3b1a9065da0520/numpy/_core/meson.build#L623 -def load_module(name, fn): - """Credit: numpy.compat.npy_load_module""" - return importlib.machinery.SourceFileLoader(name, fn).load_module(name) - - -def separator_join(sep, strs): - """ - Joins non-empty arguments strings with dot. - - Credit: numpy.distutils.misc_util.dot_join - """ - assert isinstance(strs, (list, tuple)) - assert isinstance(sep, str) - return sep.join([si for si in strs if si]) - - -pdir = join(dirname(__file__), "mkl_umath") -wdir = join(pdir, "src") - -generate_umath_py = join(pdir, "generate_umath.py") -n = separator_join("_", ("mkl_umath", "generate_umath")) -generate_umath = load_module(n, generate_umath_py) -del n - - -def generate_umath_c(build_dir): - target_dir = join(build_dir, "src") - target = join(target_dir, "__umath_generated.c") - if not exists(target_dir): - print( - "Folder {} was expected to exist, but creating".format(target_dir) - ) - makedirs(target_dir) - script = generate_umath_py - if newer(script, target): - with open(target, "w") as f: - f.write( - generate_umath.make_code( - generate_umath.defdict, generate_umath.__file__ - ) - ) - return [] - - -generate_umath_c(pdir) - -loops_header_templ = join(wdir, "mkl_umath_loops.h.src") -processed_loops_h_fn = join(wdir, "mkl_umath_loops.h") -loops_header_processed = process_c_file(loops_header_templ) - -with open(processed_loops_h_fn, "w") as fid: - fid.write(loops_header_processed) - -loops_src_templ = join(wdir, "mkl_umath_loops.c.src") -processed_loops_src_fn = join(wdir, "mkl_umath_loops.c") -loops_src_processed = process_c_file(loops_src_templ) - -with open(processed_loops_src_fn, "w") as fid: - fid.write(loops_src_processed) - - -skbuild.setup( - packages=[ - "mkl_umath", - ], - package_data={"mkl_umath": ["tests/*.*"]}, - include_package_data=True, -)