You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					2656 lines
				
				97 KiB
			
		
		
			
		
	
	
					2656 lines
				
				97 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								"""Provides the `CCompilerOpt` class, used for handling the CPU/hardware
							 | 
						||
| 
								 | 
							
								optimization, starting from parsing the command arguments, to managing the
							 | 
						||
| 
								 | 
							
								relation between the CPU baseline and dispatch-able features,
							 | 
						||
| 
								 | 
							
								also generating the required C headers and ending with compiling
							 | 
						||
| 
								 | 
							
								the sources with proper compiler's flags.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								`CCompilerOpt` doesn't provide runtime detection for the CPU features,
							 | 
						||
| 
								 | 
							
								instead only focuses on the compiler side, but it creates abstract C headers
							 | 
						||
| 
								 | 
							
								that can be used later for the final runtime dispatching process."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import atexit
							 | 
						||
| 
								 | 
							
								import inspect
							 | 
						||
| 
								 | 
							
								import os
							 | 
						||
| 
								 | 
							
								import pprint
							 | 
						||
| 
								 | 
							
								import re
							 | 
						||
| 
								 | 
							
								import subprocess
							 | 
						||
| 
								 | 
							
								import textwrap
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# These flags are used to compile any C++ source within Numpy.
							 | 
						||
| 
								 | 
							
								# They are chosen to have very few runtime dependencies.
							 | 
						||
| 
								 | 
							
								NPY_CXX_FLAGS = [
							 | 
						||
| 
								 | 
							
								    '-std=c++11',  # Minimal standard version
							 | 
						||
| 
								 | 
							
								    '-D__STDC_VERSION__=0',  # for compatibility with C headers
							 | 
						||
| 
								 | 
							
								    '-fno-exceptions',  # no exception support
							 | 
						||
| 
								 | 
							
								    '-fno-rtti']  # no runtime type information
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _Config:
							 | 
						||
| 
								 | 
							
								    """An abstract class holds all configurable attributes of `CCompilerOpt`,
							 | 
						||
| 
								 | 
							
								    these class attributes can be used to change the default behavior
							 | 
						||
| 
								 | 
							
								    of `CCompilerOpt` in order to fit other requirements.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Attributes
							 | 
						||
| 
								 | 
							
								    ----------
							 | 
						||
| 
								 | 
							
								    conf_nocache : bool
							 | 
						||
| 
								 | 
							
								        Set True to disable memory and file cache.
							 | 
						||
| 
								 | 
							
								        Default is False.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    conf_noopt : bool
							 | 
						||
| 
								 | 
							
								        Set True to forces the optimization to be disabled,
							 | 
						||
| 
								 | 
							
								        in this case `CCompilerOpt` tends to generate all
							 | 
						||
| 
								 | 
							
								        expected headers in order to 'not' break the build.
							 | 
						||
| 
								 | 
							
								        Default is False.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    conf_cache_factors : list
							 | 
						||
| 
								 | 
							
								        Add extra factors to the primary caching factors. The caching factors
							 | 
						||
| 
								 | 
							
								        are utilized to determine if there are changes had happened that
							 | 
						||
| 
								 | 
							
								        requires to discard the cache and re-updating it. The primary factors
							 | 
						||
| 
								 | 
							
								        are the arguments of `CCompilerOpt` and `CCompiler`'s properties(type, flags, etc).
							 | 
						||
| 
								 | 
							
								        Default is list of two items, containing the time of last modification
							 | 
						||
| 
								 | 
							
								        of `ccompiler_opt` and value of attribute "conf_noopt"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    conf_tmp_path : str,
							 | 
						||
| 
								 | 
							
								        The path of temporary directory. Default is auto-created
							 | 
						||
| 
								 | 
							
								        temporary directory via ``tempfile.mkdtemp()``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    conf_check_path : str
							 | 
						||
| 
								 | 
							
								        The path of testing files. Each added CPU feature must have a
							 | 
						||
| 
								 | 
							
								        **C** source file contains at least one intrinsic or instruction that
							 | 
						||
| 
								 | 
							
								        related to this feature, so it can be tested against the compiler.
							 | 
						||
| 
								 | 
							
								        Default is ``./distutils/checks``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    conf_target_groups : dict
							 | 
						||
| 
								 | 
							
								        Extra tokens that can be reached from dispatch-able sources through
							 | 
						||
| 
								 | 
							
								        the special mark ``@targets``. Default is an empty dictionary.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        **Notes**:
							 | 
						||
| 
								 | 
							
								            - case-insensitive for tokens and group names
							 | 
						||
| 
								 | 
							
								            - sign '#' must stick in the begin of group name and only within ``@targets``
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        **Example**:
							 | 
						||
| 
								 | 
							
								            .. code-block:: console
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                $ "@targets #avx_group other_tokens" > group_inside.c
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            >>> CCompilerOpt.conf_target_groups["avx_group"] = \\
							 | 
						||
| 
								 | 
							
								            "$werror $maxopt avx2 avx512f avx512_skx"
							 | 
						||
| 
								 | 
							
								            >>> cco = CCompilerOpt(cc_instance)
							 | 
						||
| 
								 | 
							
								            >>> cco.try_dispatch(["group_inside.c"])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    conf_c_prefix : str
							 | 
						||
| 
								 | 
							
								        The prefix of public C definitions. Default is ``"NPY_"``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    conf_c_prefix_ : str
							 | 
						||
| 
								 | 
							
								        The prefix of internal C definitions. Default is ``"NPY__"``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    conf_cc_flags : dict
							 | 
						||
| 
								 | 
							
								        Nested dictionaries defining several compiler flags
							 | 
						||
| 
								 | 
							
								        that linked to some major functions, the main key
							 | 
						||
| 
								 | 
							
								        represent the compiler name and sub-keys represent
							 | 
						||
| 
								 | 
							
								        flags names. Default is already covers all supported
							 | 
						||
| 
								 | 
							
								        **C** compilers.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Sub-keys explained as follows:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        "native": str or None
							 | 
						||
| 
								 | 
							
								            used by argument option `native`, to detect the current
							 | 
						||
| 
								 | 
							
								            machine support via the compiler.
							 | 
						||
| 
								 | 
							
								        "werror": str or None
							 | 
						||
| 
								 | 
							
								            utilized to treat warning as errors during testing CPU features
							 | 
						||
| 
								 | 
							
								            against the compiler and also for target's policy `$werror`
							 | 
						||
| 
								 | 
							
								            via dispatch-able sources.
							 | 
						||
| 
								 | 
							
								        "maxopt": str or None
							 | 
						||
| 
								 | 
							
								            utilized for target's policy '$maxopt' and the value should
							 | 
						||
| 
								 | 
							
								            contains the maximum acceptable optimization by the compiler.
							 | 
						||
| 
								 | 
							
								            e.g. in gcc `'-O3'`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        **Notes**:
							 | 
						||
| 
								 | 
							
								            * case-sensitive for compiler names and flags
							 | 
						||
| 
								 | 
							
								            * use space to separate multiple flags
							 | 
						||
| 
								 | 
							
								            * any flag will tested against the compiler and it will skipped
							 | 
						||
| 
								 | 
							
								              if it's not applicable.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    conf_min_features : dict
							 | 
						||
| 
								 | 
							
								        A dictionary defines the used CPU features for
							 | 
						||
| 
								 | 
							
								        argument option `'min'`, the key represent the CPU architecture
							 | 
						||
| 
								 | 
							
								        name e.g. `'x86'`. Default values provide the best effort
							 | 
						||
| 
								 | 
							
								        on wide range of users platforms.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        **Note**: case-sensitive for architecture names.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    conf_features : dict
							 | 
						||
| 
								 | 
							
								        Nested dictionaries used for identifying the CPU features.
							 | 
						||
| 
								 | 
							
								        the primary key is represented as a feature name or group name
							 | 
						||
| 
								 | 
							
								        that gathers several features. Default values covers all
							 | 
						||
| 
								 | 
							
								        supported features but without the major options like "flags",
							 | 
						||
| 
								 | 
							
								        these undefined options handle it by method `conf_features_partial()`.
							 | 
						||
| 
								 | 
							
								        Default value is covers almost all CPU features for *X86*, *IBM/Power64*
							 | 
						||
| 
								 | 
							
								        and *ARM 7/8*.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Sub-keys explained as follows:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        "implies" : str or list, optional,
							 | 
						||
| 
								 | 
							
								            List of CPU feature names to be implied by it,
							 | 
						||
| 
								 | 
							
								            the feature name must be defined within `conf_features`.
							 | 
						||
| 
								 | 
							
								            Default is None.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        "flags": str or list, optional
							 | 
						||
| 
								 | 
							
								            List of compiler flags. Default is None.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        "detect": str or list, optional
							 | 
						||
| 
								 | 
							
								            List of CPU feature names that required to be detected
							 | 
						||
| 
								 | 
							
								            in runtime. By default, its the feature name or features
							 | 
						||
| 
								 | 
							
								            in "group" if its specified.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        "implies_detect": bool, optional
							 | 
						||
| 
								 | 
							
								            If True, all "detect" of implied features will be combined.
							 | 
						||
| 
								 | 
							
								            Default is True. see `feature_detect()`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        "group": str or list, optional
							 | 
						||
| 
								 | 
							
								            Same as "implies" but doesn't require the feature name to be
							 | 
						||
| 
								 | 
							
								            defined within `conf_features`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        "interest": int, required
							 | 
						||
| 
								 | 
							
								            a key for sorting CPU features
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        "headers": str or list, optional
							 | 
						||
| 
								 | 
							
								            intrinsics C header file
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        "disable": str, optional
							 | 
						||
| 
								 | 
							
								            force disable feature, the string value should contains the
							 | 
						||
| 
								 | 
							
								            reason of disabling.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        "autovec": bool or None, optional
							 | 
						||
| 
								 | 
							
								            True or False to declare that CPU feature can be auto-vectorized
							 | 
						||
| 
								 | 
							
								            by the compiler.
							 | 
						||
| 
								 | 
							
								            By default(None), treated as True if the feature contains at
							 | 
						||
| 
								 | 
							
								            least one applicable flag. see `feature_can_autovec()`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        "extra_checks": str or list, optional
							 | 
						||
| 
								 | 
							
								            Extra test case names for the CPU feature that need to be tested
							 | 
						||
| 
								 | 
							
								            against the compiler.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            Each test case must have a C file named ``extra_xxxx.c``, where
							 | 
						||
| 
								 | 
							
								            ``xxxx`` is the case name in lower case, under 'conf_check_path'.
							 | 
						||
| 
								 | 
							
								            It should contain at least one intrinsic or function related to the test case.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            If the compiler able to successfully compile the C file then `CCompilerOpt`
							 | 
						||
| 
								 | 
							
								            will add a C ``#define`` for it into the main dispatch header, e.g.
							 | 
						||
| 
								 | 
							
								            ``#define {conf_c_prefix}_XXXX`` where ``XXXX`` is the case name in upper case.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        **NOTES**:
							 | 
						||
| 
								 | 
							
								            * space can be used as separator with options that supports "str or list"
							 | 
						||
| 
								 | 
							
								            * case-sensitive for all values and feature name must be in upper-case.
							 | 
						||
| 
								 | 
							
								            * if flags aren't applicable, its will skipped rather than disable the
							 | 
						||
| 
								 | 
							
								              CPU feature
							 | 
						||
| 
								 | 
							
								            * the CPU feature will disabled if the compiler fail to compile
							 | 
						||
| 
								 | 
							
								              the test file
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    conf_nocache = False
							 | 
						||
| 
								 | 
							
								    conf_noopt = False
							 | 
						||
| 
								 | 
							
								    conf_cache_factors = None
							 | 
						||
| 
								 | 
							
								    conf_tmp_path = None
							 | 
						||
