Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build scripts for Python bindings are not correct [Windows] #1872

Closed
gmanlan opened this issue Apr 23, 2019 · 60 comments
Closed

Build scripts for Python bindings are not correct [Windows] #1872

gmanlan opened this issue Apr 23, 2019 · 60 comments

Comments

@gmanlan
Copy link
Contributor

gmanlan commented Apr 23, 2019

Issue description

Attempting to build python bindings on Windows using Visual Studio 2017 fails due to several issues:

  1. When using the flag BUILD_PYTHON_BINDINGS, CMake still shows a warning about not building python bindings, even though the bindings will be generated (not a roadblock).
  2. When the flag BUILD_PYTHON_BINDINGS is ON, the library will be built statically by default. I presume the python bindings require mlpack as a DLL? In that case, -DBUILD_SHARED_LIBS=ON must be enforced.
  3. Line 106 of setup.py refers to an invalid path. E.g.
    package_dir={ '': 'C:/mlpack/build/src/mlpack/bindings/python/' }
    This path ends in a slash which is not valid in a python package. What is more, I believe this path should be relative. If so, it should be replaced by: package_dir={ '': '.' },
  4. The linker expects to find mlpack and boost libraries in C:\mlpack\build\lib but this directory doesn't exist as a result of an mlpack build. Therefore, the directory needs to be manually created and populated with the following libraries: boost_serialization.lib, libboost_program_options-vc141-mt-1_65_1.lib, libboost_serialization-vc141-mt-1_65_1.lib, mlpack.dll, mlpack.lib
  5. After fixing issues 1 to 4, build will be successful. However, the resulting python package will fail to import mlpack with the following error:
    ImportError: cannot import name 'test_python_binding' from 'mlpack.test_python_binding' (C:\mlpack\build\src\mlpack\bindings\python\mlpack\test_python_binding.cp37-win_amd64.pyd)

Your environment

  • version of mlpack: master branch April 19 (3.0.5)
  • operating system: windows 10 64 bits
  • compiler: MSVC 14.1
  • version of dependencies (Boost/Armadillo): boost 1.65.1, armadillo-9.300.2, OpenBLAS.0.2.14.1
  • any other environment information you think is relevant: miniconda3 (python 3.7.1)

Steps to reproduce

  1. Clone master branch
  2. Run cmake including the flags:
    -DBUILD_PYTHON_BINDINGS=ON -DBUILD_SHARED_LIBS=ON
  3. Open solution with Visual Studio 2017 and build

Expected behavior

To successfully build python bindings AND the egg package to work (be able to import mlpack in python)

Actual behavior

Build failures (when workarounds are applied, the resulting package doesn't work)

@rcurtin
Copy link
Member

rcurtin commented Apr 27, 2019

Thanks for the clear bug report. I definitely want to fix this so I'll do my best to provide patches if you can test them (I don't have a Windows setup :)). For the first three issues, thanks for pointing them out; I opened #1885 which I think will fix those parts. Let me know if they work (I can't test them on Windows). Some questions:

When the flag BUILD_PYTHON_BINDINGS is ON, the library will be built statically by default. I presume the python bindings require mlpack as a DLL? In that case, -DBUILD_SHARED_LIBS=ON must be enforced.

Ah, hmm, I am not sure, but do you mean that Python bindings fail if we try to link mlpack statically? I can set BUILD_SHARED_LIBS to ON when BUILD_PYTHON_BINDINGS is ON, but I don't know if that's what users want.

After fixing issues 1 to 4, build will be successful. However, the resulting python package will fail to import mlpack with the following error:
ImportError: cannot import name 'test_python_binding' from 'mlpack.test_python_binding' (C:\mlpack\build\src\mlpack\bindings\python\mlpack\test_python_binding.cp37-win_amd64.pyd)

