reconnect moved files to git repo
This commit is contained in:
@ -0,0 +1,116 @@
|
||||
"""
|
||||
Utilities useful during the build.
|
||||
"""
|
||||
|
||||
# author: Andy Mueller, Gael Varoquaux
|
||||
# license: BSD
|
||||
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
|
||||
import sklearn
|
||||
|
||||
from .._min_dependencies import CYTHON_MIN_VERSION
|
||||
from ..externals._packaging.version import parse
|
||||
from .openmp_helpers import check_openmp_support
|
||||
from .pre_build_helpers import basic_check_build
|
||||
|
||||
DEFAULT_ROOT = "sklearn"
|
||||
|
||||
|
||||
def _check_cython_version():
|
||||
message = (
|
||||
"Please install Cython with a version >= {0} in order "
|
||||
"to build a scikit-learn from source."
|
||||
).format(CYTHON_MIN_VERSION)
|
||||
try:
|
||||
import Cython
|
||||
except ModuleNotFoundError as e:
|
||||
# Re-raise with more informative error message instead:
|
||||
raise ModuleNotFoundError(message) from e
|
||||
|
||||
if parse(Cython.__version__) < parse(CYTHON_MIN_VERSION):
|
||||
message += " The current version of Cython is {} installed in {}.".format(
|
||||
Cython.__version__, Cython.__path__
|
||||
)
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def cythonize_extensions(extension):
|
||||
"""Check that a recent Cython is available and cythonize extensions"""
|
||||
_check_cython_version()
|
||||
from Cython.Build import cythonize
|
||||
|
||||
# Fast fail before cythonization if compiler fails compiling basic test
|
||||
# code even without OpenMP
|
||||
basic_check_build()
|
||||
|
||||
# check simple compilation with OpenMP. If it fails scikit-learn will be
|
||||
# built without OpenMP and the test test_openmp_supported in the test suite
|
||||
# will fail.
|
||||
# `check_openmp_support` compiles a small test program to see if the
|
||||
# compilers are properly configured to build with OpenMP. This is expensive
|
||||
# and we only want to call this function once.
|
||||
# The result of this check is cached as a private attribute on the sklearn
|
||||
# module (only at build-time) to be used in the build_ext subclass defined
|
||||
# in the top-level setup.py file to actually build the compiled extensions
|
||||
# with OpenMP flags if needed.
|
||||
sklearn._OPENMP_SUPPORTED = check_openmp_support()
|
||||
|
||||
n_jobs = 1
|
||||
with contextlib.suppress(ImportError):
|
||||
import joblib
|
||||
|
||||
n_jobs = joblib.cpu_count()
|
||||
|
||||
# Additional checks for Cython
|
||||
cython_enable_debug_directives = (
|
||||
os.environ.get("SKLEARN_ENABLE_DEBUG_CYTHON_DIRECTIVES", "0") != "0"
|
||||
)
|
||||
|
||||
compiler_directives = {
|
||||
"language_level": 3,
|
||||
"boundscheck": cython_enable_debug_directives,
|
||||
"wraparound": False,
|
||||
"initializedcheck": False,
|
||||
"nonecheck": False,
|
||||
"cdivision": True,
|
||||
"profile": False,
|
||||
}
|
||||
|
||||
return cythonize(
|
||||
extension,
|
||||
nthreads=n_jobs,
|
||||
compiler_directives=compiler_directives,
|
||||
annotate=False,
|
||||
)
|
||||
|
||||
|
||||
def gen_from_templates(templates):
|
||||
"""Generate cython files from a list of templates"""
|
||||
# Lazy import because cython is not a runtime dependency.
|
||||
from Cython import Tempita
|
||||
|
||||
for template in templates:
|
||||
outfile = template.replace(".tp", "")
|
||||
|
||||
# if the template is not updated, no need to output the cython file
|
||||
if not (
|
||||
os.path.exists(outfile)
|
||||
and os.stat(template).st_mtime < os.stat(outfile).st_mtime
|
||||
):
|
||||
with open(template, "r") as f:
|
||||
tmpl = f.read()
|
||||
|
||||
tmpl_ = Tempita.sub(tmpl)
|
||||
|
||||
warn_msg = (
|
||||
"# WARNING: Do not edit this file directly.\n"
|
||||
f"# It is automatically generated from {template!r}.\n"
|
||||
"# Changes must be made there.\n\n"
|
||||
)
|
||||
|
||||
with open(outfile, "w") as f:
|
||||
f.write(warn_msg)
|
||||
f.write(tmpl_)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,127 @@
|
||||
"""Helpers for OpenMP support during the build."""
|
||||
|
||||
# This code is adapted for a large part from the astropy openmp helpers, which
|
||||
# can be found at: https://github.com/astropy/extension-helpers/blob/master/extension_helpers/_openmp_helpers.py # noqa
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
import warnings
|
||||
|
||||
from .pre_build_helpers import compile_test_program
|
||||
|
||||
|
||||
def get_openmp_flag():
|
||||
if sys.platform == "win32":
|
||||
return ["/openmp"]
|
||||
elif sys.platform == "darwin" and "openmp" in os.getenv("CPPFLAGS", ""):
|
||||
# -fopenmp can't be passed as compile flag when using Apple-clang.
|
||||
# OpenMP support has to be enabled during preprocessing.
|
||||
#
|
||||
# For example, our macOS wheel build jobs use the following environment
|
||||
# variables to build with Apple-clang and the brew installed "libomp":
|
||||
#
|
||||
# export CPPFLAGS="$CPPFLAGS -Xpreprocessor -fopenmp"
|
||||
# export CFLAGS="$CFLAGS -I/usr/local/opt/libomp/include"
|
||||
# export CXXFLAGS="$CXXFLAGS -I/usr/local/opt/libomp/include"
|
||||
# export LDFLAGS="$LDFLAGS -Wl,-rpath,/usr/local/opt/libomp/lib
|
||||
# -L/usr/local/opt/libomp/lib -lomp"
|
||||
return []
|
||||
# Default flag for GCC and clang:
|
||||
return ["-fopenmp"]
|
||||
|
||||
|
||||
def check_openmp_support():
|
||||
"""Check whether OpenMP test code can be compiled and run"""
|
||||
if "PYODIDE" in os.environ:
|
||||
# Pyodide doesn't support OpenMP
|
||||
return False
|
||||
|
||||
code = textwrap.dedent(
|
||||
"""\
|
||||
#include <omp.h>
|
||||
#include <stdio.h>
|
||||
int main(void) {
|
||||
#pragma omp parallel
|
||||
printf("nthreads=%d\\n", omp_get_num_threads());
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
extra_preargs = os.getenv("LDFLAGS", None)
|
||||
if extra_preargs is not None:
|
||||
extra_preargs = extra_preargs.strip().split(" ")
|
||||
# FIXME: temporary fix to link against system libraries on linux
|
||||
# "-Wl,--sysroot=/" should be removed
|
||||
extra_preargs = [
|
||||
flag
|
||||
for flag in extra_preargs
|
||||
if flag.startswith(("-L", "-Wl,-rpath", "-l", "-Wl,--sysroot=/"))
|
||||
]
|
||||
|
||||
extra_postargs = get_openmp_flag()
|
||||
|
||||
openmp_exception = None
|
||||
try:
|
||||
output = compile_test_program(
|
||||
code, extra_preargs=extra_preargs, extra_postargs=extra_postargs
|
||||
)
|
||||
|
||||
if output and "nthreads=" in output[0]:
|
||||
nthreads = int(output[0].strip().split("=")[1])
|
||||
openmp_supported = len(output) == nthreads
|
||||
elif "PYTHON_CROSSENV" in os.environ:
|
||||
# Since we can't run the test program when cross-compiling
|
||||
# assume that openmp is supported if the program can be
|
||||
# compiled.
|
||||
openmp_supported = True
|
||||
else:
|
||||
openmp_supported = False
|
||||
|
||||
except Exception as exception:
|
||||
# We could be more specific and only catch: CompileError, LinkError,
|
||||
# and subprocess.CalledProcessError.
|
||||
# setuptools introduced CompileError and LinkError, but that requires
|
||||
# version 61.1. Even the latest version of Ubuntu (22.04LTS) only
|
||||
# ships with 59.6. So for now we catch all exceptions and reraise a
|
||||
# generic exception with the original error message instead:
|
||||
openmp_supported = False
|
||||
openmp_exception = exception
|
||||
|
||||
if not openmp_supported:
|
||||
if os.getenv("SKLEARN_FAIL_NO_OPENMP"):
|
||||
raise Exception(
|
||||
"Failed to build scikit-learn with OpenMP support"
|
||||
) from openmp_exception
|
||||
else:
|
||||
message = textwrap.dedent(
|
||||
"""
|
||||
|
||||
***********
|
||||
* WARNING *
|
||||
***********
|
||||
|
||||
It seems that scikit-learn cannot be built with OpenMP.
|
||||
|
||||
- Make sure you have followed the installation instructions:
|
||||
|
||||
https://scikit-learn.org/dev/developers/advanced_installation.html
|
||||
|
||||
- If your compiler supports OpenMP but you still see this
|
||||
message, please submit a bug report at:
|
||||
|
||||
https://github.com/scikit-learn/scikit-learn/issues
|
||||
|
||||
- The build will continue with OpenMP-based parallelism
|
||||
disabled. Note however that some estimators will run in
|
||||
sequential mode instead of leveraging thread-based
|
||||
parallelism.
|
||||
|
||||
***
|
||||
"""
|
||||
)
|
||||
warnings.warn(message)
|
||||
|
||||
return openmp_supported
|
||||
@ -0,0 +1,75 @@
|
||||
"""Helpers to check build environment before actual build of scikit-learn"""
|
||||
|
||||
import glob
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import textwrap
|
||||
|
||||
from setuptools.command.build_ext import customize_compiler, new_compiler
|
||||
|
||||
|
||||
def compile_test_program(code, extra_preargs=None, extra_postargs=None):
|
||||
"""Check that some C code can be compiled and run"""
|
||||
ccompiler = new_compiler()
|
||||
customize_compiler(ccompiler)
|
||||
|
||||
start_dir = os.path.abspath(".")
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
try:
|
||||
os.chdir(tmp_dir)
|
||||
|
||||
# Write test program
|
||||
with open("test_program.c", "w") as f:
|
||||
f.write(code)
|
||||
|
||||
os.mkdir("objects")
|
||||
|
||||
# Compile, test program
|
||||
ccompiler.compile(
|
||||
["test_program.c"], output_dir="objects", extra_postargs=extra_postargs
|
||||
)
|
||||
|
||||
# Link test program
|
||||
objects = glob.glob(os.path.join("objects", "*" + ccompiler.obj_extension))
|
||||
ccompiler.link_executable(
|
||||
objects,
|
||||
"test_program",
|
||||
extra_preargs=extra_preargs,
|
||||
extra_postargs=extra_postargs,
|
||||
)
|
||||
|
||||
if "PYTHON_CROSSENV" not in os.environ:
|
||||
# Run test program if not cross compiling
|
||||
# will raise a CalledProcessError if return code was non-zero
|
||||
output = subprocess.check_output("./test_program")
|
||||
output = output.decode(sys.stdout.encoding or "utf-8").splitlines()
|
||||
else:
|
||||
# Return an empty output if we are cross compiling
|
||||
# as we cannot run the test_program
|
||||
output = []
|
||||
except Exception:
|
||||
raise
|
||||
finally:
|
||||
os.chdir(start_dir)
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def basic_check_build():
|
||||
"""Check basic compilation and linking of C code"""
|
||||
if "PYODIDE" in os.environ:
|
||||
# The following check won't work in pyodide
|
||||
return
|
||||
|
||||
code = textwrap.dedent(
|
||||
"""\
|
||||
#include <stdio.h>
|
||||
int main(void) {
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
)
|
||||
compile_test_program(code)
|
||||
@ -0,0 +1,57 @@
|
||||
import argparse
|
||||
import os
|
||||
|
||||
from Cython import Tempita as tempita
|
||||
|
||||
# XXX: If this import ever fails (does it really?), vendor either
|
||||
# cython.tempita or numpy/npy_tempita.
|
||||
|
||||
|
||||
def process_tempita(fromfile, outfile=None):
|
||||
"""Process tempita templated file and write out the result.
|
||||
|
||||
The template file is expected to end in `.c.tp` or `.pyx.tp`:
|
||||
E.g. processing `template.c.in` generates `template.c`.
|
||||
|
||||
"""
|
||||
with open(fromfile, "r", encoding="utf-8") as f:
|
||||
template_content = f.read()
|
||||
|
||||
template = tempita.Template(template_content)
|
||||
content = template.substitute()
|
||||
|
||||
with open(outfile, "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("infile", type=str, help="Path to the input file")
|
||||
parser.add_argument("-o", "--outdir", type=str, help="Path to the output directory")
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--ignore",
|
||||
type=str,
|
||||
help=(
|
||||
"An ignored input - may be useful to add a "
|
||||
"dependency between custom targets"
|
||||
),
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.infile.endswith(".tp"):
|
||||
raise ValueError(f"Unexpected extension: {args.infile}")
|
||||
|
||||
if not args.outdir:
|
||||
raise ValueError("Missing `--outdir` argument to tempita.py")
|
||||
|
||||
outdir_abs = os.path.join(os.getcwd(), args.outdir)
|
||||
outfile = os.path.join(
|
||||
outdir_abs, os.path.splitext(os.path.split(args.infile)[1])[0]
|
||||
)
|
||||
|
||||
process_tempita(args.infile, outfile)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Extract version number from __init__.py"""
|
||||
|
||||
import os
|
||||
|
||||
sklearn_init = os.path.join(os.path.dirname(__file__), "../__init__.py")
|
||||
|
||||
data = open(sklearn_init).readlines()
|
||||
version_line = next(line for line in data if line.startswith("__version__"))
|
||||
|
||||
version = version_line.strip().split(" = ")[1].replace('"', "").replace("'", "")
|
||||
|
||||
print(version)
|
||||
Reference in New Issue
Block a user