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
ENH: distutils: make msvc + mingw-gfortran work #9431
Conversation
First of a few cross-references to comments in the previous PR: """ @xoviat - I guess you mean, for each scipy extension module, telling disutils which Fortran DLLs they depend on? |
Comments on mechanism for specifying library order:
Approach here is to list all the scipy Fortran libraries inside numpy distutils. It would be better to specify them inside Scipy, perhaps (@pv) in setup.py. But we need to take care of ordering of libraries. Do I understand right, that this will need changes to numpy distutils? |
Correct. It's not just the DLLs that we make, but the DLLs in the MinGW folder too. |
Hmm. For the DLLs in the mingw folder, how about using mingwpy or one of the newer static mingw variants that @carlkl has been building? I'm worried about putting the mingw DLLs on the path, with their current names, I guess we could easily run into a DLL clash with other code linking against these DLLs. Similarly, I wonder if there's a way to keeping the new Fortran DLL names unique, maybe with a hash of their object file contents? Just in case something else is using these DLL names? |
There are no issues with the fortran DLLs that we make clashing. In addition, the PATH is only modified for scipy, so there should not be clashes (for the mingw DLLs) if scipy doesn't spawn subprocesses. |
Also I don't think gfortran is called with -O3. We should at least try to run as fast as possible. |
I am happy to be corrected, but I believe that we'll run into trouble if any Python module imports a DLL with the same name, either before Scipy imports (Scipy will get the wrong DLL) or after it imports (the other module will get the wrong DLL). It might not be happening now, but it's difficult to predict if it will happen in the future. |
Yes, if it the same DLL. However, I do believe that the MinGW DLLs have versioned names. |
SciPy itself spawns only subprocesses in the test suite AFAIK. |
Agreed that it should be in |
As I understand, it should not be necessary to have the big global
ordered list. Rather, the order could be specified for each lib separately.
|
DLLs again - just to be clear, my understanding is that DLL names are global to the process. If package P imports DLL |
import all DLLs can be imported into a process only once, selected by the filename of the DLL. Windows DLL search order is: first Windows system libraries, then executable (https://github.com/numpy/numpy/wiki/windows-dll-notes). Please forget to try the change the DLL search path with the PATH environment variable. This won't work in all cases. I can't recommend the approach of scipy/scipy#7613 at all. |
@carlkl - just to clarify again (I know you meant this) but, for Windows, once you've imported |
@carlkl - can you say more about when adding the library directory to the PATH will not work? And why you can't recommend it? Did you have a chance to read https://github.com/pypa/wheel-builders/pull/2/files#diff-37a1ef08dd1b0096d769190274121258R378 ? |
If DLL name collisions are a concern, then Mach-O Mach-O Mangler can help you give them unique names. Basically it's the same trick that I'd generally recommend giving all DLLs unique names, and then using either the |
Nathaniel - what is the LoadLibrary trick? |
Adding the folder to PATH may not work:
|
@carlkl - could you be more specific about the problems you see? I guess, if there were somehow two different scipies being imported in the same process this could be a problem - is that what you mean? In that case - what approach are you suggesting? |
@matthew-brett: if you call @carlkl: the search order doesn't matter if your dll is named like |
using LoadLibrary with an absolute PATH means you have to ensure to get the PATH right. I.e.: you also have to consider frozen executables as well. |
@carllk - sorry to be dumb, but could you unpack for me, what approach you are suggesting, instead of setting the PATH (or loading the library by full path with LoadLibrary)? |
@carlkl: frozen executables are a pretty unusual case, and handling them likely requires some sort of ad hoc integration with whatever hacks they're using. I don't think we should worry about them until after the basic functionality is working. |
please test the scipy wheels (scipy/scipy#7597 (comment)). No PATH manipulation is needed, as all scipy |
@carlkl: I guess testing the wheels is less interesting (I can believe
you if you say they work) than how they were produced. Did you have the
recipe producing them somewhere?
|
The reason for Scipy is unpatched and build by (still unpublished) gcc-5.3 version. In short words: the recipe is not ready due to the fact, that I still get strange build errors for 32bit builds as well as cp34 and cp36 for 64 bit. |
I will squash the commits. |
/cc @rgommers |
numpy/distutils/fcompiler/gnu.py
Outdated
# the following code is not needed (read: breaks) when using MinGW | ||
# in case want to link F77 compiled code with MSVC | ||
opt.append('gcc') | ||
runtime_lib = msvc_runtime_library() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: this change is in the class for g77
, so is this a cleanup that looks right or have you been using g77
?
Comments:
- the code comment above about MinGW looks like it applied to these lines and should be removed.
- the import of
msvc_runtime_library
is now unused and should be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These lines actually had a signfiicant impact becuase the class is inherited. It's not just a "cleanup"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah right, clear.
numpy/distutils/fcompiler/gnu.py
Outdated
'linker_so': ["<F90>", "-Wall", "-g"], | ||
'archiver': ["ar", "-cr"], | ||
'ranlib': ["ranlib"], | ||
'linker_exe': [None, "-Wall"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this formatting change is for the worse, the old version was easier to read - can you undo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will revert this section.
I had a quick read through. Did not see anything critical that should prevent merging or break other configs. Traveling at the moment, so haven't been able to follow all the discussions on Windows builds in detail. |
FYI.
This is actually not correct. Supporting this (which I'm not going to get into this point) would require one additional call to lib.exe in build_ext. It is something that could be implemented with moderate additional effort. See here for more details |
Needs a release note in |
Release notes added. There was a merge conflict, so I rebased and re-pushed this. |
This allows mingw's gfortran to work with MSVC. DLLs are autogenerated using heuristics that should work with most cases. In addition, a libopenblas DLL is compiled from the static lib for use with MSVC. All generated DLLs have randomized names so that no clashes will occur.
…t gnu Add concept of unlinkable Fortran object files on the level of build_clib/build_ext. Make build_clib generate fake static libs when unlinkable object files are present, postponing the actual linkage to build_ext. This enables MSVC+gfortran DLL chaining to only involve those DLLs that are actually necessary for each .pyd file, rather than linking everything in to every file. Linking everything to everywhere has issues due to potential symbol clashes and the fact that library build order is unspecified. Record shared_libs on disk instead of in system_info. This is necessary for partial builds -- it is not guaranteed the compiler is actually called for all of the DLL files. Remove magic from openblas msvc detection. That this worked previously relied on the side effect that the generated openblas DLL would be added to shared_libs, and then being linked to all generated outputs.
Thanks so much for taking care of the merge conflicts. |
Lets get this in. If nothing else it is a good starting point for further fixes if needed. Thanks @xoviat and the reviewers. |
Enable numpy.distutils to link mingw-w64-gfortran compiled files to MSVC objects,
by putting everything gfortran to separate DLL files to avoid CRT issues.
On installation, the extra DLL files are stuffed to an
extra-dll
subdirectoryof the package, which is added to PATH so that Windows can load the files.
The filenames of the libraries contain a sha1 hash of the input object files,
so DLL hell should be avoided (barring sha1 hash collisions).
This is a bit of a hack, but it is limited to the "msvc/gnu95" special case.
Moreover, in addition to mingwpy (which may still need further work?),
this is the currently the only working easily available compiler combination
to build Python.org compatible binaries for Windows.
To handle static libraries, we need to add a concept of a "fake" static library
to numpy.distutils. This is just a collection of object files whose filenames are
listed in
*.cobjects
and*.fobjects
files. This is necessary, because MSVCcannot build linkable static libraries out of gfortran objects, so the linking
cannot be done in the
build_clib
stage but has to be postponed tobuild_ext
.(And in distutils, the only good way to share information between stages
is to store it on disk).
Limitations: fortran code that needs symbols from C code provided by the
project cannot be compiled like --- this would require using
two C compilersimport libraries in also(gcc + msvc) and this is getting too complicated
to the other direction, so it's not supported currently (will fail with undefined
symbol errors at link time). However, the typical case of wrapping Fortran
codes to Python doesn't need this.
Moreover, if linking against BLAS/LAPACK, that must be provided in
a gfortran-compatible import library. The same machinery then will
automatically build a wrapper DLL for it for MSVC if needed.
Scipy Appveyor builds & wheels enabled by this:
scipy/scipy#7616
https://ci.appveyor.com/project/scipy/scipy