This is the tricky one that will require a little debugging... there are a couple of things to test:

  • Were DLLs or .libs actually built in ${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/mlpack/?
  • Is the file ${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/mlpack/test_python_binding.pyx empty?
  • Is PYTHONPATH set right? (My guess is yes based on that the error is talking about test_python_binding.)

I might need to learn more about how Python works on Windows. But if we can get this working you are a hero because it means this will probably be easy to package on PyPI for Windows, which is something I haven't known how to do.

@rcurtin
Copy link
Member

rcurtin commented Apr 27, 2019

Oops, accidentally clicked the wrong button...

@rcurtin rcurtin reopened this Apr 27, 2019
@gmanlan
Copy link
Contributor Author

gmanlan commented Apr 30, 2019

I pulled your branch and tried the changes. When attempting to build the build_pyx_xx projects I got a linker error for all of them --> fatal error LNK1181: cannot open input file 'mlpack.lib'

I checked the directory build\src\mlpack\bindings\python\mlpack and it contains:

Mode LastWriteTime Length Name


-a---- 4/29/2019 7:49 PM 97453 adaboost.cpp
-a---- 4/29/2019 7:34 PM 0 adaboost.pyx
-a---- 4/29/2019 7:51 PM 97503 approx_kfn.cpp
-a---- 4/29/2019 7:34 PM 0 approx_kfn.pyx
-a---- 4/29/2019 7:27 PM 2829 arma.pxd
-a---- 4/29/2019 7:27 PM 2657 arma_numpy.pxd
-a---- 4/29/2019 7:27 PM 9232 arma_numpy.pyx
-a---- 4/29/2019 7:27 PM 1704 arma_util.hpp
-a---- 4/29/2019 7:52 PM 97303 cf.cpp
-a---- 4/29/2019 7:35 PM 0 cf.pyx
-a---- 4/29/2019 7:27 PM 1657 cli.pxd
-a---- 4/29/2019 7:27 PM 4100 cli_util.hpp
-a---- 4/29/2019 7:52 PM 97403 dbscan.cpp
-a---- 4/29/2019 7:35 PM 0 dbscan.pyx
-a---- 4/29/2019 7:52 PM 97603 decision_stump.cpp
-a---- 4/29/2019 7:35 PM 0 decision_stump.pyx
-a---- 4/29/2019 7:53 PM 97578 decision_tree.cpp
-a---- 4/29/2019 7:35 PM 0 decision_tree.pyx
-a---- 4/29/2019 7:54 PM 97328 det.cpp
-a---- 4/29/2019 7:37 PM 0 det.pyx
-a---- 4/29/2019 7:53 PM 97353 emst.cpp
-a---- 4/29/2019 7:28 PM 0 emst.pyx
-a---- 4/29/2019 7:54 PM 97428 fastmks.cpp
-a---- 4/29/2019 7:28 PM 0 fastmks.pyx
(...etc etc for the rest of the algorithms)

If you notice the dump, you will notice the .pyx are empty (0 bytes).

@rcurtin
Copy link
Member

rcurtin commented Apr 30, 2019

Hmmm, ok. What happens if you run one of the generate_pyx_* programs that got built? Or did those also fail to build?

@gmanlan
Copy link
Contributor Author

gmanlan commented Apr 30, 2019

The generate_pyx_* were successfully built and if I run it it prints out the python code.

I believe we need to fix a path to the libraries somewhere in the build scripts (that's I believe what I was doing in point (4)). I figured out that the linker is trying to fetch the libs from /LIBPATH:C:/mlpack/mlpack-python-pr-1885/build but that's not the right location (it should be /build/Release or Debug).

Also it's failing to find boost_serialization.lib, which is not a valid library name for Boost in Windows (at least for the official MSVC package). It should be something like boost_serialization-vc141-mt-1_65_1.lib (note: the -vc*- and -1__ chunks will change from version to version). If I rename this .lib to boost_serialization.lib, then build_pyx_* can successfully build and link.

@gmanlan
Copy link
Contributor Author

gmanlan commented May 1, 2019

Going a step beyond, this is what happens when you go ahead and install the generated mlpack python package:

  1. Navigate to \build\src\mlpack\bindings\python
  2. From there run python setup.py install
  3. Once the package is installed, try importing mlpack in python:

>>> import mlpack Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\mlpack\mlpack-python-pr-1885\build\src\mlpack\bindings\python\mlpack\__init__.py", line 11, in <module> from .test_python_binding import test_python_binding ModuleNotFoundError: No module named 'mlpack.test_python_binding'

@rcurtin
Copy link
Member

rcurtin commented May 13, 2019

Thanks for the information, and sorry for the slow response---this fell down on the list a little bit.

I think I know what is happening---the generate_pyx_* programs build successfully, but each .pyx is empty. The fact that those are empty is almost certainly why the import fails (nothing actually gets compiled). I looked more closely and realized I am assuming that generate_pyx_* is in ${CMAKE_BINARY_DIR}/bin/, which is not true on Windows. I rewrote it to use CMake generator expressions, which weren't something I knew about, and now it should get the actual path of those programs right.

I also had to use CMake generator expressions for getting the library paths right for mlpack (and some modification was needed to get the Boost serialization library path correctly). It seems to work for Linux. Let me know what happens if you test it...

@gmanlan
Copy link
Contributor Author

gmanlan commented May 13, 2019

That's right. Last week I said I was going to update here with my findings but you beat me to it. The ${CMAKE_BINARY_DIR}/bin/ issue was one and the other is a missing openblas reference somewhere, but let me test the new version and report back, thanks!

@gmanlan
Copy link
Contributor Author

gmanlan commented May 13, 2019

So I tried the new changes but these errors are blocking the way:

Configuring setup.py...
3>CUSTOMBUILD : CMake error : Error processing file: C:/mlpack/mlpack-python-windows-fix/src/mlpack/bindings/python/ConfigureSetup.cmake

And then...

Building Cython binding knn.so...
5>C:\miniconda3\python.exe: can't open file 'C:/mlpack/mlpack-python-windows-fix/build/src/mlpack/bindings/python/setup.py': [Errno 2] No such file or directory

I noticed that setup.py is not being generated/copied into any directory.

@rcurtin
Copy link
Member

rcurtin commented May 13, 2019

Ah, sorry, forgot to check in a file. ed6e3f5 should fix it---sorry about that.

@gmanlan
Copy link
Contributor Author

gmanlan commented May 14, 2019

Ok we are getting close, but still facing some issues. Now it seems the linker is unable to find the boost libs. Also the CUSTOMBUILD warnings (or errors?) are unexpected as this is a static build.

LINK : fatal error LNK1104: cannot open file 'libboost_serialization-vc141-mt-1_65_1.lib'
92>C:\miniconda3\lib\site-packages\Cython\Compiler\Main.py:367: FutureWarning: Cython directive 'language_level' not set, using 2 for now (Py2). This will change in a later release! File: C:\mlpack\mlpack-python-windows-fix\build\src\mlpack\bindings\python\mlpack\kde.pyx
92>  tree = Parsing.p_module(s, pxd, full_module_name)
92>CUSTOMBUILD : warning : no library file corresponding to 'C:/mlpack/mlpack-python-windows-fix/build/Release/mlpack.lib' found (skipping)
92>CUSTOMBUILD : warning : no library file corresponding to 'optimized C:/boost_1_65_1/lib64-msvc-14.1/boost_serialization-vc141-mt-1_65_1.lib debug C:/boost_1_65_1/lib64-msvc-14.1/boost_serialization-vc141-mt-gd-1_65_1.lib' found (skipping)
92>CUSTOMBUILD : error : command 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\bin\\HostX86\\x64\\link.exe' failed with exit status 1104
92>Done building project "build_pyx_kde.vcxproj" -- FAILED.

@rcurtin
Copy link
Member

rcurtin commented May 15, 2019

Oh, my, in digging into this I kind of stumbled upon some ugliness that I might have to work around in setuptools. Do you have the compilation command that setuptools calls for kde.pyx? Specifically I want to see how it is linking against libraries.

@gmanlan
Copy link
Contributor Author

gmanlan commented May 15, 2019

Yes, this is the link command, which I believe may be missing some of the lib paths specified at the beginning with cmake:

C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.16.27023\bin\HostX86\x64\link.exe /nologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EMBED,ID=2 /MANIFESTUAC:NO /LIBPATH:C:\miniconda3\libs /LIBPATH:C:\miniconda3\PCbuild\amd64 "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.16.27023\ATLMFC\lib\x64" "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.16.27023\lib\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\lib\um\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.17763.0\ucrt\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.17763.0\um\x64" "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.16.27023\lib\x64" "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.16.27023\atlmfc\lib\x64" "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Auxiliary\VS\lib\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.17134.0\ucrt\x64" "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Auxiliary\VS\UnitTest\lib" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.17134.0\um\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\lib\um\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\Lib\um\x64" /EXPORT:PyInit_kde build\temp.win-amd64-3.7\Release\mlpack\kde.obj /OUT:build\lib.win-amd64-3.7\mlpack\kde.cp37-win_amd64.pyd /IMPLIB:build\temp.win-amd64-3.7\Release\mlpack\kde.cp37-win_amd64.lib -openmp

@rcurtin
Copy link
Member

rcurtin commented May 16, 2019

Thanks for the output---it was helpful. I tried pushing another update to #1885; can you try that? I know how to make CMake link against the right stuff, but figuring out how to pass that to setuptools in a platform-independent way I'm less sure about...

@gmanlan
Copy link
Contributor Author

gmanlan commented May 17, 2019

Ok, just tried - now I can see some of the libs properly specified, although there is an empty LIBPATH statement (the second one in the list) which makes the LINK fail:

LINK : fatal error LNK1146: no argument specified with option '/LIBPATH:'

C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.16.27023\bin\HostX86\x64\link.exe /nologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EMBED,ID=2 /MANIFESTUAC:NO /LIBPATH:C:/mlpack/mlpack-python-windows-fix/build/Release /LIBPATH: /LIBPATH:C:\miniconda3\libs /LIBPATH:C:\miniconda3\PCbuild\amd64 "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.16.27023\ATLMFC\lib\x64" "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.16.27023\lib\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\lib\um\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.17763.0\ucrt\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.17763.0\um\x64" "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.16.27023\lib\x64" "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.16.27023\atlmfc\lib\x64" "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Auxiliary\VS\lib\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.17134.0\ucrt\x64" "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Auxiliary\VS\UnitTest\lib" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.17134.0\um\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\lib\um\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\Lib\um\x64" mlpack.lib boost_serialization-vc141-mt-gd-1_65_1.lib /EXPORT:PyInit_kde build\temp.win-amd64-3.7\Release\mlpack\kde.obj /OUT:build\lib.win-amd64-3.7\mlpack\kde.cp37-win_amd64.pyd /IMPLIB:build\temp.win-amd64-3.7\Release\mlpack\kde.cp37-win_amd64.lib -openmp

@rcurtin
Copy link
Member

rcurtin commented May 17, 2019

Okay... I pushed another commit (4906722) which should strip the empty library directory. I think it will fix the issue... or at least get us to a new error. :)

@rcurtin
Copy link
Member

rcurtin commented May 17, 2019

Ack, hang on, spoke too soon---one more thing to fix. Just a second...

@rcurtin
Copy link
Member

rcurtin commented May 17, 2019

Ok, better now. I realized the Boost link directories were not being properly passed. This should be fixed in a44fdfe. So I think it should link right... with any luck. :)

@gmanlan
Copy link
Contributor Author

gmanlan commented May 17, 2019

Uhm, it seems the library_dirs type is not a list of strings:

Traceback (most recent call last):

File "C:/mlpack/mlpack-python-windows-fix/build/src/mlpack/bindings/python/setup.py", line 121, in
"'library_dirs' (if supplied) must be a list of strings")
TypeError: 'library_dirs' (if supplied) must be a list of strings
C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\VC\VCTargets\Microsoft.CppCommon.targets(209,5): error MSB6006: "cmd.exe" exited with code 1.
Done building project "build_pyx_kde.vcxproj" -- FAILED.

