|
| 1 | +"""Embed the vcomp dll before generating the scikit-learn Windows wheel. |
| 2 | +
|
| 3 | +This script should be run from the root of the scikit-learn source tree, |
| 4 | +after running the `python setup.py build` command and before running |
| 5 | +the `python setup.py bdist_wheel` command. |
| 6 | +""" |
| 7 | + |
| 8 | +import os |
| 9 | +import os.path as op |
| 10 | +import shutil |
| 11 | +from glob import glob |
| 12 | +import textwrap |
| 13 | + |
| 14 | +VCOMP140_SRC_PATH = "C:\\Windows\System32\\vcomp140.dll" |
| 15 | +TARGET_FOLDER_GLOB_PATTERN = "build/lib.*/sklearn" |
| 16 | + |
| 17 | + |
| 18 | +def make_distributor_init(sklearn_dirname, dll_filename): |
| 19 | + """Create a _distributor_init.py file for the vcomp dll. |
| 20 | +
|
| 21 | + This file is imported first when importing the sklearn package so as |
| 22 | + to pre-load the vendored vcomp dll. |
| 23 | + """ |
| 24 | + distributor_init = op.join(sklearn_dirname, '_distributor_init.py') |
| 25 | + with open(distributor_init, 'wt') as f: |
| 26 | + f.write(textwrap.dedent(""" |
| 27 | + ''' |
| 28 | + Helper to preload the OpenMP dll to prevent "dll not found" |
| 29 | + errors. |
| 30 | + Once a DLL is preloaded, its namespace is made available to any |
| 31 | + subsequent DLL. This file originated in the scikit-learn-wheels |
| 32 | + github repo, and is created as part of the scripts that build the |
| 33 | + wheel. |
| 34 | + ''' |
| 35 | + import os |
| 36 | + import os.path as op |
| 37 | + from ctypes import WinDLL |
| 38 | +
|
| 39 | +
|
| 40 | + if os.name == 'nt': |
| 41 | + # Pre-load the DLL stored in sklearn/.libs by convention. |
| 42 | + dll_path = op.join(op.dirname(__file__), '.libs', '{}') |
| 43 | + WinDLL(op.abspath(dll_path)) |
| 44 | +
|
| 45 | + """.format(dll_filename))) |
| 46 | + return op.abspath(distributor_init) |
| 47 | + |
| 48 | + |
| 49 | +def main(): |
| 50 | + # TODO: use threadpoolctl to dynamically locate the right vcomp dll |
| 51 | + # instead? This would require first in-place building scikit-learn |
| 52 | + # to make it "importable". |
| 53 | + if not op.exists(VCOMP140_SRC_PATH): |
| 54 | + raise ValueError("Could not find %r" % VCOMP140_SRC_PATH) |
| 55 | + |
| 56 | + if not op.isdir("build"): |
| 57 | + raise RuntimeError("Could not find ./build/ folder. " |
| 58 | + "Run 'python setup.py build' first") |
| 59 | + target_folders = glob(TARGET_FOLDER_GLOB_PATTERN) |
| 60 | + if len(target_folders) == 0: |
| 61 | + raise RuntimeError("Could not find folder matching '%s'" |
| 62 | + % TARGET_FOLDER_GLOB_PATTERN) |
| 63 | + if len(target_folders) > 1: |
| 64 | + raise RuntimeError("Found too many target folders: '%s'" |
| 65 | + % "', '".join(target_folders)) |
| 66 | + target_folder = op.abspath(op.join(target_folders[0], ".libs")) |
| 67 | + |
| 68 | + # create the "sklearn/.libs" subfolder |
| 69 | + if not op.exists(target_folder): |
| 70 | + os.mkdir(target_folder) |
| 71 | + |
| 72 | + print("Copying '%s' to:\n%s" % (VCOMP140_SRC_PATH, target_folder)) |
| 73 | + shutil.copy2(VCOMP140_SRC_PATH, target_folder) |
| 74 | + |
| 75 | + # Generate the _distributor_init file in the source tree. |
| 76 | + print("Generating the '_distributor_init.py' file in:") |
| 77 | + print(make_distributor_init("sklearn", op.basename(VCOMP140_SRC_PATH))) |
| 78 | + |
| 79 | + |
| 80 | +if __name__ == "__main__": |
| 81 | + main() |
0 commit comments