pythonnumpylapackblasmeson-build

Build numpy 2.3+ without accelerated libraries


Related post: Compile numpy WITHOUT Intel MKL/BLAS/ATLAS/LAPACK

Recent versions of numpy use meson for build configuration, I can build numpy from source but failed to exclude BLAS/LAPACK/... deps.

Here is what I tried in github workflow:

      - name: Build Numpy no BLAS
        run: |
          curl -L -o numpy.tar.gz https://github.com/numpy/numpy/releases/download/v2.3.4/numpy-2.3.4.tar.gz
          tar -xzf numpy.tar.gz

          # patch meson.build to explicit skip blas detection
          patch numpy-2.3.4/numpy/meson.build build_tools/numpy_disable_blas.diff

          uv pip install meson-python cython build
          uv run python -m build --wheel --outdir ~/numpycache --config-setting=setup-args=-Dblas=none --config-setting=setup-args=-Dlapack=none ./numpy-2.3.4

I patched meson.build because there is no argument that will disable BLAS/LAPACK detection, it looks like the following after patch:

...
# numpy-2.3.4/numpy/meson.build
# start from line 108

# First try scipy-openblas, and if found don't look for cblas or lapack, we
# know what's inside the scipy-openblas wheels already.
# if blas_name == 'openblas' or blas_name == 'auto'
#   blas = dependency('scipy-openblas', method: 'pkg-config', required: false)
#   if blas.found()
#     blas_name = 'scipy-openblas'
#   endif
# endif
# if blas_name == 'auto'
#   foreach _name : blas_order
#     if _name == 'mkl'
#       blas = dependency('mkl',
#         modules: ['cblas'] + blas_interface + mkl_opts,
#         required: false,  # may be required, but we need to emit a custom error message
#         version: mkl_version_req,
#       )
#       # Insert a second try with MKL, because we may be rejecting older versions
#       # or missing it because no pkg-config installed. If so, we need to retry
#       # with MKL SDL, and drop the version constraint (this always worked).
#       if not blas.found() and mkl_may_use_sdl
#         blas = dependency('mkl', modules: ['cblas', 'sdl: true'], required: false)
#       endif
#     else
#       if _name == 'flexiblas' and use_ilp64
#         _name = 'flexiblas64'
#       endif
#       blas = dependency(_name, modules: ['cblas'] + blas_interface, required: false)
#     endif
#     if blas.found()
#       break
#     endif
#   endforeach
# else
#   if blas_name == 'mkl'
#     blas = dependency('mkl',
#       modules: ['cblas'] + blas_interface + mkl_opts,
#       required: false,
#       version: mkl_version_req,
#     )
#     # Same deal as above - try again for MKL
#     if not blas.found() and mkl_may_use_sdl
#       blas = dependency('mkl', modules: ['cblas', 'sdl: true'], required: false)
#     endif
#   else
#     blas = dependency(blas_name, modules: ['cblas'] + blas_interface, required: false)
#   endif
# endif

blas = disabler()
have_blas = false # blas.found()
if have_blas
  _args_blas = ['-DHAVE_CBLAS']  # note: used for C and C++ via `blas_dep` below
  if use_ilp64
    _args_blas += ['-DHAVE_BLAS_ILP64']
    if 'openblas' in blas.name()
      _args_blas += ['-DOPENBLAS_ILP64_NAMING_SCHEME']
    endif
  endif
  if blas_symbol_suffix == 'auto'
    if blas_name == 'scipy-openblas' and use_ilp64
      blas_symbol_suffix = '64_'
    else
      blas_symbol_suffix = blas.get_variable('symbol_suffix', default_value: '')
    endif
    message(f'BLAS symbol suffix: @blas_symbol_suffix@')
  endif
  if blas_symbol_suffix != ''
    _args_blas += ['-DBLAS_SYMBOL_SUFFIX=' + blas_symbol_suffix]
  endif
  blas_dep = declare_dependency(
    dependencies: [blas],
    compile_args: _args_blas,
  )
else
  if allow_noblas
    blas_dep = []
  else
    error('No BLAS library detected! Install one, or use the ' + \
          '`allow-noblas` build option (note, this may be up to 100x slower ' + \
          'for some linear algebra operations).')
  endif
endif