I have tried manually removing the filter() and it seems to work (although my setup.py is being re-generated with every build so I can't be sure it will work for all the pyx).

@rcurtin
Copy link
Member

rcurtin commented Jun 1, 2019

@gmanlan sorry for the slow response on this one---a bunch of traveling for me in the past two weeks. Anyway I think I pushed a fix; when the library dirs are empty, we have to pass None not []. So, I'm interested to know what happens now. :)

@gmanlan
Copy link
Contributor Author

gmanlan commented Jun 6, 2019

@rcurtin, just tried again but it seems the error remains:

File "C:/mlpack/mlpack-python-windows-fix/build/src/mlpack/bindings/python/setup.py", line 125 TypeError: 'library_dirs' (if supplied) must be a list of strings

May it probably be the filter()?

library_dirs = filter(None, ['C:/mlpack/mlpack-python-windows-fix/build/Release'] +
    'C:/boost_1_65_1/lib64-msvc-14.1'.split(' '))
if library_dirs == []:
  library_dirs = None

@rcurtin
Copy link
Member

rcurtin commented Jun 7, 2019

Color me confused---when I run

library_dirs = filter(None, ['C:/mlpack/mlpack-python-windows-fix/build/Release'] +
    'C:/boost_1_65_1/lib64-msvc-14.1'.split(' '))

then it seems that what is returned is a list of strings, just like setuptools needs.

Anyway, let's try it again just without that filtering code, since on your system we're only passing in a couple directories and I can see there are no empty directories there. I removed the filter in 87e174b0c.

@gmanlan
Copy link
Contributor Author

gmanlan commented Jun 7, 2019

Ok, now we have LINK errors, which I believe are caused because we are missing LAPACK somewhere?

mlpack.lib(adaboost_model.obj) : error LNK2001: unresolved external symbol ddot_
mlpack.lib(adaboost_model.obj) : error LNK2001: unresolved external symbol dgemv_
mlpack.lib(adaboost_model.obj) : error LNK2001: unresolved external symbol dgemm_
mlpack.lib(adaboost_model.obj) : error LNK2001: unresolved external symbol dsyrk_
mlpack.lib(adaboost_model.obj) : error LNK2001: unresolved external symbol dgesv_
mlpack.lib(adaboost_model.obj) : error LNK2001: unresolved external symbol dposv_
build\lib.win-amd64-3.7\mlpack\adaboost.cp37-win_amd64.pyd : fatal error LNK1120: 6 unresolved externals

And also some encoding issues (I think there is an unexpected character in this file):

mlpack\kernel_pca.pyx:79:24: Decoding error, missing or incorrect coding=<encoding-name> at top of source (cannot decode with encoding 'utf-8': invalid start byte)

@gmanlan
Copy link
Contributor Author

gmanlan commented Jul 19, 2019

Great! Thanks for the update @rcurtin. I think it would depend on how you include Boost into the project, as there are different ways of doing so with VS. Looking forward to the next version!

@rcurtin
Copy link
Member

rcurtin commented Aug 6, 2019

I have an update---I've been working on this in the background for a couple weeks now. Progress is a little slow because it seems Visual Studio builds singlethreadedly which is pretty slow.

I've gotten the Python bindings to build successfully and import, so import mlpack does successfully work. This required some amount of wrangling having to do with static and dynamic linking that I still have yet to work out. I noticed that to get the build working right, I had to drop all the DLLs that mlpack links against in the directories for the built programs like generate_pyx_cf, etc., and also in build/src/mlpack/bindings/python/mlpack/. I think this can be resolved if static linking is always used, but I haven't figured out how to get CMake to do that all the way through yet.

In any case, I do have something seeming to work, but the only problem is, it crashes Python. I haven't yet determined the cause of the crash because I haven't figured out how to get good debugging output, but I am continuing to dig. It looks like some memory is getting freed twice for a reason I don't yet understand.

Anyway, I'll keep working at it, but I wanted to provide an update. :)

@rcurtin
Copy link
Member

rcurtin commented Aug 15, 2019

Another update---

I've been continuing to dig into what's going on here and it seems like the crash in Python has to do with the way memory is being passed back and forth between C++ and Python. My theory (which I have no validation for and am not sure how to validate) is that on Linux and OS X, the memory allocators used in C++ and Python are the same, meaning I can allocate memory in Python and free it in C++ and vice versa, but this seems to not be the case on Windows.

What I am going to try as time allows is to rewrite the memory handling such that C++ memory is never "owned" by Python and vice versa, but this is likely to impact the efficiency of the bindings. That said, "bindings working but somewhat slower because of copies" is still better than "bindings not working at all". :)

