summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGanesh Kathiresan <ganesh3597@gmail.com>2023-01-24 15:49:43 +0530
committerGitHub <noreply@github.com>2023-01-24 11:19:43 +0100
commit04823c1a95e23c55432a78c7c7414c040279c2e1 (patch)
treef5a885b5fe175131cefe770213c9d9eab95f054c
parent172a1942893b5ce55abccd836fdd9f00235a6767 (diff)
downloadnumpy-04823c1a95e23c55432a78c7c7414c040279c2e1.tar.gz
BLD: Meson `__config__` generation (#22769)
Add functionality to autogenerate build information for a Meson-based build. In order to add new information, do the following: - Add the information as an argument in `numpy/meson.build` - Modify `__config__.py.in` to accept the new argument Note that SIMD information is added to config, but is WIP/empty, because `__cpu*` lists are not yet populated as meson does not build SIMD features yet. There are two display modes: - `stdout`: Uses `PyYaml` to display in a human friendly format. Uses `json` if `PyYaml` is not installed - `dicts`: Returns a `dict` object Things will work fine without `pyyaml` installed, an unobtrusive warning is displayed that the printed output will look better with `pyyaml`. [ci skip]
-rw-r--r--doc/release/upcoming_changes/22769.improvement.rst5
-rw-r--r--numpy/__config__.py.in270
-rw-r--r--numpy/meson.build66
-rw-r--r--numpy/tests/test_numpy_config.py44
4 files changed, 256 insertions, 129 deletions
diff --git a/doc/release/upcoming_changes/22769.improvement.rst b/doc/release/upcoming_changes/22769.improvement.rst
new file mode 100644
index 000000000..3566648ac
--- /dev/null
+++ b/doc/release/upcoming_changes/22769.improvement.rst
@@ -0,0 +1,5 @@
+`np.show_config` uses information from Meson
+--------------------------------------------
+Build and system information now contains information from Meson.
+`np.show_config` now has a new optional parameter ``mode`` to help
+customize the output.
diff --git a/numpy/__config__.py.in b/numpy/__config__.py.in
index cda615904..659a09b26 100644
--- a/numpy/__config__.py.in
+++ b/numpy/__config__.py.in
@@ -1,52 +1,136 @@
# This file is generated by numpy's build process
# It contains system_info results at the time of building this package.
-__all__ = ["get_info", "show"]
-
-import os
-import sys
-
-extra_dll_dir = os.path.join(os.path.dirname(__file__), '.libs')
-
-if sys.platform == 'win32' and os.path.isdir(extra_dll_dir):
- os.add_dll_directory(extra_dll_dir)
-
-blas_armpl_info={}
-blas_mkl_info={}
-blis_info={}
-openblas_info={}
-accelerate_info={}
-atlas_3_10_blas_threads_info={}
-atlas_3_10_blas_info={}
-atlas_blas_threads_info={}
-atlas_blas_info={}
-blas_info={}
-blas_src_info={}
-blas_opt_info={}
-lapack_armpl_info={}
-lapack_mkl_info={}
-openblas_lapack_info={}
-openblas_clapack_info={}
-flame_info={}
-atlas_3_10_threads_info={}
-atlas_3_10_info={}
-atlas_threads_info={}
-atlas_info={}
-lapack_info={}
-lapack_src_info={}
-lapack_opt_info={}
-numpy_linalg_lapack_lite={}
-
-def get_info(name):
- g = globals()
- return g.get(name, g.get(name + "_info", {}))
-
-def show():
+from enum import Enum
+from numpy.core._multiarray_umath import (
+ __cpu_features__,
+ __cpu_baseline__,
+ __cpu_dispatch__,
+)
+
+__all__ = ["show"]
+_built_with_meson = True
+
+
+class DisplayModes(Enum):
+ stdout = "stdout"
+ dicts = "dicts"
+
+
+def _cleanup(d):
"""
- Show libraries in the system on which NumPy was built.
+ Removes empty values in a `dict` recursively
+ This ensures we remove values that Meson could not provide to CONFIG
+ """
+ if type(d) is dict:
+ return dict(
+ (k, _cleanup(v)) for k, v in d.items() if v and _cleanup(v)
+ )
+ else:
+ return d
+
+
+CONFIG = _cleanup(
+ {
+ "Compilers": {
+ "c": {
+ "name": "@C_COMP@",
+ "linker": "@C_COMP_LINKER_ID@",
+ "version": "@C_COMP_VERSION@",
+ "commands": "@C_COMP_CMD_ARRAY@",
+ },
+ "cython": {
+ "name": "@CYTHON_COMP@",
+ "linker": "@CYTHON_COMP_LINKER_ID@",
+ "version": "@CYTHON_COMP_VERSION@",
+ "commands": "@CYTHON_COMP_CMD_ARRAY@",
+ },
+ "c++": {
+ "name": "@CPP_COMP@",
+ "linker": "@CPP_COMP_LINKER_ID@",
+ "version": "@CPP_COMP_VERSION@",
+ "commands": "@CPP_COMP_CMD_ARRAY@",
+ },
+ },
+ "Machine Information": {
+ "host": {
+ "cpu": "@HOST_CPU@",
+ "family": "@HOST_CPU_FAMILY@",
+ "endian": "@HOST_CPU_ENDIAN@",
+ "system": "@HOST_CPU_SYSTEM@",
+ },
+ "build": {
+ "cpu": "@BUILD_CPU@",
+ "family": "@BUILD_CPU_FAMILY@",
+ "endian": "@BUILD_CPU_ENDIAN@",
+ "system": "@BUILD_CPU_SYSTEM@",
+ },
+ "cross-compiled": "@CROSS_COMPILED@",
+ },
+ "Build Dependencies": {
+ "blas": {
+ "name": "@BLAS_NAME@",
+ "found": "@BLAS_FOUND@",
+ "version": "@BLAS_VERSION@",
+ "detection method": "@BLAS_TYPE_NAME@",
+ "include directory": "@BLAS_INCLUDEDIR@",
+ "lib directory": "@BLAS_LIBDIR@",
+ "openblas configuration": "@BLAS_OPENBLAS_CONFIG@",
+ "pc file directory": "@BLAS_PCFILEDIR@",
+ },
+ "lapack": {
+ "name": "@LAPACK_NAME@",
+ "found": "@LAPACK_FOUND@",
+ "version": "@LAPACK_VERSION@",
+ "detection method": "@LAPACK_TYPE_NAME@",
+ "include directory": "@LAPACK_INCLUDEDIR@",
+ "lib directory": "@LAPACK_LIBDIR@",
+ "openblas configuration": "@LAPACK_OPENBLAS_CONFIG@",
+ "pc file directory": "@LAPACK_PCFILEDIR@",
+ },
+ },
+ "Python Information": {
+ "path": "@PYTHON_PATH@",
+ "version": "@PYTHON_VERSION@",
+ },
+ "SIMD Extensions": {
+ "baseline": __cpu_baseline__,
+ "found": [
+ feature
+ for feature in __cpu_dispatch__
+ if __cpu_features__[feature]
+ ],
+ "not found": [
+ feature
+ for feature in __cpu_dispatch__
+ if not __cpu_features__[feature]
+ ],
+ },
+ }
+)
+
+
+def _check_pyyaml():
+ import yaml
+
+ return yaml
+
+
+def show(mode=DisplayModes.stdout.value):
+ """
+ Show libraries and system information on which NumPy was built
+ and is being used
+
+ Parameters
+ ----------
+ mode : {`'stdout'`, `'dicts'`}, optional.
+ Indicates how to display the config information.
+ `'stdout'` prints to console, `'dicts'` returns a dictionary
+ of the configuration.
- Print information about various resources (libraries, library
- directories, include directories, etc.) in the system on which
- NumPy was built.
+ Returns
+ -------
+ out : {`dict`, `None`}
+ If mode is `'dicts'`, a dict is returned, else None
See Also
--------
@@ -55,80 +139,24 @@ def show():
Notes
-----
- 1. Classes specifying the information to be printed are defined
- in the `numpy.distutils.system_info` module.
-
- Information may include:
-
- * ``language``: language used to write the libraries (mostly
- C or f77)
- * ``libraries``: names of libraries found in the system
- * ``library_dirs``: directories containing the libraries
- * ``include_dirs``: directories containing library header files
- * ``src_dirs``: directories containing library source files
- * ``define_macros``: preprocessor macros used by
- ``distutils.setup``
- * ``baseline``: minimum CPU features required
- * ``found``: dispatched features supported in the system
- * ``not found``: dispatched features that are not supported
- in the system
-
- 2. NumPy BLAS/LAPACK Installation Notes
-
- Installing a numpy wheel (``pip install numpy`` or force it
- via ``pip install numpy --only-binary :numpy: numpy``) includes
- an OpenBLAS implementation of the BLAS and LAPACK linear algebra
- APIs. In this case, ``library_dirs`` reports the original build
- time configuration as compiled with gcc/gfortran; at run time
- the OpenBLAS library is in
- ``site-packages/numpy.libs/`` (linux), or
- ``site-packages/numpy/.dylibs/`` (macOS), or
- ``site-packages/numpy/.libs/`` (windows).
-
- Installing numpy from source
- (``pip install numpy --no-binary numpy``) searches for BLAS and
- LAPACK dynamic link libraries at build time as influenced by
- environment variables NPY_BLAS_LIBS, NPY_CBLAS_LIBS, and
- NPY_LAPACK_LIBS; or NPY_BLAS_ORDER and NPY_LAPACK_ORDER;
- or the optional file ``~/.numpy-site.cfg``.
- NumPy remembers those locations and expects to load the same
- libraries at run-time.
- In NumPy 1.21+ on macOS, 'accelerate' (Apple's Accelerate BLAS
- library) is in the default build-time search order after
- 'openblas'.
-
- Examples
- --------
- >>> import numpy as np
- >>> np.show_config()
- blas_opt_info:
- language = c
- define_macros = [('HAVE_CBLAS', None)]
- libraries = ['openblas', 'openblas']
- library_dirs = ['/usr/local/lib']
+ 1. The `'stdout'` mode will give more readable
+ output if ``pyyaml`` is installed
+
"""
- from numpy.core._multiarray_umath import (
- __cpu_features__, __cpu_baseline__, __cpu_dispatch__
- )
- for name,info_dict in globals().items():
- if name[0] == "_" or type(info_dict) is not type({}): continue
- print(name + ":")
- if not info_dict:
- print(" NOT AVAILABLE")
- for k,v in info_dict.items():
- v = str(v)
- if k == "sources" and len(v) > 200:
- v = v[:60] + " ...\n... " + v[-60:]
- print(" %s = %s" % (k,v))
-
- features_found, features_not_found = [], []
- for feature in __cpu_dispatch__:
- if __cpu_features__[feature]:
- features_found.append(feature)
- else:
- features_not_found.append(feature)
-
- print("Supported SIMD extensions in this NumPy install:")
- print(" baseline = %s" % (','.join(__cpu_baseline__)))
- print(" found = %s" % (','.join(features_found)))
- print(" not found = %s" % (','.join(features_not_found)))
+ if mode == DisplayModes.stdout.value:
+ try: # Non-standard library, check import
+ yaml = _check_pyyaml()
+
+ print(yaml.dump(CONFIG))
+ except ModuleNotFoundError:
+ import warnings
+ import json
+
+ warnings.warn("Install `pyyaml` for better output", stacklevel=1)
+ print(json.dumps(CONFIG, indent=2))
+ elif mode == DisplayModes.dicts.value:
+ return CONFIG
+ else:
+ raise AttributeError(
+ f"Invalid `mode`, use one of: {', '.join([e.value for e in DisplayModes])}"
+ )
diff --git a/numpy/meson.build b/numpy/meson.build
index 23bd83a04..ec131ebb5 100644
--- a/numpy/meson.build
+++ b/numpy/meson.build
@@ -66,15 +66,17 @@ endif
blas = dependency(blas_name, required: false)
lapack = dependency(lapack_name, required: false)
+dependency_map = {
+ 'BLAS': blas,
+ 'LAPACK': lapack,
+}
+
# BLAS and LAPACK are optional dependencies for NumPy. We can only use a BLAS
# which provides a CBLAS interface.
# TODO: add ILP64 support
have_blas = blas.found() # TODO: and blas.has_cblas()
have_lapack = lapack.found()
-
-# TODO: generate __config__.py (see scipy/meson.build)
-
# Copy the main __init__.py|pxd files to the build dir (needed for Cython)
__init__py = fs.copyfile('__init__.py')
__init__pxd = fs.copyfile('__init__.pxd')
@@ -143,12 +145,60 @@ foreach subdir: pure_subdirs
install_subdir(subdir, install_dir: np_dir)
endforeach
-custom_target('__config__.py',
- output: '__config__.py',
+compilers = {
+ 'C': cc,
+ 'CPP': cpp,
+ 'CYTHON': meson.get_compiler('cython')
+}
+
+machines = {
+ 'HOST': host_machine,
+ 'BUILD': build_machine,
+}
+
+conf_data = configuration_data()
+
+# Set compiler information
+foreach name, compiler : compilers
+ conf_data.set(name + '_COMP', compiler.get_id())
+ conf_data.set(name + '_COMP_LINKER_ID', compiler.get_linker_id())
+ conf_data.set(name + '_COMP_VERSION', compiler.version())
+ conf_data.set(name + '_COMP_CMD_ARRAY', compiler.cmd_array())
+endforeach
+
+# Machines CPU and system information
+foreach name, machine : machines
+ conf_data.set(name + '_CPU', machine.cpu())
+ conf_data.set(name + '_CPU_FAMILY', machine.cpu_family())
+ conf_data.set(name + '_CPU_ENDIAN', machine.endian())
+ conf_data.set(name + '_CPU_SYSTEM', machine.system())
+endforeach
+
+conf_data.set('CROSS_COMPILED', meson.is_cross_build())
+
+# Python information
+conf_data.set('PYTHON_PATH', py.full_path())
+conf_data.set('PYTHON_VERSION', py.language_version())
+
+# Dependencies information
+foreach name, dep : dependency_map
+ conf_data.set(name + '_NAME', dep.name())
+ conf_data.set(name + '_FOUND', dep.found())
+ if dep.found()
+ conf_data.set(name + '_VERSION', dep.version())
+ conf_data.set(name + '_TYPE_NAME', dep.type_name())
+ conf_data.set(name + '_INCLUDEDIR', dep.get_variable('includedir'))
+ conf_data.set(name + '_LIBDIR', dep.get_variable('libdir'))
+ conf_data.set(name + '_OPENBLAS_CONFIG', dep.get_variable('openblas_config'))
+ conf_data.set(name + '_PCFILEDIR', dep.get_variable('pcfiledir'))
+ endif
+endforeach
+
+configure_file(
input: '__config__.py.in',
- command: [tempita_cli, '@INPUT@', '-o', '@OUTPUT@'],
- install: true,
- install_dir: np_dir
+ output: '__config__.py',
+ configuration : conf_data,
+ install_dir: np_dir,
)
subdir('core')
diff --git a/numpy/tests/test_numpy_config.py b/numpy/tests/test_numpy_config.py
new file mode 100644
index 000000000..82c1ad70b
--- /dev/null
+++ b/numpy/tests/test_numpy_config.py
@@ -0,0 +1,44 @@
+"""
+Check the numpy config is valid.
+"""
+import numpy as np
+import pytest
+from unittest.mock import Mock, patch
+
+pytestmark = pytest.mark.skipif(
+ not hasattr(np.__config__, "_built_with_meson"),
+ reason="Requires Meson builds",
+)
+
+
+class TestNumPyConfigs:
+ REQUIRED_CONFIG_KEYS = [
+ "Compilers",
+ "Machine Information",
+ "Python Information",
+ ]
+
+ @patch("numpy.__config__._check_pyyaml")
+ def test_pyyaml_not_found(self, mock_yaml_importer):
+ mock_yaml_importer.side_effect = ModuleNotFoundError()
+ with pytest.warns(UserWarning):
+ np.show_config()
+
+ def test_dict_mode(self):
+ config = np.show_config(mode="dicts")
+
+ assert isinstance(config, dict)
+ assert all([key in config for key in self.REQUIRED_CONFIG_KEYS]), (
+ "Required key missing,"
+ " see index of `False` with `REQUIRED_CONFIG_KEYS`"
+ )
+
+ def test_invalid_mode(self):
+ with pytest.raises(AttributeError):
+ np.show_config(mode="foo")
+
+ def test_warn_to_add_tests(self):
+ assert len(np.__config__.DisplayModes) == 2, (
+ "New mode detected,"
+ " please add UT if applicable and increment this count"
+ )