| 
								 | 
							
								    conf_check_path = os.path.join(
							 | 
						||
| 
								 | 
							
								        os.path.dirname(os.path.realpath(__file__)), "checks"
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    conf_target_groups = {}
							 | 
						||
| 
								 | 
							
								    conf_c_prefix = 'NPY_'
							 | 
						||
| 
								 | 
							
								    conf_c_prefix_ = 'NPY__'
							 | 
						||
| 
								 | 
							
								    conf_cc_flags = dict(
							 | 
						||
| 
								 | 
							
								        gcc = dict(
							 | 
						||
| 
								 | 
							
								            # native should always fail on arm and ppc64,
							 | 
						||
| 
								 | 
							
								            # native usually works only with x86
							 | 
						||
| 
								 | 
							
								            native = '-march=native',
							 | 
						||
| 
								 | 
							
								            opt = '-O3',
							 | 
						||
| 
								 | 
							
								            werror = '-Werror',
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        clang = dict(
							 | 
						||
| 
								 | 
							
								            native = '-march=native',
							 | 
						||
| 
								 | 
							
								            opt = "-O3",
							 | 
						||
| 
								 | 
							
								            # One of the following flags needs to be applicable for Clang to
							 | 
						||
| 
								 | 
							
								            # guarantee the sanity of the testing process, however in certain
							 | 
						||
| 
								 | 
							
								            # cases `-Werror` gets skipped during the availability test due to
							 | 
						||
| 
								 | 
							
								            # "unused arguments" warnings.
							 | 
						||
| 
								 | 
							
								            # see https://github.com/numpy/numpy/issues/19624
							 | 
						||
| 
								 | 
							
								            werror = '-Werror=switch -Werror',
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        icc = dict(
							 | 
						||
| 
								 | 
							
								            native = '-xHost',
							 | 
						||
| 
								 | 
							
								            opt = '-O3',
							 | 
						||
| 
								 | 
							
								            werror = '-Werror',
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        iccw = dict(
							 | 
						||
| 
								 | 
							
								            native = '/QxHost',
							 | 
						||
| 
								 | 
							
								            opt = '/O3',
							 | 
						||
| 
								 | 
							
								            werror = '/Werror',
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        msvc = dict(
							 | 
						||
| 
								 | 
							
								            native = None,
							 | 
						||
| 
								 | 
							
								            opt = '/O2',
							 | 
						||
| 
								 | 
							
								            werror = '/WX',
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    conf_min_features = dict(
							 | 
						||
| 
								 | 
							
								        x86 = "SSE SSE2",
							 | 
						||
| 
								 | 
							
								        x64 = "SSE SSE2 SSE3",
							 | 
						||
| 
								 | 
							
								        ppc64 = '', # play it safe
							 | 
						||
| 
								 | 
							
								        ppc64le = "VSX VSX2",
							 | 
						||
| 
								 | 
							
								        s390x = '',
							 | 
						||
| 
								 | 
							
								        armhf = '', # play it safe
							 | 
						||
| 
								 | 
							
								        aarch64 = "NEON NEON_FP16 NEON_VFPV4 ASIMD"
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    conf_features = dict(
							 | 
						||
| 
								 | 
							
								        # X86
							 | 
						||
| 
								 | 
							
								        SSE = dict(
							 | 
						||
| 
								 | 
							
								            interest=1, headers="xmmintrin.h",
							 | 
						||
| 
								 | 
							
								            # enabling SSE without SSE2 is useless also
							 | 
						||
| 
								 | 
							
								            # it's non-optional for x86_64
							 | 
						||
| 
								 | 
							
								            implies="SSE2"
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        SSE2   = dict(interest=2, implies="SSE", headers="emmintrin.h"),
							 | 
						||
| 
								 | 
							
								        SSE3   = dict(interest=3, implies="SSE2", headers="pmmintrin.h"),
							 | 
						||
| 
								 | 
							
								        SSSE3  = dict(interest=4, implies="SSE3", headers="tmmintrin.h"),
							 | 
						||
| 
								 | 
							
								        SSE41  = dict(interest=5, implies="SSSE3", headers="smmintrin.h"),
							 | 
						||
| 
								 | 
							
								        POPCNT = dict(interest=6, implies="SSE41", headers="popcntintrin.h"),
							 | 
						||
| 
								 | 
							
								        SSE42  = dict(interest=7, implies="POPCNT"),
							 | 
						||
| 
								 | 
							
								        AVX    = dict(
							 | 
						||
| 
								 | 
							
								            interest=8, implies="SSE42", headers="immintrin.h",
							 | 
						||
| 
								 | 
							
								            implies_detect=False
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        XOP    = dict(interest=9, implies="AVX", headers="x86intrin.h"),
							 | 
						||
| 
								 | 
							
								        FMA4   = dict(interest=10, implies="AVX", headers="x86intrin.h"),
							 | 
						||
| 
								 | 
							
								        F16C   = dict(interest=11, implies="AVX"),
							 | 
						||
| 
								 | 
							
								        FMA3   = dict(interest=12, implies="F16C"),
							 | 
						||
| 
								 | 
							
								        AVX2   = dict(interest=13, implies="F16C"),
							 | 
						||
| 
								 | 
							
								        AVX512F = dict(
							 | 
						||
| 
								 | 
							
								            interest=20, implies="FMA3 AVX2", implies_detect=False,
							 | 
						||
| 
								 | 
							
								            extra_checks="AVX512F_REDUCE"
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        AVX512CD = dict(interest=21, implies="AVX512F"),
							 | 
						||
| 
								 | 
							
								        AVX512_KNL = dict(
							 | 
						||
| 
								 | 
							
								            interest=40, implies="AVX512CD", group="AVX512ER AVX512PF",
							 | 
						||
| 
								 | 
							
								            detect="AVX512_KNL", implies_detect=False
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        AVX512_KNM = dict(
							 | 
						||
| 
								 | 
							
								            interest=41, implies="AVX512_KNL",
							 | 
						||
| 
								 | 
							
								            group="AVX5124FMAPS AVX5124VNNIW AVX512VPOPCNTDQ",
							 | 
						||
| 
								 | 
							
								            detect="AVX512_KNM", implies_detect=False
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        AVX512_SKX = dict(
							 | 
						||
| 
								 | 
							
								            interest=42, implies="AVX512CD", group="AVX512VL AVX512BW AVX512DQ",
							 | 
						||
| 
								 | 
							
								            detect="AVX512_SKX", implies_detect=False,
							 | 
						||
| 
								 | 
							
								            extra_checks="AVX512BW_MASK AVX512DQ_MASK"
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        AVX512_CLX = dict(
							 | 
						||
| 
								 | 
							
								            interest=43, implies="AVX512_SKX", group="AVX512VNNI",
							 | 
						||
| 
								 | 
							
								            detect="AVX512_CLX"
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        AVX512_CNL = dict(
							 | 
						||
| 
								 | 
							
								            interest=44, implies="AVX512_SKX", group="AVX512IFMA AVX512VBMI",
							 | 
						||
| 
								 | 
							
								            detect="AVX512_CNL", implies_detect=False
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        AVX512_ICL = dict(
							 | 
						||
| 
								 | 
							
								            interest=45, implies="AVX512_CLX AVX512_CNL",
							 | 
						||
| 
								 | 
							
								            group="AVX512VBMI2 AVX512BITALG AVX512VPOPCNTDQ",
							 | 
						||
| 
								 | 
							
								            detect="AVX512_ICL", implies_detect=False
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        # IBM/Power
							 | 
						||
| 
								 | 
							
								        ## Power7/ISA 2.06
							 | 
						||
| 
								 | 
							
								        VSX = dict(interest=1, headers="altivec.h", extra_checks="VSX_ASM"),
							 | 
						||
| 
								 | 
							
								        ## Power8/ISA 2.07
							 | 
						||
| 
								 | 
							
								        VSX2 = dict(interest=2, implies="VSX", implies_detect=False),
							 | 
						||
| 
								 | 
							
								        ## Power9/ISA 3.00
							 | 
						||
| 
								 | 
							
								        VSX3 = dict(interest=3, implies="VSX2", implies_detect=False),
							 | 
						||
| 
								 | 
							
								        ## Power10/ISA 3.1
							 | 
						||
| 
								 | 
							
								        VSX4 = dict(interest=4, implies="VSX3", implies_detect=False,
							 | 
						||
| 
								 | 
							
								                    extra_checks="VSX4_MMA"),
							 | 
						||
| 
								 | 
							
								        # IBM/Z
							 | 
						||
| 
								 | 
							
								        ## VX(z13) support
							 | 
						||
| 
								 | 
							
								        VX = dict(interest=1, headers="vecintrin.h"),
							 | 
						||
| 
								 | 
							
								        ## Vector-Enhancements Facility
							 | 
						||
| 
								 | 
							
								        VXE = dict(interest=2, implies="VX", implies_detect=False),
							 | 
						||
| 
								 | 
							
								        ## Vector-Enhancements Facility 2
							 | 
						||
| 
								 | 
							
								        VXE2 = dict(interest=3, implies="VXE", implies_detect=False),
							 | 
						||
| 
								 | 
							
								        # ARM
							 | 
						||
| 
								 | 
							
								        NEON  = dict(interest=1, headers="arm_neon.h"),
							 | 
						||
| 
								 | 
							
								        NEON_FP16 = dict(interest=2, implies="NEON"),
							 | 
						||
| 
								 | 
							
								        ## FMA
							 | 
						||
| 
								 | 
							
								        NEON_VFPV4 = dict(interest=3, implies="NEON_FP16"),
							 | 
						||
| 
								 | 
							
								        ## Advanced SIMD
							 | 
						||
| 
								 | 
							
								        ASIMD = dict(interest=4, implies="NEON_FP16 NEON_VFPV4", implies_detect=False),
							 | 
						||
| 
								 | 
							
								        ## ARMv8.2 half-precision & vector arithm
							 | 
						||
| 
								 | 
							
								        ASIMDHP = dict(interest=5, implies="ASIMD"),
							 | 
						||
| 
								 | 
							
								        ## ARMv8.2 dot product
							 | 
						||
| 
								 | 
							
								        ASIMDDP = dict(interest=6, implies="ASIMD"),
							 | 
						||
| 
								 | 
							
								        ## ARMv8.2 Single & half-precision Multiply
							 | 
						||
| 
								 | 
							
								        ASIMDFHM = dict(interest=7, implies="ASIMDHP"),
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    def conf_features_partial(self):
							 | 
						||
| 
								 | 
							
								        """Return a dictionary of supported CPU features by the platform,
							 | 
						||
| 
								 | 
							
								        and accumulate the rest of undefined options in `conf_features`,
							 | 
						||
| 
								 | 
							
								        the returned dict has same rules and notes in
							 | 
						||
| 
								 | 
							
								        class attribute `conf_features`, also its override
							 | 
						||
| 
								 | 
							
								        any options that been set in 'conf_features'.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        if self.cc_noopt:
							 | 
						||
| 
								 | 
							
								            # optimization is disabled
							 | 
						||
| 
								 | 
							
								            return {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        on_x86 = self.cc_on_x86 or self.cc_on_x64
							 | 
						||
| 
								 | 
							
								        is_unix = self.cc_is_gcc or self.cc_is_clang
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if on_x86 and is_unix: return dict(
							 | 
						||
| 
								 | 
							
								            SSE    = dict(flags="-msse"),
							 | 
						||
| 
								 | 
							
								            SSE2   = dict(flags="-msse2"),
							 | 
						||
| 
								 | 
							
								            SSE3   = dict(flags="-msse3"),
							 | 
						||
| 
								 | 
							
								            SSSE3  = dict(flags="-mssse3"),
							 | 
						||
| 
								 | 
							
								            SSE41  = dict(flags="-msse4.1"),
							 | 
						||
| 
								 | 
							
								            POPCNT = dict(flags="-mpopcnt"),
							 | 
						||
| 
								 | 
							
								            SSE42  = dict(flags="-msse4.2"),
							 | 
						||
| 
								 | 
							
								            AVX    = dict(flags="-mavx"),
							 | 
						||
| 
								 | 
							
								            F16C   = dict(flags="-mf16c"),
							 | 
						||
| 
								 | 
							
								            XOP    = dict(flags="-mxop"),
							 | 
						||
| 
								 | 
							
								            FMA4   = dict(flags="-mfma4"),
							 | 
						||
| 
								 | 
							
								            FMA3   = dict(flags="-mfma"),
							 | 
						||
| 
								 | 
							
								            AVX2   = dict(flags="-mavx2"),
							 | 
						||
| 
								 | 
							
								            AVX512F = dict(flags="-mavx512f -mno-mmx"),
							 | 
						||
| 
								 | 
							
								            AVX512CD = dict(flags="-mavx512cd"),
							 | 
						||
| 
								 | 
							
								            AVX512_KNL = dict(flags="-mavx512er -mavx512pf"),
							 | 
						||
| 
								 | 
							
								            AVX512_KNM = dict(
							 | 
						||
| 
								 | 
							
								                flags="-mavx5124fmaps -mavx5124vnniw -mavx512vpopcntdq"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            AVX512_SKX = dict(flags="-mavx512vl -mavx512bw -mavx512dq"),
							 | 
						||
| 
								 | 
							
								            AVX512_CLX = dict(flags="-mavx512vnni"),
							 | 
						||
| 
								 | 
							
								            AVX512_CNL = dict(flags="-mavx512ifma -mavx512vbmi"),
							 | 
						||
| 
								 | 
							
								            AVX512_ICL = dict(
							 | 
						||
| 
								 | 
							
								                flags="-mavx512vbmi2 -mavx512bitalg -mavx512vpopcntdq"
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        if on_x86 and self.cc_is_icc: return dict(
							 | 
						||
| 
								 | 
							
								            SSE    = dict(flags="-msse"),
							 | 
						||
| 
								 | 
							
								            SSE2   = dict(flags="-msse2"),
							 | 
						||
| 
								 | 
							
								            SSE3   = dict(flags="-msse3"),
							 | 
						||
| 
								 | 
							
								            SSSE3  = dict(flags="-mssse3"),
							 | 
						||
| 
								 | 
							
								            SSE41  = dict(flags="-msse4.1"),
							 | 
						||
| 
								 | 
							
								            POPCNT = {},
							 | 
						||
| 
								 | 
							
								            SSE42  = dict(flags="-msse4.2"),
							 | 
						||
| 
								 | 
							
								            AVX    = dict(flags="-mavx"),
							 | 
						||
| 
								 | 
							
								            F16C   = {},
							 | 
						||
| 
								 | 
							
								            XOP    = dict(disable="Intel Compiler doesn't support it"),
							 | 
						||
| 
								 | 
							
								            FMA4   = dict(disable="Intel Compiler doesn't support it"),
							 | 
						||
| 
								 | 
							
								            # Intel Compiler doesn't support AVX2 or FMA3 independently
							 | 
						||
| 
								 | 
							
								            FMA3 = dict(
							 | 
						||
| 
								 | 
							
								                implies="F16C AVX2", flags="-march=core-avx2"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            AVX2 = dict(implies="FMA3", flags="-march=core-avx2"),
							 | 
						||
| 
								 | 
							
								            # Intel Compiler doesn't support AVX512F or AVX512CD independently
							 | 
						||
| 
								 | 
							
								            AVX512F = dict(
							 | 
						||
| 
								 | 
							
								                implies="AVX2 AVX512CD", flags="-march=common-avx512"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            AVX512CD = dict(
							 | 
						||
| 
								 | 
							
								                implies="AVX2 AVX512F", flags="-march=common-avx512"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            AVX512_KNL = dict(flags="-xKNL"),
							 | 
						||
| 
								 | 
							
								            AVX512_KNM = dict(flags="-xKNM"),
							 | 
						||
| 
								 | 
							
								            AVX512_SKX = dict(flags="-xSKYLAKE-AVX512"),
							 | 
						||
| 
								 | 
							
								            AVX512_CLX = dict(flags="-xCASCADELAKE"),
							 | 
						||
| 
								 | 
							
								            AVX512_CNL = dict(flags="-xCANNONLAKE"),
							 | 
						||
| 
								 | 
							
								            AVX512_ICL = dict(flags="-xICELAKE-CLIENT"),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        if on_x86 and self.cc_is_iccw: return dict(
							 | 
						||
| 
								 | 
							
								            SSE    = dict(flags="/arch:SSE"),
							 | 
						||
| 
								 | 
							
								            SSE2   = dict(flags="/arch:SSE2"),
							 | 
						||
| 
								 | 
							
								            SSE3   = dict(flags="/arch:SSE3"),
							 | 
						||
| 
								 | 
							
								            SSSE3  = dict(flags="/arch:SSSE3"),
							 | 
						||
| 
								 | 
							
								            SSE41  = dict(flags="/arch:SSE4.1"),
							 | 
						||
| 
								 | 
							
								            POPCNT = {},
							 | 
						||
| 
								 | 
							
								            SSE42  = dict(flags="/arch:SSE4.2"),
							 | 
						||
| 
								 | 
							
								            AVX    = dict(flags="/arch:AVX"),
							 | 
						||
| 
								 | 
							
								            F16C   = {},
							 | 
						||
| 
								 | 
							
								            XOP    = dict(disable="Intel Compiler doesn't support it"),
							 | 
						||
| 
								 | 
							
								            FMA4   = dict(disable="Intel Compiler doesn't support it"),
							 | 
						||
| 
								 | 
							
								            # Intel Compiler doesn't support FMA3 or AVX2 independently
							 | 
						||
| 
								 | 
							
								            FMA3 = dict(
							 | 
						||
| 
								 | 
							
								                implies="F16C AVX2", flags="/arch:CORE-AVX2"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            AVX2 = dict(
							 | 
						||
| 
								 | 
							
								                implies="FMA3", flags="/arch:CORE-AVX2"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            # Intel Compiler doesn't support AVX512F or AVX512CD independently
							 | 
						||
| 
								 | 
							
								            AVX512F = dict(
							 | 
						||
| 
								 | 
							
								                implies="AVX2 AVX512CD", flags="/Qx:COMMON-AVX512"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            AVX512CD = dict(
							 | 
						||
| 
								 | 
							
								                implies="AVX2 AVX512F", flags="/Qx:COMMON-AVX512"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            AVX512_KNL = dict(flags="/Qx:KNL"),
							 | 
						||
| 
								 | 
							
								            AVX512_KNM = dict(flags="/Qx:KNM"),
							 | 
						||
| 
								 | 
							
								            AVX512_SKX = dict(flags="/Qx:SKYLAKE-AVX512"),
							 | 
						||
| 
								 | 
							
								            AVX512_CLX = dict(flags="/Qx:CASCADELAKE"),
							 | 
						||
| 
								 | 
							
								            AVX512_CNL = dict(flags="/Qx:CANNONLAKE"),
							 | 
						||
| 
								 | 
							
								            AVX512_ICL = dict(flags="/Qx:ICELAKE-CLIENT")
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        if on_x86 and self.cc_is_msvc: return dict(
							 | 
						||
| 
								 | 
							
								            SSE = dict(flags="/arch:SSE") if self.cc_on_x86 else {},
							 | 
						||
| 
								 | 
							
								            SSE2 = dict(flags="/arch:SSE2") if self.cc_on_x86 else {},
							 | 
						||
| 
								 | 
							
								            SSE3   = {},
							 | 
						||
| 
								 | 
							
								            SSSE3  = {},
							 | 
						||
| 
								 | 
							
								            SSE41  = {},
							 | 
						||
| 
								 | 
							
								            POPCNT = dict(headers="nmmintrin.h"),
							 | 
						||
| 
								 | 
							
								            SSE42  = {},
							 | 
						||
| 
								 | 
							
								            AVX    = dict(flags="/arch:AVX"),
							 | 
						||
| 
								 | 
							
								            F16C   = {},
							 | 
						||
| 
								 | 
							
								            XOP    = dict(headers="ammintrin.h"),
							 | 
						||
| 
								 | 
							
								            FMA4   = dict(headers="ammintrin.h"),
							 | 
						||
| 
								 | 
							
								            # MSVC doesn't support FMA3 or AVX2 independently
							 | 
						||
| 
								 | 
							
								            FMA3 = dict(
							 | 
						||
| 
								 | 
							
								                implies="F16C AVX2", flags="/arch:AVX2"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            AVX2 = dict(
							 | 
						||
| 
								 | 
							
								                implies="F16C FMA3", flags="/arch:AVX2"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            # MSVC doesn't support AVX512F or AVX512CD independently,
							 | 
						||
| 
								 | 
							
								            # always generate instructions belong to (VL/VW/DQ)
							 | 
						||
| 
								 | 
							
								            AVX512F = dict(
							 | 
						||
| 
								 | 
							
								                implies="AVX2 AVX512CD AVX512_SKX", flags="/arch:AVX512"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            AVX512CD = dict(
							 | 
						||
| 
								 | 
							
								                implies="AVX512F AVX512_SKX", flags="/arch:AVX512"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            AVX512_KNL = dict(
							 | 
						||
| 
								 | 
							
								                disable="MSVC compiler doesn't support it"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            AVX512_KNM = dict(
							 | 
						||
| 
								 | 
							
								                disable="MSVC compiler doesn't support it"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            AVX512_SKX = dict(flags="/arch:AVX512"),
							 | 
						||
| 
								 | 
							
								            AVX512_CLX = {},
							 | 
						||
| 
								 | 
							
								            AVX512_CNL = {},
							 | 
						||
| 
								 | 
							
								            AVX512_ICL = {}
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        on_power = self.cc_on_ppc64le or self.cc_on_ppc64
							 | 
						||
| 
								 | 
							
								        if on_power:
							 | 
						||
| 
								 | 
							
								            partial = dict(
							 | 
						||
| 
								 | 
							
								                VSX = dict(
							 | 
						||
| 
								 | 
							
								                    implies=("VSX2" if self.cc_on_ppc64le else ""),
							 | 
						||
| 
								 | 
							
								                    flags="-mvsx"
							 | 
						||
| 
								 | 
							
								                ),
							 | 
						||
| 
								 | 
							
								                VSX2 = dict(
							 | 
						||
| 
								 | 
							
								                    flags="-mcpu=power8", implies_detect=False
							 | 
						||
| 
								 | 
							
								                ),
							 | 
						||
| 
								 | 
							
								                VSX3 = dict(
							 | 
						||
| 
								 | 
							
								                    flags="-mcpu=power9 -mtune=power9", implies_detect=False
							 | 
						||
| 
								 | 
							
								                ),
							 | 
						||
| 
								 | 
							
								                VSX4 = dict(
							 | 
						||
| 
								 | 
							
								                    flags="-mcpu=power10 -mtune=power10", implies_detect=False
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            if self.cc_is_clang:
							 | 
						||
| 
								 | 
							
								                partial["VSX"]["flags"]  = "-maltivec -mvsx"
							 | 
						||
| 
								 | 
							
								                partial["VSX2"]["flags"] = "-mpower8-vector"
							 | 
						||
| 
								 | 
							
								                partial["VSX3"]["flags"] = "-mpower9-vector"
							 | 
						||
| 
								 | 
							
								                partial["VSX4"]["flags"] = "-mpower10-vector"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return partial
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        on_zarch = self.cc_on_s390x
							 | 
						||
| 
								 | 
							
								        if on_zarch:
							 | 
						||
| 
								 | 
							
								            partial = dict(
							 | 
						||
| 
								 | 
							
								                VX = dict(
							 | 
						||
| 
								 | 
							
								                    flags="-march=arch11 -mzvector"
							 | 
						||
| 
								 | 
							
								                ),
							 | 
						||
| 
								 | 
							
								                VXE = dict(
							 | 
						||
| 
								 | 
							
								                    flags="-march=arch12", implies_detect=False
							 | 
						||
| 
								 | 
							
								                ),
							 | 
						||
| 
								 | 
							
								                VXE2 = dict(
							 | 
						||
| 
								 | 
							
								                    flags="-march=arch13", implies_detect=False
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return partial
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.cc_on_aarch64 and is_unix: return dict(
							 | 
						||
| 
								 | 
							
								            NEON = dict(
							 | 
						||
| 
								 | 
							
								                implies="NEON_FP16 NEON_VFPV4 ASIMD", autovec=True
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            NEON_FP16 = dict(
							 | 
						||
| 
								 | 
							
								                implies="NEON NEON_VFPV4 ASIMD", autovec=True
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            NEON_VFPV4 = dict(
							 | 
						||
| 
								 | 
							
								                implies="NEON NEON_FP16 ASIMD", autovec=True
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            ASIMD = dict(
							 | 
						||
| 
								 | 
							
								                implies="NEON NEON_FP16 NEON_VFPV4", autovec=True
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            ASIMDHP = dict(
							 | 
						||
| 
								 | 
							
								                flags="-march=armv8.2-a+fp16"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            ASIMDDP = dict(
							 | 
						||
| 
								 | 
							
								                flags="-march=armv8.2-a+dotprod"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            ASIMDFHM = dict(
							 | 
						||
| 
								 | 
							
								                flags="-march=armv8.2-a+fp16fml"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        if self.cc_on_armhf and is_unix: return dict(
							 | 
						||
| 
								 | 
							
								            NEON = dict(
							 | 
						||
| 
								 | 
							
								                flags="-mfpu=neon"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            NEON_FP16 = dict(
							 | 
						||
| 
								 | 
							
								                flags="-mfpu=neon-fp16 -mfp16-format=ieee"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            NEON_VFPV4 = dict(
							 | 
						||
| 
								 | 
							
								                flags="-mfpu=neon-vfpv4",
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            ASIMD = dict(
							 | 
						||
| 
								 | 
							
								                flags="-mfpu=neon-fp-armv8 -march=armv8-a+simd",
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            ASIMDHP = dict(
							 | 
						||
| 
								 | 
							
								                flags="-march=armv8.2-a+fp16"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            ASIMDDP = dict(
							 | 
						||
| 
								 | 
							
								                flags="-march=armv8.2-a+dotprod",
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            ASIMDFHM = dict(
							 | 
						||
| 
								 | 
							
								                flags="-march=armv8.2-a+fp16fml"
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        # TODO: ARM MSVC
							 | 
						||
| 
								 | 
							
								        return {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self):
							 | 
						||
| 
								 | 
							
								        if self.conf_tmp_path is None:
							 | 
						||
| 
								 | 
							
								            import shutil
							 | 
						||
| 
								 | 
							
								            import tempfile
							 | 
						||
| 
								 | 
							
								            tmp = tempfile.mkdtemp()
							 | 
						||
| 
								 | 
							
								            def rm_temp():
							 | 
						||
| 
								 | 
							
								                try:
							 | 
						||
| 
								 | 
							
								                    shutil.rmtree(tmp)
							 | 
						||
| 
								 | 
							
								                except OSError:
							 | 
						||
| 
								 | 
							
								                    pass
							 | 
						||
| 
								 | 
							
								            atexit.register(rm_temp)
							 | 
						||
| 
								 | 
							
								            self.conf_tmp_path = tmp
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.conf_cache_factors is None:
							 | 
						||
| 
								 | 
							
								            self.conf_cache_factors = [
							 | 
						||
| 
								 | 
							
								                os.path.getmtime(__file__),
							 | 
						||
| 
								 | 
							
								                self.conf_nocache
							 | 
						||
| 
								 | 
							
								            ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _Distutils:
							 | 
						||
| 
								 | 
							
								    """A helper class that provides a collection of fundamental methods
							 | 
						||
| 
								 | 
							
								    implemented in a top of Python and NumPy Distutils.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The idea behind this class is to gather all methods that it may
							 | 
						||
| 
								 | 
							
								    need to override in case of reuse 'CCompilerOpt' in environment
							 | 
						||
| 
								 | 
							
								    different than of what NumPy has.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Parameters
							 | 
						||
| 
								 | 
							
								    ----------
							 | 
						||
| 
								 | 
							
								    ccompiler : `CCompiler`
							 | 
						||
| 
								 | 
							
								        The generate instance that returned from `distutils.ccompiler.new_compiler()`.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def __init__(self, ccompiler):
							 | 
						||
| 
								 | 
							
								        self._ccompiler = ccompiler
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def dist_compile(self, sources, flags, ccompiler=None, **kwargs):
							 | 
						||
| 
								 | 
							
								        """Wrap CCompiler.compile()"""
							 | 
						||
| 
								 | 
							
								        assert(isinstance(sources, list))
							 | 
						||
| 
								 | 
							
								        assert(isinstance(flags, list))
							 | 
						||
| 
								 | 
							
								        flags = kwargs.pop("extra_postargs", []) + flags
							 | 
						||
| 
								 | 
							
								        if not ccompiler:
							 | 
						||
| 
								 | 
							
								            ccompiler = self._ccompiler
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return ccompiler.compile(sources, extra_postargs=flags, **kwargs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def dist_test(self, source, flags, macros=[]):
							 | 
						||
| 
								 | 
							
								        """Return True if 'CCompiler.compile()' able to compile
							 | 
						||
| 
								 | 
							
								        a source file with certain flags.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        assert(isinstance(source, str))
							 | 
						||
| 
								 | 
							
								        from distutils.errors import CompileError
							 | 
						||
| 
								 | 
							
								        cc = self._ccompiler;
							 | 
						||
| 
								 | 
							
								        bk_spawn = getattr(cc, 'spawn', None)
							 | 
						||
| 
								 | 
							
								        if bk_spawn:
							 | 
						||
| 
								 | 
							
								            cc_type = getattr(self._ccompiler, "compiler_type", "")
							 | 
						||
| 
								 | 
							
								            if cc_type in ("msvc",):
							 | 
						||
| 
								 | 
							
								                setattr(cc, 'spawn', self._dist_test_spawn_paths)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                setattr(cc, 'spawn', self._dist_test_spawn)
							 | 
						||
| 
								 | 
							
								        test = False
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            self.dist_compile(
							 | 
						||
| 
								 | 
							
								                [source], flags, macros=macros, output_dir=self.conf_tmp_path
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            test = True
							 | 
						||
| 
								 | 
							
								        except CompileError as e:
							 | 
						||
| 
								 | 
							
								            self.dist_log(str(e), stderr=True)
							 | 
						||
| 
								 | 
							
								        if bk_spawn:
							 | 
						||
| 
								 | 
							
								            setattr(cc, 'spawn', bk_spawn)
							 | 
						||
| 
								 | 
							
								        return test
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def dist_info(self):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Return a tuple containing info about (platform, compiler, extra_args),
							 | 
						||
| 
								 | 
							
								        required by the abstract class '_CCompiler' for discovering the
							 | 
						||
| 
								 | 
							
								        platform environment. This is also used as a cache factor in order
							 | 
						||
| 
								 | 
							
								        to detect any changes happening from outside.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        if hasattr(self, "_dist_info"):
							 | 
						||
| 
								 | 
							
								            return self._dist_info
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cc_type = getattr(self._ccompiler, "compiler_type", '')
							 | 
						||
| 
								 | 
							
								        if cc_type in ("intelem", "intelemw"):
							 | 
						||
| 
								 | 
							
								            platform = "x86_64"
							 | 
						||
| 
								 | 
							
								        elif cc_type in ("intel", "intelw", "intele"):
							 | 
						||
| 
								 | 
							
								            platform = "x86"
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            from distutils.util import get_platform
							 | 
						||
| 
								 | 
							
								            platform = get_platform()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cc_info = getattr(self._ccompiler, "compiler", getattr(self._ccompiler, "compiler_so", ''))
							 | 
						||
| 
								 | 
							
								        if not cc_type or cc_type == "unix":
							 | 
						||
| 
								 | 
							
								            if hasattr(cc_info, "__iter__"):
							 | 
						||
| 
								 | 
							
								                compiler = cc_info[0]
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                compiler = str(cc_info)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            compiler = cc_type
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if hasattr(cc_info, "__iter__") and len(cc_info) > 1:
							 | 
						||
| 
								 | 
							
								            extra_args = ' '.join(cc_info[1:])
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            extra_args  = os.environ.get("CFLAGS", "")
							 | 
						||
| 
								 | 
							
								            extra_args += os.environ.get("CPPFLAGS", "")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._dist_info = (platform, compiler, extra_args)
							 | 
						||
| 
								 | 
							
								        return self._dist_info
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def dist_error(*args):
							 | 
						||
| 
								 | 
							
								        """Raise a compiler error"""
							 | 
						||
| 
								 | 
							
								        from distutils.errors import CompileError
							 | 
						||
| 
								 | 
							
								        raise CompileError(_Distutils._dist_str(*args))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def dist_fatal(*args):
							 | 
						||
| 
								 | 
							
								        """Raise a distutils error"""
							 | 
						||
| 
								 | 
							
								        from distutils.errors import DistutilsError
							 | 
						||
| 
								 | 
							
								        raise DistutilsError(_Distutils._dist_str(*args))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def dist_log(*args, stderr=False):
							 | 
						||
| 
								 | 
							
								        """Print a console message"""
							 | 
						||
| 
								 | 
							
								        from numpy.distutils import log
							 | 
						||
| 
								 | 
							
								        out = _Distutils._dist_str(*args)
							 | 
						||
| 
								 | 
							
								        if stderr:
							 | 
						||
| 
								 | 
							
								            log.warn(out)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            log.info(out)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def dist_load_module(name, path):
							 | 
						||
| 
								 | 
							
								        """Load a module from file, required by the abstract class '_Cache'."""
							 | 
						||
| 
								 | 
							
								        from .misc_util import exec_mod_from_location
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            return exec_mod_from_location(name, path)
							 | 
						||
| 
								 | 
							
								        except Exception as e:
							 | 
						||
| 
								 | 
							
								            _Distutils.dist_log(e, stderr=True)
							 | 
						||
| 
								 | 
							
								        return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def _dist_str(*args):
							 | 
						||
| 
								 | 
							
								        """Return a string to print by log and errors."""
							 | 
						||
| 
								 | 
							
								        def to_str(arg):
							 | 
						||
| 
								 | 
							
								            if not isinstance(arg, str) and hasattr(arg, '__iter__'):
							 | 
						||
| 
								 | 
							
								                ret = []
							 | 
						||
| 
								 | 
							
								                for a in arg:
							 | 
						||
| 
								 | 
							
								                    ret.append(to_str(a))
							 | 
						||
| 
								 | 
							
								                return '('+ ' '.join(ret) + ')'
							 | 
						||
| 
								 | 
							
								            return str(arg)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        stack = inspect.stack()[2]
							 | 
						||
| 
								 | 
							
								        start = "CCompilerOpt.%s[%d] : " % (stack.function, stack.lineno)
							 | 
						||
| 
								 | 
							
								        out = ' '.join([
							 | 
						||
| 
								 | 
							
								            to_str(a)
							 | 
						||
| 
								 | 
							
								            for a in (*args,)
							 | 
						||
| 
								 | 
							
								        ])
							 | 
						||
| 
								 | 
							
								        return start + out
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _dist_test_spawn_paths(self, cmd, display=None):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Fix msvc SDK ENV path same as distutils do
							 | 
						||
| 
								 | 
							
								        without it we get c1: fatal error C1356: unable to find mspdbcore.dll
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        if not hasattr(self._ccompiler, "_paths"):
							 | 
						||
| 
								 | 
							
								            self._dist_test_spawn(cmd)
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								        old_path = os.getenv("path")
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            os.environ["path"] = self._ccompiler._paths
							 | 
						||
| 
								 | 
							
								            self._dist_test_spawn(cmd)
							 | 
						||
| 
								 | 
							
								        finally:
							 | 
						||
| 
								 | 
							
								            os.environ["path"] = old_path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _dist_warn_regex = re.compile(
							 | 
						||
| 
								 | 
							
								        # intel and msvc compilers don't raise
							 | 
						||
| 
								 | 
							
								        # fatal errors when flags are wrong or unsupported
							 | 
						||
| 
								 | 
							
								        ".*("
							 | 
						||
| 
								 | 
							
								        "warning D9002|"  # msvc, it should be work with any language.
							 | 
						||
| 
								 | 
							
								        "invalid argument for option" # intel
							 | 
						||
| 
								 | 
							
								        ").*"
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def _dist_test_spawn(cmd, display=None):
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            o = subprocess.check_output(cmd, stderr=subprocess.STDOUT,
							 | 
						||
| 
								 | 
							
								                                        text=True)
							 | 
						||
| 
								 | 
							
								            if o and re.match(_Distutils._dist_warn_regex, o):
							 | 
						||
| 
								 | 
							
								                _Distutils.dist_error(
							 | 
						||
| 
								 | 
							
								                    "Flags in command", cmd ,"aren't supported by the compiler"
							 | 
						||
| 
								 | 
							
								                    ", output -> \n%s" % o
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								        except subprocess.CalledProcessError as exc:
							 | 
						||
| 
								 | 
							
								            o = exc.output
							 | 
						||
| 
								 | 
							
								            s = exc.returncode
							 | 
						||
| 
								 | 
							
								        except OSError as e:
							 | 
						||
| 
								 | 
							
								            o = e
							 | 
						||
| 
								 | 
							
								            s = 127
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								        _Distutils.dist_error(
							 | 
						||
| 
								 | 
							
								            "Command", cmd, "failed with exit status %d output -> \n%s" % (
							 | 
						||
| 
								 | 
							
								            s, o
							 | 
						||
| 
								 | 
							
								        ))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								_share_cache = {}
							 | 
						||
| 
								 | 
							
								class _Cache:
							 | 
						||
| 
								 | 
							
								    """An abstract class handles caching functionality, provides two
							 | 
						||
| 
								 | 
							
								    levels of caching, in-memory by share instances attributes among
							 | 
						||
| 
								 | 
							
								    each other and by store attributes into files.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    **Note**:
							 | 
						||
| 
								 | 
							
								        any attributes that start with ``_`` or ``conf_`` will be ignored.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Parameters
							 | 
						||
| 
								 | 
							
								    ----------
							 | 
						||
| 
								 | 
							
								    cache_path : str or None
							 | 
						||
| 
								 | 
							
								        The path of cache file, if None then cache in file will disabled.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    *factors :
							 | 
						||
| 
								 | 
							
								        The caching factors that need to utilize next to `conf_cache_factors`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Attributes
							 | 
						||
| 
								 | 
							
								    ----------
							 | 
						||
| 
								 | 
							
								    cache_private : set
							 | 
						||
| 
								 | 
							
								        Hold the attributes that need be skipped from "in-memory cache".
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    cache_infile : bool
							 | 
						||
| 
								 | 
							
								        Utilized during initializing this class, to determine if the cache was able
							 | 
						||
| 
								 | 
							
								        to loaded from the specified cache path in 'cache_path'.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # skip attributes from cache
							 | 
						||
| 
								 | 
							
								    _cache_ignore = re.compile("^(_|conf_)")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, cache_path=None, *factors):
							 | 
						||
| 
								 | 
							
								        self.cache_me = {}
							 | 
						||
| 
								 | 
							
								        self.cache_private = set()
							 | 
						||
| 
								 | 
							
								        self.cache_infile = False
							 | 
						||
| 
								 | 
							
								        self._cache_path = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.conf_nocache:
							 | 
						||
| 
								 | 
							
								            self.dist_log("cache is disabled by `Config`")
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._cache_hash = self.cache_hash(*factors, *self.conf_cache_factors)
							 | 
						||
| 
								 | 
							
								        self._cache_path = cache_path
							 | 
						||
| 
								 | 
							
								        if cache_path:
							 | 
						||
| 
								 | 
							
								            if os.path.exists(cache_path):
							 | 
						||
| 
								 | 
							
								                self.dist_log("load cache from file ->", cache_path)
							 | 
						||
| 
								 | 
							
								                cache_mod = self.dist_load_module("cache", cache_path)
							 | 
						||
| 
								 | 
							
								                if not cache_mod:
							 | 
						||
| 
								 | 
							
								                    self.dist_log(
							 | 
						||
| 
								 | 
							
								                        "unable to load the cache file as a module",
							 | 
						||
| 
								 | 
							
								                        stderr=True
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                elif not hasattr(cache_mod, "hash") or \
							 | 
						||
| 
								 | 
							
								                     not hasattr(cache_mod, "data"):
							 | 
						||
| 
								 | 
							
								                    self.dist_log("invalid cache file", stderr=True)
							 | 
						||
| 
								 | 
							
								                elif self._cache_hash == cache_mod.hash:
							 | 
						||
| 
								 | 
							
								                    self.dist_log("hit the file cache")
							 | 
						||
| 
								 | 
							
								                    for attr, val in cache_mod.data.items():
							 | 
						||
| 
								 | 
							
								                        setattr(self, attr, val)
							 | 
						||
| 
								 | 
							
								                    self.cache_infile = True
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    self.dist_log("miss the file cache")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not self.cache_infile:
							 | 
						||
| 
								 | 
							
								            other_cache = _share_cache.get(self._cache_hash)
							 | 
						||
| 
								 | 
							
								            if other_cache:
							 | 
						||
| 
								 | 
							
								                self.dist_log("hit the memory cache")
							 | 
						||
| 
								 | 
							
								                for attr, val in other_cache.__dict__.items():
							 | 
						||
| 
								 | 
							
								                    if attr in other_cache.cache_private or \
							 | 
						||
| 
								 | 
							
								                               re.match(self._cache_ignore, attr):
							 | 
						||
| 
								 | 
							
								                        continue
							 | 
						||
| 
								 | 
							
								                    setattr(self, attr, val)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        _share_cache[self._cache_hash] = self
							 | 
						||
| 
								 | 
							
								        atexit.register(self.cache_flush)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __del__(self):
							 | 
						||
| 
								 | 
							
								        for h, o in _share_cache.items():
							 | 
						||
| 
								 | 
							
								            if o == self:
							 | 
						||
| 
								 | 
							
								                _share_cache.pop(h)
							 | 
						||
| 
								 | 
							
								                break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def cache_flush(self):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Force update the cache.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        if not self._cache_path:
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								        # TODO: don't write if the cache doesn't change
							 | 
						||
| 
								 | 
							
								        self.dist_log("write cache to path ->", self._cache_path)
							 | 
						||
| 
								 | 
							
								        cdict = self.__dict__.copy()
							 | 
						||
| 
								 | 
							
								        for attr in self.__dict__.keys():
							 | 
						||
| 
								 | 
							
								            if re.match(self._cache_ignore, attr):
							 | 
						||
| 
								 | 
							
								                cdict.pop(attr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        d = os.path.dirname(self._cache_path)
							 | 
						||
| 
								 | 
							
								        if not os.path.exists(d):
							 | 
						||
| 
								 | 
							
								            os.makedirs(d)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        repr_dict = pprint.pformat(cdict, compact=True)
							 | 
						||
| 
								 | 
							
								        with open(self._cache_path, "w") as f:
							 | 
						||
| 
								 | 
							
								            f.write(textwrap.dedent("""\
							 | 
						||
| 
								 | 
							
								            # AUTOGENERATED DON'T EDIT
							 | 
						||
| 
								 | 
							
								            # Please make changes to the code generator \
							 | 
						||
| 
								 | 
							
								            (distutils/ccompiler_opt.py)
							 | 
						||
| 
								 | 
							
								            hash = {}
							 | 
						||
| 
								 | 
							
								            data = \\
							 | 
						||
| 
								 | 
							
								            """).format(self._cache_hash))
							 | 
						||
| 
								 | 
							
								            f.write(repr_dict)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def cache_hash(self, *factors):
							 | 
						||
| 
								 | 
							
								        # is there a built-in non-crypto hash?
							 | 
						||
| 
								 | 
							
								        # sdbm
							 | 
						||
| 
								 | 
							
								        chash = 0
							 | 
						||
| 
								 | 
							
								        for f in factors:
							 | 
						||
| 
								 | 
							
								            for char in str(f):
							 | 
						||
| 
								 | 
							
								                chash  = ord(char) + (chash << 6) + (chash << 16) - chash
							 | 
						||
| 
								 | 
							
								                chash &= 0xFFFFFFFF
							 | 
						||
| 
								 | 
							
								        return chash
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def me(cb):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        A static method that can be treated as a decorator to
							 | 
						||
| 
								 | 
							
								        dynamically cache certain methods.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        def cache_wrap_me(self, *args, **kwargs):
							 | 
						||
| 
								 | 
							
								            # good for normal args
							 | 
						||
| 
								 | 
							
								            cache_key = str((
							 | 
						||
| 
								 | 
							
								                cb.__name__, *args, *kwargs.keys(), *kwargs.values()
							 | 
						||
| 
								 | 
							
								            ))
							 | 
						||
| 
								 | 
							
								            if cache_key in self.cache_me:
							 | 
						||
| 
								 | 
							
								                return self.cache_me[cache_key]
							 | 
						||
| 
								 | 
							
								            ccb = cb(self, *args, **kwargs)
							 | 
						||
| 
								 | 
							
								            self.cache_me[cache_key] = ccb
							 | 
						||
| 
								 | 
							
								            return ccb
							 | 
						||
| 
								 | 
							
								        return cache_wrap_me
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _CCompiler:
							 | 
						||
| 
								 | 
							
								    """A helper class for `CCompilerOpt` containing all utilities that
							 | 
						||
| 
								 | 
							
								    related to the fundamental compiler's functions.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Attributes
							 | 
						||
| 
								 | 
							
								    ----------
							 | 
						||
| 
								 | 
							
								    cc_on_x86 : bool
							 | 
						||
| 
								 | 
							
								        True when the target architecture is 32-bit x86
							 | 
						||
| 
								 | 
							
								    cc_on_x64 : bool
							 | 
						||
| 
								 | 
							
								        True when the target architecture is 64-bit x86
							 | 
						||
| 
								 | 
							
								    cc_on_ppc64 : bool
							 | 
						||
| 
								 | 
							
								        True when the target architecture is 64-bit big-endian powerpc
							 | 
						||
| 
								 | 
							
								    cc_on_ppc64le : bool
							 | 
						||
| 
								 | 
							
								        True when the target architecture is 64-bit litle-endian powerpc
							 | 
						||
| 
								 | 
							
								    cc_on_s390x : bool
							 | 
						||
| 
								 | 
							
								        True when the target architecture is IBM/ZARCH on linux
							 | 
						||
| 
								 | 
							
								    cc_on_armhf : bool
							 | 
						||
| 
								 | 
							
								        True when the target architecture is 32-bit ARMv7+
							 | 
						||
| 
								 | 
							
								    cc_on_aarch64 : bool
							 | 
						||
| 
								 | 
							
								        True when the target architecture is 64-bit Armv8-a+
							 | 
						||
| 
								 | 
							
								    cc_on_noarch : bool
							 | 
						||
| 
								 | 
							
								        True when the target architecture is unknown or not supported
							 | 
						||
| 
								 | 
							
								    cc_is_gcc : bool
							 | 
						||
| 
								 | 
							
								        True if the compiler is GNU or
							 | 
						||
| 
								 | 
							
								        if the compiler is unknown
							 | 
						||
| 
								 | 
							
								    cc_is_clang : bool
							 | 
						||
| 
								 | 
							
								        True if the compiler is Clang
							 | 
						||
| 
								 | 
							
								    cc_is_icc : bool
							 | 
						||
| 
								 | 
							
								        True if the compiler is Intel compiler (unix like)
							 | 
						||
| 
								 | 
							
								    cc_is_iccw : bool
							 | 
						||
| 
								 | 
							
								        True if the compiler is Intel compiler (msvc like)
							 | 
						||
| 
								 | 
							
								    cc_is_nocc : bool
							 | 
						||
| 
								 | 
							
								        True if the compiler isn't supported directly,
							 | 
						||
| 
								 | 
							
								        Note: that cause a fail-back to gcc
							 | 
						||
| 
								 | 
							
								    cc_has_debug : bool
							 | 
						||
| 
								 | 
							
								        True if the compiler has debug flags
							 | 
						||
| 
								 | 
							
								    cc_has_native : bool
							 | 
						||
| 
								 | 
							
								        True if the compiler has native flags
							 | 
						||
| 
								 | 
							
								    cc_noopt : bool
							 | 
						||
| 
								 | 
							
								        True if the compiler has definition 'DISABLE_OPT*',
							 | 
						||
| 
								 | 
							
								        or 'cc_on_noarch' is True
							 | 
						||
| 
								 | 
							
								    cc_march : str
							 | 
						||
| 
								 | 
							
								        The target architecture name, or "unknown" if
							 | 
						||
| 
								 | 
							
								        the architecture isn't supported
							 | 
						||
| 
								 | 
							
								    cc_name : str
							 | 
						||
| 
								 | 
							
								        The compiler name, or "unknown" if the compiler isn't supported
							 | 
						||
| 
								 | 
							
								    cc_flags : dict
							 | 
						||
| 
								 | 
							
								        Dictionary containing the initialized flags of `_Config.conf_cc_flags`
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def __init__(self):
							 | 
						||
| 
								 | 
							
								        if hasattr(self, "cc_is_cached"):
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								        #      attr            regex        compiler-expression
							 | 
						||
| 
								 | 
							
								        detect_arch = (
							 | 
						||
| 
								 | 
							
								            ("cc_on_x64",      ".*(x|x86_|amd)64.*", ""),
							 | 
						||
| 
								 | 
							
								            ("cc_on_x86",      ".*(win32|x86|i386|i686).*", ""),
							 | 
						||
| 
								 | 
							
								            ("cc_on_ppc64le",  ".*(powerpc|ppc)64(el|le).*", ""),
							 | 
						||
| 
								 | 
							
								            ("cc_on_ppc64",    ".*(powerpc|ppc)64.*", ""),
							 | 
						||
| 
								 | 
							
								            ("cc_on_aarch64",  ".*(aarch64|arm64).*", ""),
							 | 
						||
| 
								 | 
							
								            ("cc_on_armhf",    ".*arm.*", "defined(__ARM_ARCH_7__) || "
							 | 
						||
| 
								 | 
							
								                                          "defined(__ARM_ARCH_7A__)"),
							 | 
						||
| 
								 | 
							
								            ("cc_on_s390x",    ".*s390x.*", ""),
							 | 
						||
| 
								 | 
							
								            # undefined platform
							 | 
						||
| 
								 | 
							
								            ("cc_on_noarch",   "", ""),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        detect_compiler = (
							 | 
						||
| 
								 | 
							
								            ("cc_is_gcc",     r".*(gcc|gnu\-g).*", ""),
							 | 
						||
| 
								 | 
							
								            ("cc_is_clang",    ".*clang.*", ""),
							 | 
						||
| 
								 | 
							
								            # intel msvc like
							 | 
						||
| 
								 | 
							
								            ("cc_is_iccw",     ".*(intelw|intelemw|iccw).*", ""),
							 | 
						||
| 
								 | 
							
								            ("cc_is_icc",      ".*(intel|icc).*", ""),  # intel unix like
							 | 
						||
| 
								 | 
							
								            ("cc_is_msvc",     ".*msvc.*", ""),
							 | 
						||
| 
								 | 
							
								            # undefined compiler will be treat it as gcc
							 | 
						||
| 
								 | 
							
								            ("cc_is_nocc",     "", ""),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        detect_args = (
							 | 
						||
| 
								 | 
							
								           ("cc_has_debug",  ".*(O0|Od|ggdb|coverage|debug:full).*", ""),
							 | 
						||
| 
								 | 
							
								           ("cc_has_native", ".*(-march=native|-xHost|/QxHost).*", ""),
							 | 
						||
| 
								 | 
							
								           # in case if the class run with -DNPY_DISABLE_OPTIMIZATION
							 | 
						||
| 
								 | 
							
								           ("cc_noopt", ".*DISABLE_OPT.*", ""),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        dist_info = self.dist_info()
							 | 
						||
| 
								 | 
							
								        platform, compiler_info, extra_args = dist_info
							 | 
						||
| 
								 | 
							
								        # set False to all attrs
							 | 
						||
| 
								 | 
							
								        for section in (detect_arch, detect_compiler, detect_args):
							 | 
						||
| 
								 | 
							
								            for attr, rgex, cexpr in section:
							 | 
						||
| 
								 | 
							
								                setattr(self, attr, False)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for detect, searchin in ((detect_arch, platform), (detect_compiler, compiler_info)):
							 | 
						||
| 
								 | 
							
								            for attr, rgex, cexpr in detect:
							 | 
						||
| 
								 | 
							
								                if rgex and not re.match(rgex, searchin, re.IGNORECASE):
							 | 
						||
| 
								 | 
							
								                    continue
							 | 
						||
| 
								 | 
							
								                if cexpr and not self.cc_test_cexpr(cexpr):
							 | 
						||
| 
								 | 
							
								                    continue
							 | 
						||
| 
								 | 
							
								                setattr(self, attr, True)
							 | 
						||
| 
								 | 
							
								                break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for attr, rgex, cexpr in detect_args:
							 | 
						||
| 
								 | 
							
								            if rgex and not re.match(rgex, extra_args, re.IGNORECASE):
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            if cexpr and not self.cc_test_cexpr(cexpr):
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            setattr(self, attr, True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.cc_on_noarch:
							 | 
						||
| 
								 | 
							
								            self.dist_log(
							 | 
						||
| 
								 | 
							
								                "unable to detect CPU architecture which lead to disable the optimization. "
							 | 
						||
| 
								 | 
							
								                f"check dist_info:<<\n{dist_info}\n>>",
							 | 
						||
| 
								 | 
							
								                stderr=True
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            self.cc_noopt = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.conf_noopt:
							 | 
						||
| 
								 | 
							
								            self.dist_log("Optimization is disabled by the Config", stderr=True)
							 | 
						||
| 
								 | 
							
								            self.cc_noopt = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.cc_is_nocc:
							 | 
						||
| 
								 | 
							
								            """
							 | 
						||
| 
								 | 
							
								            mingw can be treated as a gcc, and also xlc even if it based on clang,
							 | 
						||
| 
								 | 
							
								            but still has the same gcc optimization flags.
							 | 
						||
| 
								 | 
							
								            """
							 | 
						||
| 
								 | 
							
								            self.dist_log(
							 | 
						||
| 
								 | 
							
								                "unable to detect compiler type which leads to treating it as GCC. "
							 | 
						||
| 
								 | 
							
								                "this is a normal behavior if you're using gcc-like compiler such as MinGW or IBM/XLC."
							 | 
						||
| 
								 | 
							
								                f"check dist_info:<<\n{dist_info}\n>>",
							 | 
						||
| 
								 | 
							
								                stderr=True
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            self.cc_is_gcc = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.cc_march = "unknown"
							 | 
						||
| 
								 | 
							
								        for arch in ("x86", "x64", "ppc64", "ppc64le",
							 | 
						||
| 
								 | 
							
								                     "armhf", "aarch64", "s390x"):
							 | 
						||
| 
								 | 
							
								            if getattr(self, "cc_on_" + arch):
							 | 
						||
| 
								 | 
							
								                self.cc_march = arch
							 | 
						||
| 
								 | 
							
								                break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.cc_name = "unknown"
							 | 
						||
| 
								 | 
							
								        for name in ("gcc", "clang", "iccw", "icc", "msvc"):
							 | 
						||
| 
								 | 
							
								            if getattr(self, "cc_is_" + name):
							 | 
						||
| 
								 | 
							
								                self.cc_name = name
							 | 
						||
| 
								 | 
							
								                break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.cc_flags = {}
							 | 
						||
| 
								 | 
							
								        compiler_flags = self.conf_cc_flags.get(self.cc_name)
							 | 
						||
| 
								 | 
							
								        if compiler_flags is None:
							 | 
						||
| 
								 | 
							
								            self.dist_fatal(
							 | 
						||
| 
								 | 
							
								                "undefined flag for compiler '%s', "
							 | 
						||
| 
								 | 
							
								                "leave an empty dict instead" % self.cc_name
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        for name, flags in compiler_flags.items():
							 | 
						||
| 
								 | 
							
								            self.cc_flags[name] = nflags = []
							 | 
						||
| 
								 | 
							
								            if flags:
							 | 
						||
| 
								 | 
							
								                assert(isinstance(flags, str))
							 | 
						||
| 
								 | 
							
								                flags = flags.split()
							 | 
						||
| 
								 | 
							
								                for f in flags:
							 | 
						||
| 
								 | 
							
								                    if self.cc_test_flags([f]):
							 | 
						||
| 
								 | 
							
								                        nflags.append(f)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.cc_is_cached = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @_Cache.me
							 | 
						||
| 
								 | 
							
								    def cc_test_flags(self, flags):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Returns True if the compiler supports 'flags'.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        assert(isinstance(flags, list))
							 | 
						||
| 
								 | 
							
								        self.dist_log("testing flags", flags)
							 | 
						||
| 
								 | 
							
								        test_path = os.path.join(self.conf_check_path, "test_flags.c")
							 | 
						||
| 
								 | 
							
								        test = self.dist_test(test_path, flags)
							 | 
						||
| 
								 | 
							
								        if not test:
							 | 
						||
| 
								 | 
							
								            self.dist_log("testing failed", stderr=True)
							 | 
						||
| 
								 | 
							
								        return test
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @_Cache.me
							 | 
						||
| 
								 | 
							
								    def cc_test_cexpr(self, cexpr, flags=[]):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Same as the above but supports compile-time expressions.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        self.dist_log("testing compiler expression", cexpr)
							 | 
						||
| 
								 | 
							
								        test_path = os.path.join(self.conf_tmp_path, "npy_dist_test_cexpr.c")
							 | 
						||
| 
								 | 
							
								        with open(test_path, "w") as fd:
							 | 
						||
| 
								 | 
							
								            fd.write(textwrap.dedent(f"""\
							 | 
						||
| 
								 | 
							
								               #if !({cexpr})
							 | 
						||
| 
								 | 
							
								                   #error "unsupported expression"
							 | 
						||
| 
								 | 
							
								               #endif
							 | 
						||
| 
								 | 
							
								               int dummy;
							 | 
						||
| 
								 | 
							
								            """))
							 | 
						||
| 
								 | 
							
								        test = self.dist_test(test_path, flags)
							 | 
						||
| 
								 | 
							
								        if not test:
							 | 
						||
| 
								 | 
							
								            self.dist_log("testing failed", stderr=True)
							 | 
						||
| 
								 | 
							
								        return test
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def cc_normalize_flags(self, flags):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Remove the conflicts that caused due gathering implied features flags.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Parameters
							 | 
						||
| 
								 | 
							
								        ----------
							 | 
						||
| 
								 | 
							
								        'flags' list, compiler flags
							 | 
						||
| 
								 | 
							
								            flags should be sorted from the lowest to the highest interest.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Returns
							 | 
						||
| 
								 | 
							
								        -------
							 | 
						||
| 
								 | 
							
								        list, filtered from any conflicts.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Examples
							 | 
						||
| 
								 | 
							
								        --------
							 | 
						||
| 
								 | 
							
								        >>> self.cc_normalize_flags(['-march=armv8.2-a+fp16', '-march=armv8.2-a+dotprod'])
							 | 
						||
| 
								 | 
							
								        ['armv8.2-a+fp16+dotprod']
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        >>> self.cc_normalize_flags(
							 | 
						||
| 
								 | 
							
								            ['-msse', '-msse2', '-msse3', '-mssse3', '-msse4.1', '-msse4.2', '-mavx', '-march=core-avx2']
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        ['-march=core-avx2']
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        assert(isinstance(flags, list))
							 | 
						||
| 
								 | 
							
								        if self.cc_is_gcc or self.cc_is_clang or self.cc_is_icc:
							 | 
						||
| 
								 | 
							
								            return self._cc_normalize_unix(flags)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.cc_is_msvc or self.cc_is_iccw:
							 | 
						||
| 
								 | 
							
								            return self._cc_normalize_win(flags)
							 | 
						||
| 
								 | 
							
								        return flags
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _cc_normalize_unix_mrgx = re.compile(
							 | 
						||
| 
								 | 
							
								        # 1- to check the highest of
							 | 
						||
| 
								 | 
							
								        r"^(-mcpu=|-march=|-x[A-Z0-9\-])"
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    _cc_normalize_unix_frgx = re.compile(
							 | 
						||
| 
								 | 
							
								        # 2- to remove any flags starts with
							 | 
						||
| 
								 | 
							
								        # -march, -mcpu, -x(INTEL) and '-m' without '='
							 | 
						||
| 
								 | 
							
								        r"^(?!(-mcpu=|-march=|-x[A-Z0-9\-]|-m[a-z0-9\-\.]*.$))|"
							 | 
						||
| 
								 | 
							
								        # exclude:
							 | 
						||
| 
								 | 
							
								        r"(?:-mzvector)"
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    _cc_normalize_unix_krgx = re.compile(
							 | 
						||
| 
								 | 
							
								        # 3- keep only the highest of
							 | 
						||
| 
								 | 
							
								        r"^(-mfpu|-mtune)"
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    _cc_normalize_arch_ver = re.compile(
							 | 
						||
| 
								 | 
							
								        r"[0-9.]"
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    def _cc_normalize_unix(self, flags):
							 | 
						||
| 
								 | 
							
								        def ver_flags(f):
							 | 
						||
| 
								 | 
							
								            #        arch ver  subflag
							 | 
						||
| 
								 | 
							
								            # -march=armv8.2-a+fp16fml
							 | 
						||
| 
								 | 
							
								            tokens = f.split('+')
							 | 
						||
| 
								 | 
							
								            ver = float('0' + ''.join(
							 | 
						||
| 
								 | 
							
								                re.findall(self._cc_normalize_arch_ver, tokens[0])
							 | 
						||
| 
								 | 
							
								            ))
							 | 
						||
| 
								 | 
							
								            return ver, tokens[0], tokens[1:]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if len(flags) <= 1:
							 | 
						||
| 
								 | 
							
								            return flags
							 | 
						||
| 
								 | 
							
								        # get the highest matched flag
							 | 
						||
| 
								 | 
							
								        for i, cur_flag in enumerate(reversed(flags)):
							 | 
						||
| 
								 | 
							
								            if not re.match(self._cc_normalize_unix_mrgx, cur_flag):
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            lower_flags = flags[:-(i+1)]
							 | 
						||
| 
								 | 
							
								            upper_flags = flags[-i:]
							 | 
						||
| 
								 | 
							
								            filterd = list(filter(
							 | 
						||
| 
								 | 
							
								                self._cc_normalize_unix_frgx.search, lower_flags
							 | 
						||
| 
								 | 
							
								            ))
							 | 
						||
| 
								 | 
							
								            # gather subflags
							 | 
						||
| 
								 | 
							
								            ver, arch, subflags = ver_flags(cur_flag)
							 | 
						||
| 
								 | 
							
								            if ver > 0 and len(subflags) > 0:
							 | 
						||
| 
								 | 
							
								                for xflag in lower_flags:
							 | 
						||
| 
								 | 
							
								                    xver, _, xsubflags = ver_flags(xflag)
							 | 
						||
| 
								 | 
							
								                    if ver == xver:
							 | 
						||
| 
								 | 
							
								                        subflags = xsubflags + subflags
							 | 
						||
| 
								 | 
							
								                cur_flag = arch + '+' + '+'.join(subflags)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            flags = filterd + [cur_flag]
							 | 
						||
| 
								 | 
							
								            if i > 0:
							 | 
						||
| 
								 | 
							
								                flags += upper_flags
							 | 
						||
| 
								 | 
							
								            break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # to remove overridable flags
							 | 
						||
| 
								 | 
							
								        final_flags = []
							 | 
						||
| 
								 | 
							
								        matched = set()
							 | 
						||
| 
								 | 
							
								        for f in reversed(flags):
							 | 
						||
| 
								 | 
							
								            match = re.match(self._cc_normalize_unix_krgx, f)
							 | 
						||
| 
								 | 
							
								            if not match:
							 | 
						||
| 
								 | 
							
								                pass
							 | 
						||
| 
								 | 
							
								            elif match[0] in matched:
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                matched.add(match[0])
							 | 
						||
| 
								 | 
							
								            final_flags.insert(0, f)
							 | 
						||
| 
								 | 
							
								        return final_flags
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _cc_normalize_win_frgx = re.compile(
							 | 
						||
| 
								 | 
							
								        r"^(?!(/arch\:|/Qx\:))"
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    _cc_normalize_win_mrgx = re.compile(
							 | 
						||
| 
								 | 
							
								        r"^(/arch|/Qx:)"
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    def _cc_normalize_win(self, flags):
							 | 
						||
| 
								 | 
							
								        for i, f in enumerate(reversed(flags)):
							 | 
						||
| 
								 | 
							
								            if not re.match(self._cc_normalize_win_mrgx, f):
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            i += 1
							 | 
						||
| 
								 | 
							
								            return list(filter(
							 | 
						||
| 
								 | 
							
								                self._cc_normalize_win_frgx.search, flags[:-i]
							 | 
						||
| 
								 | 
							
								            )) + flags[-i:]
							 | 
						||
| 
								 | 
							
								        return flags
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _Feature:
							 | 
						||
| 
								 | 
							
								    """A helper class for `CCompilerOpt` that managing CPU features.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Attributes
							 | 
						||
| 
								 | 
							
								    ----------
							 | 
						||
| 
								 | 
							
								    feature_supported : dict
							 | 
						||
| 
								 | 
							
								        Dictionary containing all CPU features that supported
							 | 
						||
| 
								 | 
							
								        by the platform, according to the specified values in attribute
							 | 
						||
| 
								 | 
							
								        `_Config.conf_features` and `_Config.conf_features_partial()`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    feature_min : set
							 | 
						||
| 
								 | 
							
								        The minimum support of CPU features, according to
							 | 
						||
| 
								 | 
							
								        the specified values in attribute `_Config.conf_min_features`.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def __init__(self):
							 | 
						||
| 
								 | 
							
								        if hasattr(self, "feature_is_cached"):
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								        self.feature_supported = pfeatures = self.conf_features_partial()
							 | 
						||
| 
								 | 
							
								        for feature_name in list(pfeatures.keys()):
							 | 
						||
| 
								 | 
							
								            feature  = pfeatures[feature_name]
							 | 
						||
| 
								 | 
							
								            cfeature = self.conf_features[feature_name]
							 | 
						||
| 
								 | 
							
								            feature.update({
							 | 
						||
| 
								 | 
							
								                k:v for k,v in cfeature.items() if k not in feature
							 | 
						||
| 
								 | 
							
								            })
							 | 
						||
| 
								 | 
							
								            disabled = feature.get("disable")
							 | 
						||
| 
								 | 
							
								            if disabled is not None:
							 | 
						||
| 
								 | 
							
								                pfeatures.pop(feature_name)
							 | 
						||
| 
								 | 
							
								                self.dist_log(
							 | 
						||
| 
								 | 
							
								                    "feature '%s' is disabled," % feature_name,
							 | 
						||
| 
								 | 
							
								                    disabled, stderr=True
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            # list is used internally for these options
							 | 
						||
| 
								 | 
							
								            for option in (
							 | 
						||
| 
								 | 
							
								                "implies", "group", "detect", "headers", "flags", "extra_checks"
							 | 
						||
| 
								 | 
							
								            ) :
							 | 
						||
| 
								 | 
							
								                oval = feature.get(option)
							 | 
						||
| 
								 | 
							
								                if isinstance(oval, str):
							 | 
						||
| 
								 | 
							
								                    feature[option] = oval.split()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.feature_min = set()
							 | 
						||
| 
								 | 
							
								        min_f = self.conf_min_features.get(self.cc_march, "")
							 | 
						||
| 
								 | 
							
								        for F in min_f.upper().split():
							 | 
						||
| 
								 | 
							
								            if F in self.feature_supported:
							 | 
						||
| 
								 | 
							
								                self.feature_min.add(F)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.feature_is_cached = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def feature_names(self, names=None, force_flags=None, macros=[]):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Returns a set of CPU feature names that supported by platform and the **C** compiler.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Parameters
							 | 
						||
| 
								 | 
							
								        ----------
							 | 
						||
| 
								 | 
							
								        names : sequence or None, optional
							 | 
						||
| 
								 | 
							
								            Specify certain CPU features to test it against the **C** compiler.
							 | 
						||
| 
								 | 
							
								            if None(default), it will test all current supported features.
							 | 
						||
| 
								 | 
							
								            **Note**: feature names must be in upper-case.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        force_flags : list or None, optional
							 | 
						||
| 
								 | 
							
								            If None(default), default compiler flags for every CPU feature will
							 | 
						||
| 
								 | 
							
								            be used during the test.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        macros : list of tuples, optional
							 | 
						||
| 
								 | 
							
								            A list of C macro definitions.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        assert(
							 | 
						||
| 
								 | 
							
								            names is None or (
							 | 
						||
| 
								 | 
							
								                not isinstance(names, str) and
							 | 
						||
| 
								 | 
							
								                hasattr(names, "__iter__")
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        assert(force_flags is None or isinstance(force_flags, list))
							 | 
						||
| 
								 | 
							
								        if names is None:
							 | 
						||
| 
								 | 
							
								            names = self.feature_supported.keys()
							 | 
						||
| 
								 | 
							
								        supported_names = set()
							 | 
						||
| 
								 | 
							
								        for f in names:
							 | 
						||
| 
								 | 
							
								            if self.feature_is_supported(
							 | 
						||
| 
								 | 
							
								                f, force_flags=force_flags, macros=macros
							 | 
						||
| 
								 | 
							
								            ):
							 | 
						||
| 
								 | 
							
								                supported_names.add(f)
							 | 
						||
| 
								 | 
							
								        return supported_names
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def feature_is_exist(self, name):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Returns True if a certain feature is exist and covered within
							 | 
						||
| 
								 | 
							
								        `_Config.conf_features`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Parameters
							 | 
						||
| 
								 | 
							
								        ----------
							 | 
						||
| 
								 | 
							
								        'name': str
							 | 
						||
| 
								 | 
							
								            feature name in uppercase.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        assert(name.isupper())
							 | 
						||
| 
								 | 
							
								        return name in self.conf_features
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def feature_sorted(self, names, reverse=False):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Sort a list of CPU features ordered by the lowest interest.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Parameters
							 | 
						||
| 
								 | 
							
								        ----------
							 | 
						||
| 
								 | 
							
								        'names': sequence
							 | 
						||
| 
								 | 
							
								            sequence of supported feature names in uppercase.
							 | 
						||
| 
								 | 
							
								        'reverse': bool, optional
							 | 
						||
| 
								 | 
							
								            If true, the sorted features is reversed. (highest interest)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Returns
							 | 
						||
| 
								 | 
							
								        -------
							 | 
						||
| 
								 | 
							
								        list, sorted CPU features
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        def sort_cb(k):
							 | 
						||
| 
								 | 
							
								            if isinstance(k, str):
							 | 
						||
| 
								 | 
							
								                return self.feature_supported[k]["interest"]
							 | 
						||
| 
								 | 
							
								            # multiple features
							 | 
						||
| 
								 | 
							
								            rank = max([self.feature_supported[f]["interest"] for f in k])
							 | 
						||
| 
								 | 
							
								            # FIXME: that's not a safe way to increase the rank for
							 | 
						||
| 
								 | 
							
								            # multi targets
							 | 
						||
| 
								 | 
							
								            rank += len(k) -1
							 | 
						||
| 
								 | 
							
								            return rank
							 | 
						||
| 
								 | 
							
								        return sorted(names, reverse=reverse, key=sort_cb)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def feature_implies(self, names, keep_origins=False):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Return a set of CPU features that implied by 'names'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Parameters
							 | 
						||
| 
								 | 
							
								        ----------
							 | 
						||
| 
								 | 
							
								        names : str or sequence of str
							 | 
						||
| 
								 | 
							
								            CPU feature name(s) in uppercase.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        keep_origins : bool
							 | 
						||
| 
								 | 
							
								            if False(default) then the returned set will not contain any
							 | 
						||
| 
								 | 
							
								            features from 'names'. This case happens only when two features
							 | 
						||
| 
								 | 
							
								            imply each other.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Examples
							 | 
						||
| 
								 | 
							
								        --------
							 | 
						||
| 
								 | 
							
								        >>> self.feature_implies("SSE3")
							 | 
						||
| 
								 | 
							
								        {'SSE', 'SSE2'}
							 | 
						||
| 
								 | 
							
								        >>> self.feature_implies("SSE2")
							 | 
						||
| 
								 | 
							
								        {'SSE'}
							 | 
						||
| 
								 | 
							
								        >>> self.feature_implies("SSE2", keep_origins=True)
							 | 
						||
| 
								 | 
							
								        # 'SSE2' found here since 'SSE' and 'SSE2' imply each other
							 | 
						||
| 
								 | 
							
								        {'SSE', 'SSE2'}
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        def get_implies(name, _caller=set()):
							 | 
						||
| 
								 | 
							
								            implies = set()
							 | 
						||
| 
								 | 
							
								            d = self.feature_supported[name]
							 | 
						||
| 
								 | 
							
								            for i in d.get("implies", []):
							 | 
						||
| 
								 | 
							
								                implies.add(i)
							 | 
						||
| 
								 | 
							
								                if i in _caller:
							 | 
						||
| 
								 | 
							
								                    # infinity recursive guard since
							 | 
						||
| 
								 | 
							
								                    # features can imply each other
							 | 
						||
| 
								 | 
							
								                    continue
							 | 
						||
| 
								 | 
							
								                _caller.add(name)
							 | 
						||
| 
								 | 
							
								                implies = implies.union(get_implies(i, _caller))
							 | 
						||
| 
								 | 
							
								            return implies
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if isinstance(names, str):
							 | 
						||
| 
								 | 
							
								            implies = get_implies(names)
							 | 
						||
| 
								 | 
							
								            names = [names]
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            assert(hasattr(names, "__iter__"))
							 | 
						||
| 
								 | 
							
								            implies = set()
							 | 
						||
| 
								 | 
							
								            for n in names:
							 | 
						||
| 
								 | 
							
								                implies = implies.union(get_implies(n))
							 | 
						||
| 
								 | 
							
								        if not keep_origins:
							 | 
						||
| 
								 | 
							
								            implies.difference_update(names)
							 | 
						||
| 
								 | 
							
								        return implies
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def feature_implies_c(self, names):
							 | 
						||
| 
								 | 
							
								        """same as feature_implies() but combining 'names'"""
							 | 
						||
| 
								 | 
							
								        if isinstance(names, str):
							 | 
						||
| 
								 | 
							
								            names = set((names,))
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            names = set(names)
							 | 
						||
| 
								 | 
							
								        return names.union(self.feature_implies(names))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def feature_ahead(self, names):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Return list of features in 'names' after remove any
							 | 
						||
| 
								 | 
							
								        implied features and keep the origins.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Parameters
							 | 
						||
| 
								 | 
							
								        ----------
							 | 
						||
| 
								 | 
							
								        'names': sequence
							 | 
						||
| 
								 | 
							
								            sequence of CPU feature names in uppercase.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Returns
							 | 
						||
| 
								 | 
							
								        -------
							 | 
						||
| 
								 | 
							
								        list of CPU features sorted as-is 'names'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Examples
							 | 
						||
| 
								 | 
							
								        --------
							 | 
						||
| 
								 | 
							
								        >>> self.feature_ahead(["SSE2", "SSE3", "SSE41"])
							 | 
						||
| 
								 | 
							
								        ["SSE41"]
							 | 
						||
| 
								 | 
							
								        # assume AVX2 and FMA3 implies each other and AVX2
							 | 
						||
| 
								 | 
							
								        # is the highest interest
							 | 
						||
| 
								 | 
							
								        >>> self.feature_ahead(["SSE2", "SSE3", "SSE41", "AVX2", "FMA3"])
							 | 
						||
| 
								 | 
							
								        ["AVX2"]
							 | 
						||
| 
								 | 
							
								        # assume AVX2 and FMA3 don't implies each other
							 | 
						||
| 
								 | 
							
								        >>> self.feature_ahead(["SSE2", "SSE3", "SSE41", "AVX2", "FMA3"])
							 | 
						||
| 
								 | 
							
								        ["AVX2", "FMA3"]
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        assert(
							 | 
						||
| 
								 | 
							
								            not isinstance(names, str)
							 | 
						||
| 
								 | 
							
								            and hasattr(names, '__iter__')
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        implies = self.feature_implies(names, keep_origins=True)
							 | 
						||
| 
								 | 
							
								        ahead = [n for n in names if n not in implies]
							 | 
						||
| 
								 | 
							
								        if len(ahead) == 0:
							 | 
						||
| 
								 | 
							
								            # return the highest interested feature
							 | 
						||
| 
								 | 
							
								            # if all features imply each other
							 | 
						||
| 
								 | 
							
								            ahead = self.feature_sorted(names, reverse=True)[:1]
							 | 
						||
| 
								 | 
							
								        return ahead
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def feature_untied(self, names):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        same as 'feature_ahead()' but if both features implied each other
							 | 
						||
| 
								 | 
							
								        and keep the highest interest.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Parameters
							 | 
						||
| 
								 | 
							
								        ----------
							 | 
						||
| 
								 | 
							
								        'names': sequence
							 | 
						||
| 
								 | 
							
								            sequence of CPU feature names in uppercase.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Returns
							 | 
						||
| 
								 | 
							
								        -------
							 | 
						||
| 
								 | 
							
								        list of CPU features sorted as-is 'names'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Examples
							 | 
						||
| 
								 | 
							
								        --------
							 | 
						||
| 
								 | 
							
								        >>> self.feature_untied(["SSE2", "SSE3", "SSE41"])
							 | 
						||
| 
								 | 
							
								        ["SSE2", "SSE3", "SSE41"]
							 | 
						||
| 
								 | 
							
								        # assume AVX2 and FMA3 implies each other
							 | 
						||
| 
								 | 
							
								        >>> self.feature_untied(["SSE2", "SSE3", "SSE41", "FMA3", "AVX2"])
							 | 
						||
| 
								 | 
							
								        ["SSE2", "SSE3", "SSE41", "AVX2"]
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        assert(
							 | 
						||
| 
								 | 
							
								            not isinstance(names, str)
							 | 
						||
| 
								 | 
							
								            and hasattr(names, '__iter__')
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        final = []
							 | 
						||
| 
								 | 
							
								        for n in names:
							 | 
						||
| 
								 | 
							
								            implies = self.feature_implies(n)
							 | 
						||
| 
								 | 
							
								            tied = [
							 | 
						||
| 
								 | 
							
								                nn for nn in final
							 | 
						||
| 
								 | 
							
								                if nn in implies and n in self.feature_implies(nn)
							 | 
						||
| 
								 | 
							
								            ]
							 | 
						||
| 
								 | 
							
								            if tied:
							 | 
						||
| 
								 | 
							
								                tied = self.feature_sorted(tied + [n])
							 | 
						||
| 
								 | 
							
								                if n not in tied[1:]:
							 | 
						||
| 
								 | 
							
								                    continue
							 | 
						||
| 
								 | 
							
								                final.remove(tied[:1][0])
							 | 
						||
| 
								 | 
							
								            final.append(n)
							 | 
						||
| 
								 | 
							
								        return final
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def feature_get_til(self, names, keyisfalse):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        same as `feature_implies_c()` but stop collecting implied
							 | 
						||
| 
								 | 
							
								        features when feature's option that provided through
							 | 
						||
| 
								 | 
							
								        parameter 'keyisfalse' is False, also sorting the returned
							 | 
						||
| 
								 | 
							
								        features.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        def til(tnames):
							 | 
						||
| 
								 | 
							
								            # sort from highest to lowest interest then cut if "key" is False
							 | 
						||
| 
								 | 
							
								            tnames = self.feature_implies_c(tnames)
							 | 
						||
| 
								 | 
							
								            tnames = self.feature_sorted(tnames, reverse=True)
							 | 
						||
| 
								 | 
							
								            for i, n in enumerate(tnames):
							 | 
						||
| 
								 | 
							
								                if not self.feature_supported[n].get(keyisfalse, True):
							 | 
						||
| 
								 | 
							
								                    tnames = tnames[:i+1]
							 | 
						||
| 
								 | 
							
								                    break
							 | 
						||
| 
								 | 
							
								            return tnames
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if isinstance(names, str) or len(names) <= 1:
							 | 
						||
| 
								 | 
							
								            names = til(names)
							 | 
						||
| 
								 | 
							
								            # normalize the sort
							 | 
						||
| 
								 | 
							
								            names.reverse()
							 | 
						||
| 
								 | 
							
								            return names
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        names = self.feature_ahead(names)
							 | 
						||
| 
								 | 
							
								        names = {t for n in names for t in til(n)}
							 | 
						||
| 
								 | 
							
								        return self.feature_sorted(names)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def feature_detect(self, names):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Return a list of CPU features that required to be detected
							 | 
						||
| 
								 | 
							
								        sorted from the lowest to highest interest.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        names = self.feature_get_til(names, "implies_detect")
							 | 
						||
| 
								 | 
							
								        detect = []
							 | 
						||
| 
								 | 
							
								        for n in names:
							 | 
						||
| 
								 | 
							
								            d = self.feature_supported[n]
							 | 
						||
| 
								 | 
							
								            detect += d.get("detect", d.get("group", [n]))
							 | 
						||
| 
								 | 
							
								        return detect
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @_Cache.me
							 | 
						||
| 
								 | 
							
								    def feature_flags(self, names):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Return a list of CPU features flags sorted from the lowest
							 | 
						||
| 
								 | 
							
								        to highest interest.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        names = self.feature_sorted(self.feature_implies_c(names))
							 | 
						||
| 
								 | 
							
								        flags = []
							 | 
						||
| 
								 | 
							
								        for n in names:
							 | 
						||
| 
								 | 
							
								            d = self.feature_supported[n]
							 | 
						||
| 
								 | 
							
								            f = d.get("flags", [])
							 | 
						||
| 
								 | 
							
								            if not f or not self.cc_test_flags(f):
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            flags += f
							 | 
						||
| 
								 | 
							
								        return self.cc_normalize_flags(flags)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @_Cache.me
							 | 
						||
| 
								 | 
							
								    def feature_test(self, name, force_flags=None, macros=[]):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Test a certain CPU feature against the compiler through its own
							 | 
						||
| 
								 | 
							
								        check file.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Parameters
							 | 
						||
| 
								 | 
							
								        ----------
							 | 
						||
| 
								 | 
							
								        name : str
							 | 
						||
| 
								 | 
							
								            Supported CPU feature name.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        force_flags : list or None, optional
							 | 
						||
| 
								 | 
							
								            If None(default), the returned flags from `feature_flags()`
							 | 
						||
| 
								 | 
							
								            will be used.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        macros : list of tuples, optional
							 | 
						||
| 
								 | 
							
								            A list of C macro definitions.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        if force_flags is None:
							 | 
						||
| 
								 | 
							
								            force_flags = self.feature_flags(name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.dist_log(
							 | 
						||
| 
								 | 
							
								            "testing feature '%s' with flags (%s)" % (
							 | 
						||
| 
								 | 
							
								            name, ' '.join(force_flags)
							 | 
						||
| 
								 | 
							
								        ))
							 | 
						||
| 
								 | 
							
								        # Each CPU feature must have C source code contains at
							 | 
						||
| 
								 | 
							
								        # least one intrinsic or instruction related to this feature.
							 | 
						||
| 
								 | 
							
								        test_path = os.path.join(
							 | 
						||
| 
								 | 
							
								            self.conf_check_path, "cpu_%s.c" % name.lower()
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        if not os.path.exists(test_path):
							 | 
						||
| 
								 | 
							
								            self.dist_fatal("feature test file is not exist", test_path)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        test = self.dist_test(
							 | 
						||
| 
								 | 
							
								            test_path, force_flags + self.cc_flags["werror"], macros=macros
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        if not test:
							 | 
						||
| 
								 | 
							
								            self.dist_log("testing failed", stderr=True)
							 | 
						||
| 
								 | 
							
								        return test
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @_Cache.me
							 | 
						||
| 
								 | 
							
								    def feature_is_supported(self, name, force_flags=None, macros=[]):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Check if a certain CPU feature is supported by the platform and compiler.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Parameters
							 | 
						||
| 
								 | 
							
								        ----------
							 | 
						||
| 
								 | 
							
								        name : str
							 | 
						||
| 
								 | 
							
								            CPU feature name in uppercase.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        force_flags : list or None, optional
							 | 
						||
| 
								 | 
							
								            If None(default), default compiler flags for every CPU feature will
							 | 
						||
| 
								 | 
							
								            be used during test.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        macros : list of tuples, optional
							 | 
						||
| 
								 | 
							
								            A list of C macro definitions.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        assert(name.isupper())
							 | 
						||
| 
								 | 
							
								        assert(force_flags is None or isinstance(force_flags, list))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        supported = name in self.feature_supported
							 | 
						||
| 
								 | 
							
								        if supported:
							 | 
						||
| 
								 | 
							
								            for impl in self.feature_implies(name):
							 | 
						||
| 
								 | 
							
								                if not self.feature_test(impl, force_flags, macros=macros):
							 | 
						||
| 
								 | 
							
								                    return False
							 | 
						||
| 
								 | 
							
								            if not self.feature_test(name, force_flags, macros=macros):
							 | 
						||
| 
								 | 
							
								                return False
							 | 
						||
| 
								 | 
							
								        return supported
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @_Cache.me
							 | 
						||
| 
								 | 
							
								    def feature_can_autovec(self, name):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        check if the feature can be auto-vectorized by the compiler
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        assert(isinstance(name, str))
							 | 
						||
| 
								 | 
							
								        d = self.feature_supported[name]
							 | 
						||
| 
								 | 
							
								        can = d.get("autovec", None)
							 | 
						||
| 
								 | 
							
								        if can is None:
							 | 
						||
| 
								 | 
							
								            valid_flags = [
							 | 
						||
| 
								 | 
							
								                self.cc_test_flags([f]) for f in d.get("flags", [])
							 | 
						||
| 
								 | 
							
								            ]
							 | 
						||
| 
								 | 
							
								            can = valid_flags and any(valid_flags)
							 | 
						||
| 
								 | 
							
								        return can
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @_Cache.me
							 | 
						||
| 
								 | 
							
								    def feature_extra_checks(self, name):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Return a list of supported extra checks after testing them against
							 | 
						||
| 
								 | 
							
								        the compiler.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Parameters
							 | 
						||
| 
								 | 
							
								        ----------
							 | 
						||
| 
								 | 
							
								        names : str
							 | 
						||
| 
								 | 
							
								            CPU feature name in uppercase.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        assert isinstance(name, str)
							 | 
						||
| 
								 | 
							
								        d = self.feature_supported[name]
							 | 
						||
| 
								 | 
							
								        extra_checks = d.get("extra_checks", [])
							 | 
						||
| 
								 | 
							
								        if not extra_checks:
							 | 
						||
| 
								 | 
							
								            return []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.dist_log("Testing extra checks for feature '%s'" % name, extra_checks)
							 | 
						||
| 
								 | 
							
								        flags = self.feature_flags(name)
							 | 
						||
| 
								 | 
							
								        available = []
							 | 
						||
| 
								 | 
							
								        not_available = []
							 | 
						||
| 
								 | 
							
								        for chk in extra_checks:
							 | 
						||
| 
								 | 
							
								            test_path = os.path.join(
							 | 
						||
| 
								 | 
							
								                self.conf_check_path, "extra_%s.c" % chk.lower()
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            if not os.path.exists(test_path):
							 | 
						||
| 
								 | 
							
								                self.dist_fatal("extra check file does not exist", test_path)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            is_supported = self.dist_test(test_path, flags + self.cc_flags["werror"])
							 | 
						||
| 
								 | 
							
								            if is_supported:
							 | 
						||
| 
								 | 
							
								                available.append(chk)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                not_available.append(chk)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not_available:
							 | 
						||
| 
								 | 
							
								            self.dist_log("testing failed for checks", not_available, stderr=True)
							 | 
						||
| 
								 | 
							
								        return available
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def feature_c_preprocessor(self, feature_name, tabs=0):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Generate C preprocessor definitions and include headers of a CPU feature.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Parameters
							 | 
						||
| 
								 | 
							
								        ----------
							 | 
						||
| 
								 | 
							
								        'feature_name': str
							 | 
						||
| 
								 | 
							
								            CPU feature name in uppercase.
							 | 
						||
| 
								 | 
							
								        'tabs': int
							 | 
						||
| 
								 | 
							
								            if > 0, align the generated strings to the right depend on number of tabs.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Returns
							 | 
						||
| 
								 | 
							
								        -------
							 | 
						||
| 
								 | 
							
								        str, generated C preprocessor
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Examples
							 | 
						||
| 
								 | 
							
								        --------
							 | 
						||
| 
								 | 
							
								        >>> self.feature_c_preprocessor("SSE3")
							 | 
						||
| 
								 | 
							
								        /** SSE3 **/
							 | 
						||
| 
								 | 
							
								        #define NPY_HAVE_SSE3 1
							 | 
						||
| 
								 | 
							
								        #include <pmmintrin.h>
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        assert(feature_name.isupper())
							 | 
						||
| 
								 | 
							
								        feature = self.feature_supported.get(feature_name)
							 | 
						||
| 
								 | 
							
								        assert(feature is not None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        prepr = [
							 | 
						||
| 
								 | 
							
								            "/** %s **/" % feature_name,
							 | 
						||
| 
								 | 
							
								            "#define %sHAVE_%s 1" % (self.conf_c_prefix, feature_name)
							 | 
						||
| 
								 | 
							
								        ]
							 | 
						||
| 
								 | 
							
								        prepr += [
							 | 
						||
| 
								 | 
							
								            "#include <%s>" % h for h in feature.get("headers", [])
							 | 
						||
| 
								 | 
							
								        ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        extra_defs = feature.get("group", [])
							 | 
						||
| 
								 | 
							
								        extra_defs += self.feature_extra_checks(feature_name)
							 | 
						||
| 
								 | 
							
								        for edef in extra_defs:
							 | 
						||
| 
								 | 
							
								            # Guard extra definitions in case of duplicate with
							 | 
						||
| 
								 | 
							
								            # another feature
							 | 
						||
| 
								 | 
							
								            prepr += [
							 | 
						||
| 
								 | 
							
								                "#ifndef %sHAVE_%s" % (self.conf_c_prefix, edef),
							 | 
						||
| 
								 | 
							
								                "\t#define %sHAVE_%s 1" % (self.conf_c_prefix, edef),
							 | 
						||
| 
								 | 
							
								                "#endif",
							 | 
						||
| 
								 | 
							
								            ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if tabs > 0:
							 | 
						||
| 
								 | 
							
								            prepr = [('\t'*tabs) + l for l in prepr]
							 | 
						||
| 
								 | 
							
								        return '\n'.join(prepr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _Parse:
							 | 
						||
| 
								 | 
							
								    """A helper class that parsing main arguments of `CCompilerOpt`,
							 | 
						||
| 
								 | 
							
								    also parsing configuration statements in dispatch-able sources.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Parameters
							 | 
						||
| 
								 | 
							
								    ----------
							 | 
						||
| 
								 | 
							
								    cpu_baseline : str or None
							 | 
						||
| 
								 | 
							
								        minimal set of required CPU features or special options.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    cpu_dispatch : str or None
							 | 
						||
| 
								 | 
							
								        dispatched set of additional CPU features or special options.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Special options can be:
							 | 
						||
| 
								 | 
							
								        - **MIN**: Enables the minimum CPU features that utilized via `_Config.conf_min_features`
							 | 
						||
| 
								 | 
							
								        - **MAX**: Enables all supported CPU features by the Compiler and platform.
							 | 
						||
| 
								 | 
							
								        - **NATIVE**: Enables all CPU features that supported by the current machine.
							 | 
						||
| 
								 | 
							
								        - **NONE**: Enables nothing
							 | 
						||
| 
								 | 
							
								        - **Operand +/-**: remove or add features, useful with options **MAX**, **MIN** and **NATIVE**.
							 | 
						||
| 
								 | 
							
								            NOTE: operand + is only added for nominal reason.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    NOTES:
							 | 
						||
| 
								 | 
							
								        - Case-insensitive among all CPU features and special options.
							 | 
						||
| 
								 | 
							
								        - Comma or space can be used as a separator.
							 | 
						||
| 
								 | 
							
								        - If the CPU feature is not supported by the user platform or compiler,
							 | 
						||
| 
								 | 
							
								          it will be skipped rather than raising a fatal error.
							 | 
						||
| 
								 | 
							
								        - Any specified CPU features to 'cpu_dispatch' will be skipped if its part of CPU baseline features
							 | 
						||
| 
								 | 
							
								        - 'cpu_baseline' force enables implied features.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Attributes
							 | 
						||
| 
								 | 
							
								    ----------
							 | 
						||
| 
								 | 
							
								    parse_baseline_names : list
							 | 
						||
| 
								 | 
							
								        Final CPU baseline's feature names(sorted from low to high)
							 | 
						||
| 
								 | 
							
								    parse_baseline_flags : list
							 | 
						||
| 
								 | 
							
								        Compiler flags of baseline features
							 | 
						||
| 
								 | 
							
								    parse_dispatch_names : list
							 | 
						||
| 
								 | 
							
								        Final CPU dispatch-able feature names(sorted from low to high)
							 | 
						||
| 
								 | 
							
								    parse_target_groups : dict
							 | 
						||
| 
								 | 
							
								        Dictionary containing initialized target groups that configured
							 | 
						||
| 
								 | 
							
								        through class attribute `conf_target_groups`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        The key is represent the group name and value is a tuple
							 | 
						||
| 
								 | 
							
								        contains three items :
							 | 
						||
| 
								 | 
							
								            - bool, True if group has the 'baseline' option.
							 | 
						||
| 
								 | 
							
								            - list, list of CPU features.
							 | 
						||
| 
								 | 
							
								            - list, list of extra compiler flags.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def __init__(self, cpu_baseline, cpu_dispatch):
							 | 
						||
| 
								 | 
							
								        self._parse_policies = dict(
							 | 
						||
| 
								 | 
							
								            # POLICY NAME, (HAVE, NOT HAVE, [DEB])
							 | 
						||
| 
								 | 
							
								            KEEP_BASELINE = (
							 | 
						||
| 
								 | 
							
								                None, self._parse_policy_not_keepbase,
							 | 
						||
| 
								 | 
							
								                []
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            KEEP_SORT = (
							 | 
						||
| 
								 | 
							
								                self._parse_policy_keepsort,
							 | 
						||
| 
								 | 
							
								                self._parse_policy_not_keepsort,
							 | 
						||
| 
								 | 
							
								                []
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            MAXOPT = (
							 | 
						||
| 
								 | 
							
								                self._parse_policy_maxopt, None,
							 | 
						||
| 
								 | 
							
								                []
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            WERROR = (
							 | 
						||
| 
								 | 
							
								                self._parse_policy_werror, None,
							 | 
						||
| 
								 | 
							
								                []
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            AUTOVEC = (
							 | 
						||
| 
								 | 
							
								                self._parse_policy_autovec, None,
							 | 
						||
| 
								 | 
							
								                ["MAXOPT"]
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        if hasattr(self, "parse_is_cached"):
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.parse_baseline_names = []
							 | 
						||
| 
								 | 
							
								        self.parse_baseline_flags = []
							 | 
						||
| 
								 | 
							
								        self.parse_dispatch_names = []
							 | 
						||
| 
								 | 
							
								        self.parse_target_groups = {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.cc_noopt:
							 | 
						||
| 
								 | 
							
								            # skip parsing baseline and dispatch args and keep parsing target groups
							 | 
						||
| 
								 | 
							
								            cpu_baseline = cpu_dispatch = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.dist_log("check requested baseline")
							 | 
						||
| 
								 | 
							
								        if cpu_baseline is not None:
							 | 
						||
| 
								 | 
							
								            cpu_baseline = self._parse_arg_features("cpu_baseline", cpu_baseline)
							 | 
						||
| 
								 | 
							
								            baseline_names = self.feature_names(cpu_baseline)
							 | 
						||
| 
								 | 
							
								            self.parse_baseline_flags = self.feature_flags(baseline_names)
							 | 
						||
| 
								 | 
							
								            self.parse_baseline_names = self.feature_sorted(
							 | 
						||
| 
								 | 
							
								                self.feature_implies_c(baseline_names)
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.dist_log("check requested dispatch-able features")
							 | 
						||
| 
								 | 
							
								        if cpu_dispatch is not None:
							 | 
						||
| 
								 | 
							
								            cpu_dispatch_ = self._parse_arg_features("cpu_dispatch", cpu_dispatch)
							 | 
						||
| 
								 | 
							
								            cpu_dispatch = {
							 | 
						||
| 
								 | 
							
								                f for f in cpu_dispatch_
							 | 
						||
| 
								 | 
							
								                if f not in self.parse_baseline_names
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            conflict_baseline = cpu_dispatch_.difference(cpu_dispatch)
							 | 
						||
| 
								 | 
							
								            self.parse_dispatch_names = self.feature_sorted(
							 | 
						||
| 
								 | 
							
								                self.feature_names(cpu_dispatch)
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            if len(conflict_baseline) > 0:
							 | 
						||
| 
								 | 
							
								                self.dist_log(
							 | 
						||
| 
								 | 
							
								                    "skip features", conflict_baseline, "since its part of baseline"
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.dist_log("initialize targets groups")
							 | 
						||
| 
								 | 
							
								        for group_name, tokens in self.conf_target_groups.items():
							 | 
						||
| 
								 | 
							
								            self.dist_log("parse target group", group_name)
							 | 
						||
| 
								 | 
							
								            GROUP_NAME = group_name.upper()
							 | 
						||
| 
								 | 
							
								            if not tokens or not tokens.strip():
							 | 
						||
| 
								 | 
							
								                # allow empty groups, useful in case if there's a need
							 | 
						||
| 
								 | 
							
								                # to disable certain group since '_parse_target_tokens()'
							 | 
						||
| 
								 | 
							
								                # requires at least one valid target
							 | 
						||
| 
								 | 
							
								                self.parse_target_groups[GROUP_NAME] = (
							 | 
						||
| 
								 | 
							
								                    False, [], []
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            has_baseline, features, extra_flags = \
							 | 
						||
| 
								 | 
							
								                self._parse_target_tokens(tokens)
							 | 
						||
| 
								 | 
							
								            self.parse_target_groups[GROUP_NAME] = (
							 | 
						||
| 
								 | 
							
								                has_baseline, features, extra_flags
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.parse_is_cached = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def parse_targets(self, source):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Fetch and parse configuration statements that required for
							 | 
						||
| 
								 | 
							
								        defining the targeted CPU features, statements should be declared
							 | 
						||
| 
								 | 
							
								        in the top of source in between **C** comment and start
							 | 
						||
| 
								 | 
							
								        with a special mark **@targets**.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Configuration statements are sort of keywords representing
							 | 
						||
| 
								 | 
							
								        CPU features names, group of statements and policies, combined
							 | 
						||
| 
								 | 
							
								        together to determine the required optimization.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Parameters
							 | 
						||
| 
								 | 
							
								        ----------
							 | 
						||
| 
								 | 
							
								        source : str
							 | 
						||
| 
								 | 
							
								            the path of **C** source file.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Returns
							 | 
						||
| 
								 | 
							
								        -------
							 | 
						||
| 
								 | 
							
								        - bool, True if group has the 'baseline' option
							 | 
						||
| 
								 | 
							
								        - list, list of CPU features
							 | 
						||
| 
								 | 
							
								        - list, list of extra compiler flags
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        self.dist_log("looking for '@targets' inside -> ", source)
							 | 
						||
| 
								 | 
							
								        # get lines between /*@targets and */
							 | 
						||
| 
								 | 
							
								        with open(source) as fd:
							 | 
						||
| 
								 | 
							
								            tokens = ""
							 | 
						||
| 
								 | 
							
								            max_to_reach = 1000 # good enough, isn't?
							 | 
						||
| 
								 | 
							
								            start_with = "@targets"
							 | 
						||
| 
								 | 
							
								            start_pos = -1
							 | 
						||
| 
								 | 
							
								            end_with = "*/"
							 | 
						||
| 
								 | 
							
								            end_pos = -1
							 | 
						||
| 
								 | 
							
								            for current_line, line in enumerate(fd):
							 | 
						||
| 
								 | 
							
								                if current_line == max_to_reach:
							 | 
						||
| 
								 | 
							
								                    self.dist_fatal("reached the max of lines")
							 | 
						||
| 
								 | 
							
								                    break
							 | 
						||
| 
								 | 
							
								                if start_pos == -1:
							 | 
						||
| 
								 | 
							
								                    start_pos = line.find(start_with)
							 | 
						||
| 
								 | 
							
								                    if start_pos == -1:
							 | 
						||
| 
								 | 
							
								                        continue
							 | 
						||
| 
								 | 
							
								                    start_pos += len(start_with)
							 | 
						||
| 
								 | 
							
								                tokens += line
							 | 
						||
| 
								 | 
							
								                end_pos = line.find(end_with)
							 | 
						||
| 
								 | 
							
								                if end_pos != -1:
							 | 
						||
| 
								 | 
							
								                    end_pos += len(tokens) - len(line)
							 | 
						||
| 
								 | 
							
								                    break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if start_pos == -1:
							 | 
						||
| 
								 | 
							
								            self.dist_fatal("expected to find '%s' within a C comment" % start_with)
							 | 
						||
| 
								 | 
							
								        if end_pos == -1:
							 | 
						||
| 
								 | 
							
								            self.dist_fatal("expected to end with '%s'" % end_with)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        tokens = tokens[start_pos:end_pos]
							 | 
						||
| 
								 | 
							
								        return self._parse_target_tokens(tokens)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _parse_regex_arg = re.compile(r'\s|,|([+-])')
							 | 
						||
| 
								 | 
							
								    def _parse_arg_features(self, arg_name, req_features):
							 | 
						||
| 
								 | 
							
								        if not isinstance(req_features, str):
							 | 
						||
| 
								 | 
							
								            self.dist_fatal("expected a string in '%s'" % arg_name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        final_features = set()
							 | 
						||
| 
								 | 
							
								        # space and comma can be used as a separator
							 | 
						||
| 
								 | 
							
								        tokens = list(filter(None, re.split(self._parse_regex_arg, req_features)))
							 | 
						||
| 
								 | 
							
								        append = True # append is the default
							 | 
						||
| 
								 | 
							
								        for tok in tokens:
							 | 
						||
| 
								 | 
							
								            if tok[0] in ("#", "$"):
							 | 
						||
| 
								 | 
							
								                self.dist_fatal(
							 | 
						||
| 
								 | 
							
								                    arg_name, "target groups and policies "
							 | 
						||
| 
								 | 
							
								                    "aren't allowed from arguments, "
							 | 
						||
| 
								 | 
							
								                    "only from dispatch-able sources"
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            if tok == '+':
							 | 
						||
| 
								 | 
							
								                append = True
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            if tok == '-':
							 | 
						||
| 
								 | 
							
								                append = False
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            TOK = tok.upper() # we use upper-case internally
							 | 
						||
| 
								 | 
							
								            features_to = set()
							 | 
						||
| 
								 | 
							
								            if TOK == "NONE":
							 | 
						||
| 
								 | 
							
								                pass
							 | 
						||
| 
								 | 
							
								            elif TOK == "NATIVE":
							 | 
						||
| 
								 | 
							
								                native = self.cc_flags["native"]
							 | 
						||
| 
								 | 
							
								                if not native:
							 | 
						||
| 
								 | 
							
								                    self.dist_fatal(arg_name,
							 | 
						||
| 
								 | 
							
								                        "native option isn't supported by the compiler"
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                features_to = self.feature_names(
							 | 
						||
| 
								 | 
							
								                    force_flags=native, macros=[("DETECT_FEATURES", 1)]
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            elif TOK == "MAX":
							 | 
						||
| 
								 | 
							
								                features_to = self.feature_supported.keys()
							 | 
						||
| 
								 | 
							
								            elif TOK == "MIN":
							 | 
						||
| 
								 | 
							
								                features_to = self.feature_min
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                if TOK in self.feature_supported:
							 | 
						||
| 
								 | 
							
								                    features_to.add(TOK)
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    if not self.feature_is_exist(TOK):
							 | 
						||
| 
								 | 
							
								                        self.dist_fatal(arg_name,
							 | 
						||
| 
								 | 
							
								                            ", '%s' isn't a known feature or option" % tok
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								            if append:
							 | 
						||
| 
								 | 
							
								                final_features = final_features.union(features_to)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                final_features = final_features.difference(features_to)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            append = True # back to default
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return final_features
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _parse_regex_target = re.compile(r'\s|[*,/]|([()])')
							 | 
						||
| 
								 | 
							
								    def _parse_target_tokens(self, tokens):
							 | 
						||
| 
								 | 
							
								        assert(isinstance(tokens, str))
							 | 
						||
| 
								 | 
							
								        final_targets = [] # to keep it sorted as specified
							 | 
						||
| 
								 | 
							
								        extra_flags = []
							 | 
						||
| 
								 | 
							
								        has_baseline = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        skipped  = set()
							 | 
						||
| 
								 | 
							
								        policies = set()
							 | 
						||
| 
								 | 
							
								        multi_target = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        tokens = list(filter(None, re.split(self._parse_regex_target, tokens)))
							 | 
						||
| 
								 | 
							
								        if not tokens:
							 | 
						||
| 
								 | 
							
								            self.dist_fatal("expected one token at least")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for tok in tokens:
							 | 
						||
| 
								 | 
							
								            TOK = tok.upper()
							 | 
						||
| 
								 | 
							
								            ch = tok[0]
							 | 
						||
| 
								 | 
							
								            if ch in ('+', '-'):
							 | 
						||
| 
								 | 
							
								                self.dist_fatal(
							 | 
						||
| 
								 | 
							
								                    "+/- are 'not' allowed from target's groups or @targets, "
							 | 
						||
| 
								 | 
							
								                    "only from cpu_baseline and cpu_dispatch parms"
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            elif ch == '$':
							 | 
						||
| 
								 | 
							
								                if multi_target is not None:
							 | 
						||
| 
								 | 
							
								                    self.dist_fatal(
							 | 
						||
| 
								 | 
							
								                        "policies aren't allowed inside multi-target '()'"
							 | 
						||
| 
								 | 
							
								                        ", only CPU features"
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                policies.add(self._parse_token_policy(TOK))
							 | 
						||
| 
								 | 
							
								            elif ch == '#':
							 | 
						||
| 
								 | 
							
								                if multi_target is not None:
							 | 
						||
| 
								 | 
							
								                    self.dist_fatal(
							 | 
						||
| 
								 | 
							
								                        "target groups aren't allowed inside multi-target '()'"
							 | 
						||
| 
								 | 
							
								                        ", only CPU features"
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                has_baseline, final_targets, extra_flags = \
							 | 
						||
| 
								 | 
							
								                self._parse_token_group(TOK, has_baseline, final_targets, extra_flags)
							 | 
						||
| 
								 | 
							
								            elif ch == '(':
							 | 
						||
| 
								 | 
							
								                if multi_target is not None:
							 | 
						||
| 
								 | 
							
								                    self.dist_fatal("unclosed multi-target, missing ')'")
							 | 
						||
| 
								 | 
							
								                multi_target = set()
							 | 
						||
| 
								 | 
							
								            elif ch == ')':
							 | 
						||
| 
								 | 
							
								                if multi_target is None:
							 | 
						||
| 
								 | 
							
								                    self.dist_fatal("multi-target opener '(' wasn't found")
							 | 
						||
| 
								 | 
							
								                targets = self._parse_multi_target(multi_target)
							 | 
						||
| 
								 | 
							
								                if targets is None:
							 | 
						||
| 
								 | 
							
								                    skipped.add(tuple(multi_target))
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    if len(targets) == 1:
							 | 
						||
| 
								 | 
							
								                        targets = targets[0]
							 | 
						||
| 
								 | 
							
								                    if targets and targets not in final_targets:
							 | 
						||
| 
								 | 
							
								                        final_targets.append(targets)
							 | 
						||
| 
								 | 
							
								                multi_target = None # back to default
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                if TOK == "BASELINE":
							 | 
						||
| 
								 | 
							
								                    if multi_target is not None:
							 | 
						||
| 
								 | 
							
								                        self.dist_fatal("baseline isn't allowed inside multi-target '()'")
							 | 
						||
| 
								 | 
							
								                    has_baseline = True
							 | 
						||
| 
								 | 
							
								                    continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if multi_target is not None:
							 | 
						||
| 
								 | 
							
								                    multi_target.add(TOK)
							 | 
						||
| 
								 | 
							
								                    continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if not self.feature_is_exist(TOK):
							 | 
						||
| 
								 | 
							
								                    self.dist_fatal("invalid target name '%s'" % TOK)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                is_enabled = (
							 | 
						||
| 
								 | 
							
								                    TOK in self.parse_baseline_names or
							 | 
						||
| 
								 | 
							
								                    TOK in self.parse_dispatch_names
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                if  is_enabled:
							 | 
						||
| 
								 | 
							
								                    if TOK not in final_targets:
							 | 
						||
| 
								 | 
							
								                        final_targets.append(TOK)
							 | 
						||
| 
								 | 
							
								                    continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                skipped.add(TOK)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if multi_target is not None:
							 | 
						||
| 
								 | 
							
								            self.dist_fatal("unclosed multi-target, missing ')'")
							 | 
						||
| 
								 | 
							
								        if skipped:
							 | 
						||
| 
								 | 
							
								            self.dist_log(
							 | 
						||
| 
								 | 
							
								                "skip targets", skipped,
							 | 
						||
| 
								 | 
							
								                "not part of baseline or dispatch-able features"
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        final_targets = self.feature_untied(final_targets)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # add polices dependencies
							 | 
						||
| 
								 | 
							
								        for p in list(policies):
							 | 
						||
| 
								 | 
							
								            _, _, deps = self._parse_policies[p]
							 | 
						||
| 
								 | 
							
								            for d in deps:
							 | 
						||
| 
								 | 
							
								                if d in policies:
							 | 
						||
| 
								 | 
							
								                    continue
							 | 
						||
| 
								 | 
							
								                self.dist_log(
							 | 
						||
| 
								 | 
							
								                    "policy '%s' force enables '%s'" % (
							 | 
						||
| 
								 | 
							
								                    p, d
							 | 
						||
| 
								 | 
							
								                ))
							 | 
						||
| 
								 | 
							
								                policies.add(d)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # release policies filtrations
							 | 
						||
| 
								 | 
							
								        for p, (have, nhave, _) in self._parse_policies.items():
							 | 
						||
| 
								 | 
							
								            func = None
							 | 
						||
| 
								 | 
							
								            if p in policies:
							 | 
						||
| 
								 | 
							
								                func = have
							 | 
						||
| 
								 | 
							
								                self.dist_log("policy '%s' is ON" % p)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                func = nhave
							 | 
						||
| 
								 | 
							
								            if not func:
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            has_baseline, final_targets, extra_flags = func(
							 | 
						||
| 
								 | 
							
								                has_baseline, final_targets, extra_flags
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return has_baseline, final_targets, extra_flags
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _parse_token_policy(self, token):
							 | 
						||
| 
								 | 
							
								        """validate policy token"""
							 | 
						||
| 
								 | 
							
								        if len(token) <= 1 or token[-1:] == token[0]:
							 | 
						||
| 
								 | 
							
								            self.dist_fatal("'$' must stuck in the begin of policy name")
							 | 
						||
| 
								 | 
							
								        token = token[1:]
							 | 
						||
| 
								 | 
							
								        if token not in self._parse_policies:
							 | 
						||
| 
								 | 
							
								            self.dist_fatal(
							 | 
						||
| 
								 | 
							
								                "'%s' is an invalid policy name, available policies are" % token,
							 | 
						||
| 
								 | 
							
								                self._parse_policies.keys()
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        return token
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _parse_token_group(self, token, has_baseline, final_targets, extra_flags):
							 | 
						||
| 
								 | 
							
								        """validate group token"""
							 | 
						||
| 
								 | 
							
								        if len(token) <= 1 or token[-1:] == token[0]:
							 | 
						||
| 
								 | 
							
								            self.dist_fatal("'#' must stuck in the begin of group name")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        token = token[1:]
							 | 
						||
| 
								 | 
							
								        ghas_baseline, gtargets, gextra_flags = self.parse_target_groups.get(
							 | 
						||
| 
								 | 
							
								            token, (False, None, [])
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        if gtargets is None:
							 | 
						||
| 
								 | 
							
								            self.dist_fatal(
							 | 
						||
| 
								 | 
							
								                "'%s' is an invalid target group name, " % token + \
							 | 
						||
| 
								 | 
							
								                "available target groups are",
							 | 
						||
| 
								 | 
							
								                self.parse_target_groups.keys()
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        if ghas_baseline:
							 | 
						||
| 
								 | 
							
								            has_baseline = True
							 | 
						||
| 
								 | 
							
								        # always keep sorting as specified
							 | 
						||
| 
								 | 
							
								        final_targets += [f for f in gtargets if f not in final_targets]
							 | 
						||
| 
								 | 
							
								        extra_flags += [f for f in gextra_flags if f not in extra_flags]
							 | 
						||
| 
								 | 
							
								        return has_baseline, final_targets, extra_flags
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _parse_multi_target(self, targets):
							 | 
						||
| 
								 | 
							
								        """validate multi targets that defined between parentheses()"""
							 | 
						||
| 
								 | 
							
								        # remove any implied features and keep the origins
							 | 
						||
| 
								 | 
							
								        if not targets:
							 | 
						||
| 
								 | 
							
								            self.dist_fatal("empty multi-target '()'")
							 | 
						||
| 
								 | 
							
								        if not all([
							 | 
						||
| 
								 | 
							
								            self.feature_is_exist(tar) for tar in targets
							 | 
						||
| 
								 | 
							
								        ]) :
							 | 
						||
| 
								 | 
							
								            self.dist_fatal("invalid target name in multi-target", targets)
							 | 
						||
| 
								 | 
							
								        if not all([
							 | 
						||
| 
								 | 
							
								            (
							 | 
						||
| 
								 | 
							
								                tar in self.parse_baseline_names or
							 | 
						||
| 
								 | 
							
								                tar in self.parse_dispatch_names
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            for tar in targets
							 | 
						||
| 
								 | 
							
								        ]) :
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								        targets = self.feature_ahead(targets)
							 | 
						||
| 
								 | 
							
								        if not targets:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								        # force sort multi targets, so it can be comparable
							 | 
						||
| 
								 | 
							
								        targets = self.feature_sorted(targets)
							 | 
						||
| 
								 | 
							
								        targets = tuple(targets) # hashable
							 | 
						||
| 
								 | 
							
								        return targets
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _parse_policy_not_keepbase(self, has_baseline, final_targets, extra_flags):
							 | 
						||
| 
								 | 
							
								        """skip all baseline features"""
							 | 
						||
| 
								 | 
							
								        skipped = []
							 | 
						||
| 
								 | 
							
								        for tar in final_targets[:]:
							 | 
						||
| 
								 | 
							
								            is_base = False
							 | 
						||
| 
								 | 
							
								            if isinstance(tar, str):
							 | 
						||
| 
								 | 
							
								                is_base = tar in self.parse_baseline_names
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                # multi targets
							 | 
						||
| 
								 | 
							
								                is_base = all([
							 | 
						||
| 
								 | 
							
								                    f in self.parse_baseline_names
							 | 
						||
| 
								 | 
							
								                    for f in tar
							 | 
						||
| 
								 | 
							
								                ])
							 | 
						||
| 
								 | 
							
								            if is_base:
							 | 
						||
| 
								 | 
							
								                skipped.append(tar)
							 | 
						||
| 
								 | 
							
								                final_targets.remove(tar)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if skipped:
							 | 
						||
| 
								 | 
							
								            self.dist_log("skip baseline features", skipped)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return has_baseline, final_targets, extra_flags
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _parse_policy_keepsort(self, has_baseline, final_targets, extra_flags):
							 | 
						||
| 
								 | 
							
								        """leave a notice that $keep_sort is on"""
							 | 
						||
| 
								 | 
							
								        self.dist_log(
							 | 
						||
| 
								 | 
							
								            "policy 'keep_sort' is on, dispatch-able targets", final_targets, "\n"
							 | 
						||
| 
								 | 
							
								            "are 'not' sorted depend on the highest interest but"
							 | 
						||
| 
								 | 
							
								            "as specified in the dispatch-able source or the extra group"
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        return has_baseline, final_targets, extra_flags
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _parse_policy_not_keepsort(self, has_baseline, final_targets, extra_flags):
							 | 
						||
| 
								 | 
							
								        """sorted depend on the highest interest"""
							 | 
						||
| 
								 | 
							
								        final_targets = self.feature_sorted(final_targets, reverse=True)
							 | 
						||
| 
								 | 
							
								        return has_baseline, final_targets, extra_flags
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _parse_policy_maxopt(self, has_baseline, final_targets, extra_flags):
							 | 
						||
| 
								 | 
							
								        """append the compiler optimization flags"""
							 | 
						||
| 
								 | 
							
								        if self.cc_has_debug:
							 | 
						||
| 
								 | 
							
								            self.dist_log("debug mode is detected, policy 'maxopt' is skipped.")
							 | 
						||
| 
								 | 
							
								        elif self.cc_noopt:
							 | 
						||
| 
								 | 
							
								            self.dist_log("optimization is disabled, policy 'maxopt' is skipped.")
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            flags = self.cc_flags["opt"]
							 | 
						||
| 
								 | 
							
								            if not flags:
							 | 
						||
| 
								 | 
							
								                self.dist_log(
							 | 
						||
| 
								 | 
							
								                    "current compiler doesn't support optimization flags, "
							 | 
						||
| 
								 | 
							
								                    "policy 'maxopt' is skipped", stderr=True
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                extra_flags += flags
							 | 
						||
| 
								 | 
							
								        return has_baseline, final_targets, extra_flags
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _parse_policy_werror(self, has_baseline, final_targets, extra_flags):
							 | 
						||
| 
								 | 
							
								        """force warnings to treated as errors"""
							 | 
						||
| 
								 | 
							
								        flags = self.cc_flags["werror"]
							 | 
						||
| 
								 | 
							
								        if not flags:
							 | 
						||
| 
								 | 
							
								            self.dist_log(
							 | 
						||
| 
								 | 
							
								                "current compiler doesn't support werror flags, "
							 | 
						||
| 
								 | 
							
								                "warnings will 'not' treated as errors", stderr=True
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            self.dist_log("compiler warnings are treated as errors")
							 | 
						||
| 
								 | 
							
								            extra_flags += flags
							 | 
						||
| 
								 | 
							
								        return has_baseline, final_targets, extra_flags
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _parse_policy_autovec(self, has_baseline, final_targets, extra_flags):
							 | 
						||
| 
								 | 
							
								        """skip features that has no auto-vectorized support by compiler"""
							 | 
						||
| 
								 | 
							
								        skipped = []
							 | 
						||
| 
								 | 
							
								        for tar in final_targets[:]:
							 | 
						||
| 
								 | 
							
								            if isinstance(tar, str):
							 | 
						||
| 
								 | 
							
								                can = self.feature_can_autovec(tar)
							 | 
						||
| 
								 | 
							
								            else: # multiple target
							 | 
						||
| 
								 | 
							
								                can = all([
							 | 
						||
| 
								 | 
							
								                    self.feature_can_autovec(t)
							 | 
						||
| 
								 | 
							
								                    for t in tar
							 | 
						||
| 
								 | 
							
								                ])
							 | 
						||
| 
								 | 
							
								            if not can:
							 | 
						||
| 
								 | 
							
								                final_targets.remove(tar)
							 | 
						||
| 
								 | 
							
								                skipped.append(tar)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if skipped:
							 | 
						||
| 
								 | 
							
								            self.dist_log("skip non auto-vectorized features", skipped)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return has_baseline, final_targets, extra_flags
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class CCompilerOpt(_Config, _Distutils, _Cache, _CCompiler, _Feature, _Parse):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    A helper class for `CCompiler` aims to provide extra build options
							 | 
						||
| 
								 | 
							
								    to effectively control of compiler optimizations that are directly
							 | 
						||
| 
								 | 
							
								    related to CPU features.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def __init__(self, ccompiler, cpu_baseline="min", cpu_dispatch="max", cache_path=None):
							 | 
						||
| 
								 | 
							
								        _Config.__init__(self)
							 | 
						||
| 
								 | 
							
								        _Distutils.__init__(self, ccompiler)
							 | 
						||
| 
								 | 
							
								        _Cache.__init__(self, cache_path, self.dist_info(), cpu_baseline, cpu_dispatch)
							 | 
						||
| 
								 | 
							
								        _CCompiler.__init__(self)
							 | 
						||
| 
								 | 
							
								        _Feature.__init__(self)
							 | 
						||
| 
								 | 
							
								        if not self.cc_noopt and self.cc_has_native:
							 | 
						||
| 
								 | 
							
								            self.dist_log(
							 | 
						||
| 
								 | 
							
								                "native flag is specified through environment variables. "
							 | 
						||
| 
								 | 
							
								                "force cpu-baseline='native'"
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            cpu_baseline = "native"
							 | 
						||
| 
								 | 
							
								        _Parse.__init__(self, cpu_baseline, cpu_dispatch)
							 | 
						||
| 
								 | 
							
								        # keep the requested features untouched, need it later for report
							 | 
						||
| 
								 | 
							
								        # and trace purposes
							 | 
						||
| 
								 | 
							
								        self._requested_baseline = cpu_baseline
							 | 
						||
| 
								 | 
							
								        self._requested_dispatch = cpu_dispatch
							 | 
						||
| 
								 | 
							
								        # key is the dispatch-able source and value is a tuple
							 | 
						||
| 
								 | 
							
								        # contains two items (has_baseline[boolean], dispatched-features[list])
							 | 
						||
| 
								 | 
							
								        self.sources_status = getattr(self, "sources_status", {})
							 | 
						||
| 
								 | 
							
								        # every instance should has a separate one
							 | 
						||
| 
								 | 
							
								        self.cache_private.add("sources_status")
							 | 
						||
| 
								 | 
							
								        # set it at the end to make sure the cache writing was done after init
							 | 
						||
| 
								 | 
							
								        # this class
							 | 
						||
| 
								 | 
							
								        self.hit_cache = hasattr(self, "hit_cache")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def is_cached(self):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Returns True if the class loaded from the cache file
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return self.cache_infile and self.hit_cache
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def cpu_baseline_flags(self):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Returns a list of final CPU baseline compiler flags
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return self.parse_baseline_flags
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def cpu_baseline_names(self):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return a list of final CPU baseline feature names
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return self.parse_baseline_names
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def cpu_dispatch_names(self):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return a list of final CPU dispatch feature names
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return self.parse_dispatch_names
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def try_dispatch(self, sources, src_dir=None, ccompiler=None, **kwargs):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Compile one or more dispatch-able sources and generates object files,
							 | 
						||
| 
								 | 
							
								        also generates abstract C config headers and macros that
							 | 
						||
| 
								 | 
							
								        used later for the final runtime dispatching process.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        The mechanism behind it is to takes each source file that specified
							 | 
						||
| 
								 | 
							
								        in 'sources' and branching it into several files depend on
							 | 
						||
| 
								 | 
							
								        special configuration statements that must be declared in the
							 | 
						||
| 
								 | 
							
								        top of each source which contains targeted CPU features,
							 | 
						||
| 
								 | 
							
								        then it compiles every branched source with the proper compiler flags.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Parameters
							 | 
						||
| 
								 | 
							
								        ----------
							 | 
						||
| 
								 | 
							
								        sources : list
							 | 
						||
| 
								 | 
							
								            Must be a list of dispatch-able sources file paths,
							 | 
						||
| 
								 | 
							
								            and configuration statements must be declared inside
							 | 
						||
| 
								 | 
							
								            each file.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        src_dir : str
							 | 
						||
| 
								 | 
							
								            Path of parent directory for the generated headers and wrapped sources.
							 | 
						||
| 
								 | 
							
								            If None(default) the files will generated in-place.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        ccompiler : CCompiler
							 | 
						||
| 
								 | 
							
								            Distutils `CCompiler` instance to be used for compilation.
							 | 
						||
| 
								 | 
							
								            If None (default), the provided instance during the initialization
							 | 
						||
| 
								 | 
							
								            will be used instead.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        **kwargs : any
							 | 
						||
| 
								 | 
							
								            Arguments to pass on to the `CCompiler.compile()`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Returns
							 | 
						||
| 
								 | 
							
								        -------
							 | 
						||
| 
								 | 
							
								        list : generated object files
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Raises
							 | 
						||
| 
								 | 
							
								        ------
							 | 
						||
| 
								 | 
							
								        CompileError
							 | 
						||
| 
								 | 
							
								            Raises by `CCompiler.compile()` on compiling failure.
							 | 
						||
| 
								 | 
							
								        DistutilsError
							 | 
						||
| 
								 | 
							
								            Some errors during checking the sanity of configuration statements.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        See Also
							 | 
						||
| 
								 | 
							
								        --------
							 | 
						||
| 
								 | 
							
								        parse_targets :
							 | 
						||
| 
								 | 
							
								            Parsing the configuration statements of dispatch-able sources.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        to_compile = {}
							 | 
						||
| 
								 | 
							
								        baseline_flags = self.cpu_baseline_flags()
							 | 
						||
| 
								 | 
							
								        include_dirs = kwargs.setdefault("include_dirs", [])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for src in sources:
							 | 
						||
| 
								 | 
							
								            output_dir = os.path.dirname(src)
							 | 
						||
| 
								 | 
							
								            if src_dir:
							 | 
						||
| 
								 | 
							
								                if not output_dir.startswith(src_dir):
							 | 
						||
| 
								 | 
							
								                    output_dir = os.path.join(src_dir, output_dir)
							 | 
						||
| 
								 | 
							
								                if output_dir not in include_dirs:
							 | 
						||
| 
								 | 
							
								                    # To allow including the generated config header(*.dispatch.h)
							 | 
						||
| 
								 | 
							
								                    # by the dispatch-able sources
							 | 
						||
| 
								 | 
							
								                    include_dirs.append(output_dir)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            has_baseline, targets, extra_flags = self.parse_targets(src)
							 | 
						||
| 
								 | 
							
								            nochange = self._generate_config(output_dir, src, targets, has_baseline)
							 | 
						||
| 
								 | 
							
								            for tar in targets:
							 | 
						||
| 
								 | 
							
								                tar_src = self._wrap_target(output_dir, src, tar, nochange=nochange)
							 | 
						||
| 
								 | 
							
								                flags = tuple(extra_flags + self.feature_flags(tar))
							 | 
						||
| 
								 | 
							
								                to_compile.setdefault(flags, []).append(tar_src)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if has_baseline:
							 | 
						||
| 
								 | 
							
								                flags = tuple(extra_flags + baseline_flags)
							 | 
						||
| 
								 | 
							
								                to_compile.setdefault(flags, []).append(src)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            self.sources_status[src] = (has_baseline, targets)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # For these reasons, the sources are compiled in a separate loop:
							 | 
						||
| 
								 | 
							
								        # - Gathering all sources with the same flags to benefit from
							 | 
						||
| 
								 | 
							
								        #   the parallel compiling as much as possible.
							 | 
						||
| 
								 | 
							
								        # - To generate all config headers of the dispatchable sources,
							 | 
						||
| 
								 | 
							
								        #   before the compilation in case if there are dependency relationships
							 | 
						||
| 
								 | 
							
								        #   among them.
							 | 
						||
| 
								 | 
							
								        objects = []
							 | 
						||
| 
								 | 
							
								        for flags, srcs in to_compile.items():
							 | 
						||
| 
								 | 
							
								            objects += self.dist_compile(
							 | 
						||
| 
								 | 
							
								                srcs, list(flags), ccompiler=ccompiler, **kwargs
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        return objects
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def generate_dispatch_header(self, header_path):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Generate the dispatch header which contains the #definitions and headers
							 | 
						||
| 
								 | 
							
								        for platform-specific instruction-sets for the enabled CPU baseline and
							 | 
						||
| 
								 | 
							
								        dispatch-able features.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Its highly recommended to take a look at the generated header
							 | 
						||
| 
								 | 
							
								        also the generated source files via `try_dispatch()`
							 | 
						||
| 
								 | 
							
								        in order to get the full picture.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        self.dist_log("generate CPU dispatch header: (%s)" % header_path)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        baseline_names = self.cpu_baseline_names()
							 | 
						||
| 
								 | 
							
								        dispatch_names = self.cpu_dispatch_names()
							 | 
						||
| 
								 | 
							
								        baseline_len = len(baseline_names)
							 | 
						||
| 
								 | 
							
								        dispatch_len = len(dispatch_names)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        header_dir = os.path.dirname(header_path)
							 | 
						||
| 
								 | 
							
								        if not os.path.exists(header_dir):
							 | 
						||
| 
								 | 
							
								            self.dist_log(
							 | 
						||
| 
								 | 
							
								                f"dispatch header dir {header_dir} does not exist, creating it",
							 | 
						||
| 
								 | 
							
								                stderr=True
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            os.makedirs(header_dir)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        with open(header_path, 'w') as f:
							 | 
						||
| 
								 | 
							
								            baseline_calls = ' \\\n'.join([
							 | 
						||
| 
								 | 
							
								                (
							 | 
						||
| 
								 | 
							
								                    "\t%sWITH_CPU_EXPAND_(MACRO_TO_CALL(%s, __VA_ARGS__))"
							 | 
						||
| 
								 | 
							
								                ) % (self.conf_c_prefix, f)
							 | 
						||
| 
								 | 
							
								                for f in baseline_names
							 | 
						||
| 
								 | 
							
								            ])
							 | 
						||
| 
								 | 
							
								            dispatch_calls = ' \\\n'.join([
							 | 
						||
| 
								 | 
							
								                (
							 | 
						||
| 
								 | 
							
								                    "\t%sWITH_CPU_EXPAND_(MACRO_TO_CALL(%s, __VA_ARGS__))"
							 | 
						||
| 
								 | 
							
								                ) % (self.conf_c_prefix, f)
							 | 
						||
| 
								 | 
							
								                for f in dispatch_names
							 | 
						||
| 
								 | 
							
								            ])
							 | 
						||
| 
								 | 
							
								            f.write(textwrap.dedent("""\
							 | 
						||
| 
								 | 
							
								                /*
							 | 
						||
| 
								 | 
							
								                 * AUTOGENERATED DON'T EDIT
							 | 
						||
| 
								 | 
							
								                 * Please make changes to the code generator (distutils/ccompiler_opt.py)
							 | 
						||
| 
								 | 
							
								                */
							 | 
						||
| 
								 | 
							
								                #define {pfx}WITH_CPU_BASELINE  "{baseline_str}"
							 | 
						||
| 
								 | 
							
								                #define {pfx}WITH_CPU_DISPATCH  "{dispatch_str}"
							 | 
						||
| 
								 | 
							
								                #define {pfx}WITH_CPU_BASELINE_N {baseline_len}
							 | 
						||
| 
								 | 
							
								                #define {pfx}WITH_CPU_DISPATCH_N {dispatch_len}
							 | 
						||
| 
								 | 
							
								                #define {pfx}WITH_CPU_EXPAND_(X) X
							 | 
						||
| 
								 | 
							
								                #define {pfx}WITH_CPU_BASELINE_CALL(MACRO_TO_CALL, ...) \\
							 | 
						||
| 
								 | 
							
								                {baseline_calls}
							 | 
						||
| 
								 | 
							
								                #define {pfx}WITH_CPU_DISPATCH_CALL(MACRO_TO_CALL, ...) \\
							 | 
						||
| 
								 | 
							
								                {dispatch_calls}
							 | 
						||
| 
								 | 
							
								            """).format(
							 | 
						||
| 
								 | 
							
								                pfx=self.conf_c_prefix, baseline_str=" ".join(baseline_names),
							 | 
						||
| 
								 | 
							
								                dispatch_str=" ".join(dispatch_names), baseline_len=baseline_len,
							 | 
						||
| 
								 | 
							
								                dispatch_len=dispatch_len, baseline_calls=baseline_calls,
							 | 
						||
| 
								 | 
							
								                dispatch_calls=dispatch_calls
							 | 
						||
| 
								 | 
							
								            ))
							 | 
						||
| 
								 | 
							
								            baseline_pre = ''
							 | 
						||
| 
								 | 
							
								            for name in baseline_names:
							 | 
						||
| 
								 | 
							
								                baseline_pre += self.feature_c_preprocessor(name, tabs=1) + '\n'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            dispatch_pre = ''
							 | 
						||
| 
								 | 
							
								            for name in dispatch_names:
							 | 
						||
| 
								 | 
							
								                dispatch_pre += textwrap.dedent("""\
							 | 
						||
| 
								 | 
							
								                #ifdef {pfx}CPU_TARGET_{name}
							 | 
						||
| 
								 | 
							
								                {pre}
							 | 
						||
| 
								 | 
							
								                #endif /*{pfx}CPU_TARGET_{name}*/
							 | 
						||
| 
								 | 
							
								                """).format(
							 | 
						||
| 
								 | 
							
								                    pfx=self.conf_c_prefix_, name=name, pre=self.feature_c_preprocessor(
							 | 
						||
| 
								 | 
							
								                    name, tabs=1
							 | 
						||
| 
								 | 
							
								                ))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            f.write(textwrap.dedent("""\
							 | 
						||
| 
								 | 
							
								            /******* baseline features *******/
							 | 
						||
| 
								 | 
							
								            {baseline_pre}
							 | 
						||
| 
								 | 
							
								            /******* dispatch features *******/
							 | 
						||
| 
								 | 
							
								            {dispatch_pre}
							 | 
						||
| 
								 | 
							
								            """).format(
							 | 
						||
| 
								 | 
							
								                pfx=self.conf_c_prefix_, baseline_pre=baseline_pre,
							 | 
						||
| 
								 | 
							
								                dispatch_pre=dispatch_pre
							 | 
						||
| 
								 | 
							
								            ))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def report(self, full=False):
							 | 
						||
| 
								 | 
							
								        report = []
							 | 
						||
| 
								 | 
							
								        platform_rows = []
							 | 
						||
| 
								 | 
							
								        baseline_rows = []
							 | 
						||
| 
								 | 
							
								        dispatch_rows = []
							 | 
						||
| 
								 | 
							
								        report.append(("Platform", platform_rows))
							 | 
						||
| 
								 | 
							
								        report.append(("", ""))
							 | 
						||
| 
								 | 
							
								        report.append(("CPU baseline", baseline_rows))
							 | 
						||
| 
								 | 
							
								        report.append(("", ""))
							 | 
						||
| 
								 | 
							
								        report.append(("CPU dispatch", dispatch_rows))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        ########## platform ##########
							 | 
						||
| 
								 | 
							
								        platform_rows.append(("Architecture", (
							 | 
						||
| 
								 | 
							
								            "unsupported" if self.cc_on_noarch else self.cc_march)
							 | 
						||
| 
								 | 
							
								        ))
							 | 
						||
| 
								 | 
							
								        platform_rows.append(("Compiler", (
							 | 
						||
| 
								 | 
							
								            "unix-like"   if self.cc_is_nocc   else self.cc_name)
							 | 
						||
| 
								 | 
							
								        ))
							 | 
						||
| 
								 | 
							
								        ########## baseline ##########
							 | 
						||
| 
								 | 
							
								        if self.cc_noopt:
							 | 
						||
| 
								 | 
							
								            baseline_rows.append(("Requested", "optimization disabled"))
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            baseline_rows.append(("Requested", repr(self._requested_baseline)))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        baseline_names = self.cpu_baseline_names()
							 | 
						||
| 
								 | 
							
								        baseline_rows.append((
							 | 
						||
| 
								 | 
							
								            "Enabled", (' '.join(baseline_names) if baseline_names else "none")
							 | 
						||
| 
								 | 
							
								        ))
							 | 
						||
| 
								 | 
							
								        baseline_flags = self.cpu_baseline_flags()
							 | 
						||
| 
								 | 
							
								        baseline_rows.append((
							 | 
						||
| 
								 | 
							
								            "Flags", (' '.join(baseline_flags) if baseline_flags else "none")
							 | 
						||
| 
								 | 
							
								        ))
							 | 
						||
| 
								 | 
							
								        extra_checks = []
							 | 
						||
| 
								 | 
							
								        for name in baseline_names:
							 | 
						||
| 
								 | 
							
								            extra_checks += self.feature_extra_checks(name)
							 | 
						||
| 
								 | 
							
								        baseline_rows.append((
							 | 
						||
| 
								 | 
							
								            "Extra checks", (' '.join(extra_checks) if extra_checks else "none")
							 | 
						||
| 
								 | 
							
								        ))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        ########## dispatch ##########
							 | 
						||
| 
								 | 
							
								        if self.cc_noopt:
							 | 
						||
| 
								 | 
							
								            baseline_rows.append(("Requested", "optimization disabled"))
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            dispatch_rows.append(("Requested", repr(self._requested_dispatch)))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        dispatch_names = self.cpu_dispatch_names()
							 | 
						||
| 
								 | 
							
								        dispatch_rows.append((
							 | 
						||
| 
								 | 
							
								            "Enabled", (' '.join(dispatch_names) if dispatch_names else "none")
							 | 
						||
| 
								 | 
							
								        ))
							 | 
						||
| 
								 | 
							
								        ########## Generated ##########
							 | 
						||
| 
								 | 
							
								        # TODO:
							 | 
						||
| 
								 | 
							
								        # - collect object names from 'try_dispatch()'
							 | 
						||
| 
								 | 
							
								        #   then get size of each object and printed
							 | 
						||
| 
								 | 
							
								        # - give more details about the features that not
							 | 
						||
| 
								 | 
							
								        #   generated due compiler support
							 | 
						||
| 
								 | 
							
								        # - find a better output's design.
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        target_sources = {}
							 | 
						||
| 
								 | 
							
								        for source, (_, targets) in self.sources_status.items():
							 | 
						||
| 
								 | 
							
								            for tar in targets:
							 | 
						||
| 
								 | 
							
								                target_sources.setdefault(tar, []).append(source)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not full or not target_sources:
							 | 
						||
| 
								 | 
							
								            generated = ""
							 | 
						||
| 
								 | 
							
								            for tar in self.feature_sorted(target_sources):
							 | 
						||
| 
								 | 
							
								                sources = target_sources[tar]
							 | 
						||
| 
								 | 
							
								                name = tar if isinstance(tar, str) else '(%s)' % ' '.join(tar)
							 | 
						||
| 
								 | 
							
								                generated += name + "[%d] " % len(sources)
							 | 
						||
| 
								 | 
							
								            dispatch_rows.append(("Generated", generated[:-1] if generated else "none"))
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            dispatch_rows.append(("Generated", ''))
							 | 
						||
| 
								 | 
							
								            for tar in self.feature_sorted(target_sources):
							 | 
						||
| 
								 | 
							
								                sources = target_sources[tar]
							 | 
						||
| 
								 | 
							
								                pretty_name = tar if isinstance(tar, str) else '(%s)' % ' '.join(tar)
							 | 
						||
| 
								 | 
							
								                flags = ' '.join(self.feature_flags(tar))
							 | 
						||
| 
								 | 
							
								                implies = ' '.join(self.feature_sorted(self.feature_implies(tar)))
							 | 
						||
| 
								 | 
							
								                detect = ' '.join(self.feature_detect(tar))
							 | 
						||
| 
								 | 
							
								                extra_checks = []
							 | 
						||
| 
								 | 
							
								                for name in ((tar,) if isinstance(tar, str) else tar):
							 | 
						||
| 
								 | 
							
								                    extra_checks += self.feature_extra_checks(name)
							 | 
						||
| 
								 | 
							
								                extra_checks = (' '.join(extra_checks) if extra_checks else "none")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                dispatch_rows.append(('', ''))
							 | 
						||
| 
								 | 
							
								                dispatch_rows.append((pretty_name, implies))
							 | 
						||
| 
								 | 
							
								                dispatch_rows.append(("Flags", flags))
							 | 
						||
| 
								 | 
							
								                dispatch_rows.append(("Extra checks", extra_checks))
							 | 
						||
| 
								 | 
							
								                dispatch_rows.append(("Detect", detect))
							 | 
						||
| 
								 | 
							
								                for src in sources:
							 | 
						||
| 
								 | 
							
								                    dispatch_rows.append(("", src))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        ###############################
							 | 
						||
| 
								 | 
							
								        # TODO: add support for 'markdown' format
							 | 
						||
| 
								 | 
							
								        text = []
							 | 
						||
| 
								 | 
							
								        secs_len = [len(secs) for secs, _ in report]
							 | 
						||
| 
								 | 
							
								        cols_len = [len(col) for _, rows in report for col, _ in rows]
							 | 
						||
| 
								 | 
							
								        tab = ' ' * 2
							 | 
						||
| 
								 | 
							
								        pad =  max(max(secs_len), max(cols_len))
							 | 
						||
| 
								 | 
							
								        for sec, rows in report:
							 | 
						||
| 
								 | 
							
								            if not sec:
							 | 
						||
| 
								 | 
							
								                text.append("") # empty line
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            sec += ' ' * (pad - len(sec))
							 | 
						||
| 
								 | 
							
								            text.append(sec + tab + ': ')
							 | 
						||
| 
								 | 
							
								            for col, val in rows:
							 | 
						||
| 
								 | 
							
								                col += ' ' * (pad - len(col))
							 | 
						||
| 
								 | 
							
								                text.append(tab + col + ': ' + val)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return '\n'.join(text)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _wrap_target(self, output_dir, dispatch_src, target, nochange=False):
							 | 
						||
| 
								 | 
							
								        assert(isinstance(target, (str, tuple)))
							 | 
						||
| 
								 | 
							
								        if isinstance(target, str):
							 | 
						||
| 
								 | 
							
								            ext_name = target_name = target
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            # multi-target
							 | 
						||
| 
								 | 
							
								            ext_name = '.'.join(target)
							 | 
						||
| 
								 | 
							
								            target_name = '__'.join(target)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        wrap_path = os.path.join(output_dir, os.path.basename(dispatch_src))
							 | 
						||
| 
								 | 
							
								        wrap_path = "{0}.{2}{1}".format(*os.path.splitext(wrap_path), ext_name.lower())
							 | 
						||
| 
								 | 
							
								        if nochange and os.path.exists(wrap_path):
							 | 
						||
| 
								 | 
							
								            return wrap_path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.dist_log("wrap dispatch-able target -> ", wrap_path)
							 | 
						||
| 
								 | 
							
								        # sorting for readability
							 | 
						||
| 
								 | 
							
								        features = self.feature_sorted(self.feature_implies_c(target))
							 | 
						||
| 
								 | 
							
								        target_join = "#define %sCPU_TARGET_" % self.conf_c_prefix_
							 | 
						||
| 
								 | 
							
								        target_defs = [target_join + f for f in features]
							 | 
						||
| 
								 | 
							
								        target_defs = '\n'.join(target_defs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        with open(wrap_path, "w") as fd:
							 | 
						||
| 
								 | 
							
								            fd.write(textwrap.dedent("""\
							 | 
						||
| 
								 | 
							
								            /**
							 | 
						||
| 
								 | 
							
								             * AUTOGENERATED DON'T EDIT
							 | 
						||
| 
								 | 
							
								             * Please make changes to the code generator \
							 | 
						||
| 
								 | 
							
								             (distutils/ccompiler_opt.py)
							 | 
						||
| 
								 | 
							
								             */
							 | 
						||
| 
								 | 
							
								            #define {pfx}CPU_TARGET_MODE
							 | 
						||
| 
								 | 
							
								            #define {pfx}CPU_TARGET_CURRENT {target_name}
							 | 
						||
| 
								 | 
							
								            {target_defs}
							 | 
						||
| 
								 | 
							
								            #include "{path}"
							 | 
						||
| 
								 | 
							
								            """).format(
							 | 
						||
| 
								 | 
							
								                pfx=self.conf_c_prefix_, target_name=target_name,
							 | 
						||
| 
								 | 
							
								                path=os.path.abspath(dispatch_src), target_defs=target_defs
							 | 
						||
| 
								 | 
							
								            ))
							 | 
						||
| 
								 | 
							
								        return wrap_path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _generate_config(self, output_dir, dispatch_src, targets, has_baseline=False):
							 | 
						||
| 
								 | 
							
								        config_path = os.path.basename(dispatch_src)
							 | 
						||
| 
								 | 
							
								        config_path = os.path.splitext(config_path)[0] + '.h'
							 | 
						||
| 
								 | 
							
								        config_path = os.path.join(output_dir, config_path)
							 | 
						||
| 
								 | 
							
								        # check if targets didn't change to avoid recompiling
							 | 
						||
| 
								 | 
							
								        cache_hash = self.cache_hash(targets, has_baseline)
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            with open(config_path) as f:
							 | 
						||
| 
								 | 
							
								                last_hash = f.readline().split("cache_hash:")
							 | 
						||
| 
								 | 
							
								                if len(last_hash) == 2 and int(last_hash[1]) == cache_hash:
							 | 
						||
| 
								 | 
							
								                    return True
							 | 
						||
| 
								 | 
							
								        except OSError:
							 | 
						||
| 
								 | 
							
								            pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        os.makedirs(os.path.dirname(config_path), exist_ok=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.dist_log("generate dispatched config -> ", config_path)
							 | 
						||
| 
								 | 
							
								        dispatch_calls = []
							 | 
						||
| 
								 | 
							
								        for tar in targets:
							 | 
						||
| 
								 | 
							
								            if isinstance(tar, str):
							 | 
						||
| 
								 | 
							
								                target_name = tar
							 | 
						||
| 
								 | 
							
								            else: # multi target
							 | 
						||
| 
								 | 
							
								                target_name = '__'.join([t for t in tar])
							 | 
						||
| 
								 | 
							
								            req_detect = self.feature_detect(tar)
							 | 
						||
| 
								 | 
							
								            req_detect = '&&'.join([
							 | 
						||
| 
								 | 
							
								                "CHK(%s)" % f for f in req_detect
							 | 
						||
| 
								 | 
							
								            ])
							 | 
						||
| 
								 | 
							
								            dispatch_calls.append(
							 | 
						||
| 
								 | 
							
								                "\t%sCPU_DISPATCH_EXPAND_(CB((%s), %s, __VA_ARGS__))" % (
							 | 
						||
| 
								 | 
							
								                self.conf_c_prefix_, req_detect, target_name
							 | 
						||
| 
								 | 
							
								            ))
							 | 
						||
| 
								 | 
							
								        dispatch_calls = ' \\\n'.join(dispatch_calls)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if has_baseline:
							 | 
						||
| 
								 | 
							
								            baseline_calls = (
							 | 
						||
| 
								 | 
							
								                "\t%sCPU_DISPATCH_EXPAND_(CB(__VA_ARGS__))"
							 | 
						||
| 
								 | 
							
								            ) % self.conf_c_prefix_
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            baseline_calls = ''
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        with open(config_path, "w") as fd:
							 | 
						||
| 
								 | 
							
								            fd.write(textwrap.dedent("""\
							 | 
						||
| 
								 | 
							
								            // cache_hash:{cache_hash}
							 | 
						||
| 
								 | 
							
								            /**
							 | 
						||
| 
								 | 
							
								             * AUTOGENERATED DON'T EDIT
							 | 
						||
| 
								 | 
							
								             * Please make changes to the code generator (distutils/ccompiler_opt.py)
							 | 
						||
| 
								 | 
							
								             */
							 | 
						||
| 
								 | 
							
								            #ifndef {pfx}CPU_DISPATCH_EXPAND_
							 | 
						||
| 
								 | 
							
								                #define {pfx}CPU_DISPATCH_EXPAND_(X) X
							 | 
						||
| 
								 | 
							
								            #endif
							 | 
						||
| 
								 | 
							
								            #undef {pfx}CPU_DISPATCH_BASELINE_CALL
							 | 
						||
| 
								 | 
							
								            #undef {pfx}CPU_DISPATCH_CALL
							 | 
						||
| 
								 | 
							
								            #define {pfx}CPU_DISPATCH_BASELINE_CALL(CB, ...) \\
							 | 
						||
| 
								 | 
							
								            {baseline_calls}
							 | 
						||
| 
								 | 
							
								            #define {pfx}CPU_DISPATCH_CALL(CHK, CB, ...) \\
							 | 
						||
| 
								 | 
							
								            {dispatch_calls}
							 | 
						||
| 
								 | 
							
								            """).format(
							 | 
						||
| 
								 | 
							
								                pfx=self.conf_c_prefix_, baseline_calls=baseline_calls,
							 | 
						||
| 
								 | 
							
								                dispatch_calls=dispatch_calls, cache_hash=cache_hash
							 | 
						||
| 
								 | 
							
								            ))
							 | 
						||
| 
								 | 
							
								        return False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def new_ccompiler_opt(compiler, dispatch_hpath, **kwargs):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Create a new instance of 'CCompilerOpt' and generate the dispatch header
							 | 
						||
| 
								 | 
							
								    which contains the #definitions and headers of platform-specific instruction-sets for
							 | 
						||
| 
								 | 
							
								    the enabled CPU baseline and dispatch-able features.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Parameters
							 | 
						||
| 
								 | 
							
								    ----------
							 | 
						||
| 
								 | 
							
								    compiler : CCompiler instance
							 | 
						||
| 
								 | 
							
								    dispatch_hpath : str
							 | 
						||
| 
								 | 
							
								        path of the dispatch header
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    **kwargs: passed as-is to `CCompilerOpt(...)`
							 | 
						||
| 
								 | 
							
								    Returns
							 | 
						||
| 
								 | 
							
								    -------
							 | 
						||
| 
								 | 
							
								    new instance of CCompilerOpt
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    opt = CCompilerOpt(compiler, **kwargs)
							 | 
						||
| 
								 | 
							
								    if not os.path.exists(dispatch_hpath) or not opt.is_cached():
							 | 
						||
| 
								 | 
							
								        opt.generate_dispatch_header(dispatch_hpath)
							 | 
						||
| 
								 | 
							
								    return opt
							 |