@gmanlan
Copy link
Contributor Author

gmanlan commented Aug 15, 2019

Hey, thanks for the update. That sounds like a big one, I'm wondering how this was working before on Windows. Looking forward to the next iteration!

@rcurtin
Copy link
Member

rcurtin commented Aug 23, 2019

Ok, pretty happy to provide this update. I seem to have the Python bindings successfully working from Windows, when I don't allow any passing of memory pointers back and forth from Python to C++. This means there are more copies than we might hope for, but "a bit slow" is better than "doesn't work".

Next I need to do some serious cleanups and fix some tests that don't work the way I hoped on Windows, but I believe I have the hard part of this (mostly?) figured out, assuming that I don't soon find that something was far more complicated than I thought. Once I get everything cleaned up, I'll open a PR and we can see if it works for both of us. :)

@jeffin143
Copy link
Member

jeffin143 commented Aug 23, 2019

Ok, pretty happy to provide this update. I seem to have the Python bindings successfully working from Windows, when I don't allow any passing of memory pointers back and forth from Python to C++. This means there are more copies than we might hope for, but "a bit slow" is better than "doesn't work".

Next I need to do some serious cleanups and fix some tests that don't work the way I hoped on Windows, but I believe I have the hard part of this (mostly?) figured out, assuming that I don't soon find that something was far more complicated than I thought. Once I get everything cleaned up, I'll open a PR and we can see if it works for both of us. :)