# if 'mkl' in blas.name() or blas.name() == 'accelerate' or blas_name == 'scipy-openblas'
#   # For these libraries we know that they contain LAPACK, and it's desirable to
#   # use that - no need to run the full detection twice.
#   lapack = blas
# else
#   if lapack_name == 'auto'
#     foreach _name : lapack_order
#       lapack = dependency(_name, modules: ['lapack'] + blas_interface, required: false)
#       if lapack.found()
#         break
#       endif
#     endforeach
#   else
#     lapack = dependency(lapack_name, modules: ['lapack'] + blas_interface, required: false)
#   endif
# endif

lapack = disabler()
have_lapack = false # lapack.found()
if not have_lapack and not allow_noblas
  error('No LAPACK library detected! Install one, or use the ' + \
        '`allow-noblas` build option (note, this may be up to 100x slower ' + \
        'for some linear algebra operations).')
else
  lapack_dep = declare_dependency(dependencies: [lapack, blas_dep])
endif

...

The building log shows BLAS related variables are not set, which is exactly what I expected.

Configuring __config__.py using configuration
..\numpy\meson.build:445: WARNING: The variable(s) 'BLAS_INCLUDEDIR', 'BLAS_LIBDIR', 'BLAS_OPENBLAS_CONFIG', 'BLAS_PCFILEDIR', 'BLAS_TYPE_NAME', 'BLAS_VERSION', 'LAPACK_INCLUDEDIR', 'LAPACK_LIBDIR', 'LAPACK_OPENBLAS_CONFIG', 'LAPACK_PCFILEDIR', 'LAPACK_TYPE_NAME', 'LAPACK_VERSION' in the input file 'numpy\__config__.py.in' are not present in the given configuration data.
Checking for size of "short" : 2
...
Build targets in project: 104
WARNING: Deprecated features used:
 * 1.3.0: {'Source file src/umath/svml/linux/avx512/svml_z0_acos_d_la.s in the 'objects' kwarg is not an object.'}
NumPy 2.3.4
  User defined options
    Native files: /home/runner/work/STDF-Viewer/STDF-Viewer/numpy-2.3.4/.mesonpy-l6ngii4n/meson-python-native-file.ini
    b_ndebug    : if-release
    b_vscrt     : md
    blas        : none
    buildtype   : release
    lapack      : none
Found ninja-1.13.1 at /usr/local/bin/ninja
+ /usr/local/bin/ninja
[1/512] Copying file numpy/__init__.pxd
...

However, numpy still manages to detect the BLAS, which might be preinstalled in Github runner:

# numpy.show_config()

  "Build Dependencies": {
    "blas": {
      "name": "scipy-openblas",
      "found": true,
      "version": "0.3.30",
      "detection method": "pkgconfig",
      "include directory": "/opt/_internal/cpython-3.13.8/lib/python3.13/site-packages/scipy_openblas64/include",
  warnings.warn("Install `pyyaml` for better output", stacklevel=1)
      "lib directory": "/opt/_internal/cpython-3.13.8/lib/python3.13/site-packages/scipy_openblas64/lib",
      "openblas configuration": "OpenBLAS 0.3.30  USE64BITINT DYNAMIC_ARCH NO_AFFINITY Haswell MAX_THREADS=64",
      "pc file directory": "/project/.openblas"
    },
    "lapack": {
      "name": "scipy-openblas",
      "found": true,
      "version": "0.3.30",
      "detection method": "pkgconfig",
      "include directory": "/opt/_internal/cpython-3.13.8/lib/python3.13/site-packages/scipy_openblas64/include",
      "lib directory": "/opt/_internal/cpython-3.13.8/lib/python3.13/site-packages/scipy_openblas64/lib",
      "openblas configuration": "OpenBLAS 0.3.30  USE64BITINT DYNAMIC_ARCH NO_AFFINITY Haswell MAX_THREADS=64",
      "pc file directory": "/project/.openblas"
    }
  },

Is there anything I missed? What is the proper way to disable BLAS detection?


Solution

  • Turns out the uv is behind all this.

    uv silently uninstalled the local numpy and installed the numpy from pypi before:

    uv run python -c "import numpy as np; print('NumPy version:', np.__version__); np.__config__.show()"
    

    If you are trying to build via uv and numpy is one of the deps in your project, you need to add --no-sync to uv run to prevent the local numpy being uninstalled automatically.

    uv pip install meson-python cython build
    uv run --no-sync python -m build --wheel --config-setting=setup-args=-Dblas=none --config-setting=setup-args=-Dlapack=none ./numpy-2.3.4
    

    You should see the following config if the local numpy is installed:

    # numpy.show_config()
    
      "Build Dependencies": {
        "blas": {
          "name": "none"
        },
        "lapack": {
          "name": "none"
        }
      },