Can't we do something like, let binding behave as it is in Linux and os x , but it should behave differently when it is windows , since i would not like it to become slow :)

@rcurtin
Copy link
Member

rcurtin commented Aug 23, 2019

Agreed, I was planning on different behavior for each system.

@gmanlan
Copy link
Contributor Author

gmanlan commented Aug 23, 2019

That's awesome @rcurtin. I look forward to testing the PR!

@rcurtin
Copy link
Member

rcurtin commented Aug 29, 2019

I can't believe I am posting this. I thought it would never happen.

win_python

It'll take me a little while to figure out all the changes I made and clean it up for a PR, but, this is a nice big step forward. (The next question is whether or not it will work on anyone else's system!)

@rcurtin
Copy link
Member

rcurtin commented Aug 31, 2019

@gmanlan I pushed some changes to #1885---do you want to try checking them out and seeing if the Python bindings now build on your system? There may still be some little things I overlooked, but at the very least, this code works on my system, so it shouldn't be too hard to figure out how to make it run on yours if it doesn't already. :)

@gmanlan
Copy link
Contributor Author

gmanlan commented Sep 9, 2019

@rcurtin I have pulled the last changes and attempted to use it. On the positive side, I can build perfectly fine without issues. Unfortunately after installing the python package on Windows, I'm back to the initial errors (from .cf import cf ImportError: cannot import name 'cf' from 'mlpack.cf').

These are the steps I performed:

  1. Run cmake, build python.vcxproj in Visual Studio (Release/x64)
    ========== Build: 96 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
  2. Navigate to C:\mlpack\mlpack-python-windows-fix\build\src\mlpack\bindings\python and run python setup.py install
  3. Add Boost and OpenBLAS DLL dirs to the PATH
  4. Start python and run "import mlpack"
>>> import mlpack
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\mlpack\mlpack-python-windows-fix\build\src\mlpack\bindings\python\mlpack\__init__.py", line 14, in <module>
    from .cf import cf
ImportError: cannot import name 'cf' from 'mlpack.cf' (C:\mlpack\mlpack-python-windows-fix\build\src\mlpack\bindings\python\mlpack\cf.cp37-win_amd64.pyd)

Let me know if you did something different when installing/using the package. Thanks!

@rcurtin
Copy link
Member

rcurtin commented Sep 9, 2019

Hmmm, ok. Can you tell me if the file build/src/mlpack/bindings/python/cf.pyx has anything in it?

@gmanlan
Copy link
Contributor Author

gmanlan commented Sep 9, 2019

Good point - cf.pyx is empty (0 bytes), as well as lmnn.pyx. All the others are ok.

@rcurtin
Copy link
Member

rcurtin commented Sep 9, 2019

Hmm, interesting. I seem to remember encountering this in my own experimentation but I thought I had it fixed in the PR. In any case, can you tell me what happens if you run the program generate_pyx_cf? I think, in your setup, that should be in C:\mlpack\mlpack-python-windows-fix\build\Debug\ (or Release\). My guess is that this will have some problem. (The same for generate_pyx_lmnn too.)

@rcurtin
Copy link
Member

rcurtin commented Sep 9, 2019

By the way, when I ran these programs locally in my build, I had to make sure all the necessary DLLs were in place, and I ran it from either a command prompt or PowerShell so that I could see the output (instead of the window just closing).

@gmanlan
Copy link
Contributor Author

gmanlan commented Sep 9, 2019

I think both are working fine (I can see the python code being generated, without errors). You can see the output of generate_pyx_cf.exe in the attached file.

C:\mlpack\mlpack-python-windows-fix\build\Release>generate_pyx_cf.exe > output.txt
output.txt

@rcurtin
Copy link
Member

rcurtin commented Sep 9, 2019

Well now that is strange. generate_pyx_cf.exe worked fine, but the generated file was still empty. Let me try to reproduce this on a completely clean build of the Python fixes branch tonight.

You might be able to get everything to build by "rebuild"ing the build_pyx_cf target, then the python target. If cf.pyx isn't empty in build/src/mlpack/bindings/python/mlpack/, then everything should work, I think.

@gmanlan
Copy link
Contributor Author

gmanlan commented Sep 10, 2019

I've been debugging but I see nothing wrong... still, the pyx is empty. Do you know from where the .exe is being called? I can't find it in the VS logs.

@rcurtin
Copy link
Member

rcurtin commented Sep 15, 2019

I managed to reproduce it and nail down what the issue is. It so happens that cf and lmnn are the only two bindings that require a direct linkage against libopenblas.dll.a (others call functions that are part of mlpack.dll which is in turns linked against OpenBLAS). This means that when libopenblas.dll.a is not in the runtime path when generate_pyx_cf is called, the program fails to start, and my CMake scripts are not picking up this failure (not 100% sure why yet).

A workaround is to drop libopenblas.dll.a in the directory containing generate_pyx_cf and generate_pyx_lmnn and rebuild the python target.

I'll be working on figuring out how to get a clean solution for this and let you know what I figure out.

@gmanlan
Copy link
Contributor Author

gmanlan commented Sep 16, 2019

Ah, interesting. I'm wondering why the command C:\mlpack\mlpack-python-windows-fix\build\Release>generate_pyx_cf.exe > output.txt would be able to run then, considering the PATH is the same for both the command prompt and VS. When I run the exe from the cmd prompt, it only requires the dll, but again, VS is sometimes very mysterious.

@rcurtin
Copy link
Member

rcurtin commented Sep 25, 2019

That's strange, but I agree, Visual Studio can be pretty mysterious. Same with CMake. I spent a while digging into it and I concluded that the safest way I can fix the issue is simply to copy all the necessary DLLs to the right place. So, the latest commit that I pushed adds the CMake variables DLL_COPY_LIBS and DLL_COPY_DIRS that can be specified, containing any additional directories of things that should be copied.

In the case of the Boost libraries and specifically OpenBLAS installed via nuget (where the runtime libraries are in the OpenBLAS directory's bin/x64/ directory), these are automatically copied at configure time, so there is no need to specify them.

I'll push a commit tomorrow with updated documentation, but, the commit I just pushed to #1885 at least I think should make the Python bindings compile and run successfully for you also. Sorry again for the slow response---the compile/test cycles are quite slow because I'm running in an underpowered VM, so it's basically one or two compile attempts per night. :)

@gmanlan
Copy link
Contributor Author

gmanlan commented Sep 25, 2019

Hey, good news. I have tried the latest #1885 and the windows python bindings are building and running. To check if it's properly working, I adapted the example from https://www.mlpack.org/doc/mlpack-3.1.1/doxygen/python_quickstart.html and it seems to work just fine. Well done! We may want to properly document how to build this, and even better in the future, auto-generate a windows package to be installed using pip.
My extremely manual build steps are:

  1. Run cmake
  2. Open mlpack.sln, build the project 'python' in Release x64
  3. Navigate to \build\src\mlpack\bindings\python and run 'python setup.py install'
  4. Add Boost and OpenBLAS DLL dirs to the PATH
  5. Run python and import mlpack

@rcurtin
Copy link
Member

rcurtin commented Sep 27, 2019

Definitely, the https://github.com/mlpack/mlpack-wheels repository is ready to try and attempt some Windows builds when everything is merged. I'm not actually sure what happens with the Python bindings on Windows when I make the "INSTALL" target. I'm running now to find out... :)

@rcurtin
Copy link
Member

rcurtin commented Oct 10, 2019

Ok, I fought with it for a while and realized I was setting CMAKE_INSTALL_PREFIX incorrectly in my configuration. I did get mlpack to install but there was some issue with the Python bindings. However I pushed some minor changes in 3bbec97 that add some extra paranoia to CMake. Personally, I think the PR is ready, and once we merge it it will be a big step forward. I'll comment more in #1885.

@gmanlan
Copy link
Contributor Author

gmanlan commented Oct 10, 2019

This is awesome @rcurtin! I left a minor comment after reviewing. Very excited to get this integrated soon!

@rcurtin
Copy link
Member

rcurtin commented Nov 3, 2019

With #1885 merged, I think that we can close this. :) Next I will move towards getting mlpack Python bindings packaged for PyPI on Windows.

@rcurtin rcurtin closed this as completed Nov 3, 2019
@rcurtin rcurtin added this to the mlpack 3.2.2 milestone Nov 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants