This is an archive of the discontinued LLVM Phabricator instance.

[mlir] Fix python bindings build on Windows in Debug
ClosedPublic

Authored by stella.stamenova on May 9 2022, 6:07 PM.

Details

Summary

Currently, building mlir with the python bindings enabled on Windows in Debug is broken because pybind11, python and cmake don't like to play together. This change normalizes how the three interact, so that the builds can now run and succeed.

The main issue is that python and cmake both make assumptions about which libraries are needed in a Windows build based on the flavor.

  • cmake assumes that a debug (or a debug-like) flavor of the build will always require pythonX_d.lib and provides no option/hint to tell it to use a different library. cmake does find both the debug and release versions, but then uses the debug library.
  • python (specifically pyconfig.h and by extension python.h) hardcodes the dependency on pythonX_d.lib or pythonX.lib depending on whether _DEBUG is defined. This is NOT transparent - it does not show up anywhere in the build logs until the link step fails with pythonX_d.lib is missing (or pythonX.lib is missing)
  • pybind11 tries to "fix" this by implementing a workaround - unless Py_DEBUG is defined, _DEBUG is explicitly undefined right before including python headers. This also requires some windows headers to be included differently, so while clever, this is a non-trivial workaround.

mlir itself includes the pybind11 headers (which contain the workaround) AS WELL AS python.h, essentially always requiring both pythonX.lib and pythonX_d.lib for linking. cmake explicitly only adds one or the other, so the build fails.

This change does a couple of things:

  • In the cmake files, explicitly add the release version of the python library on Windows builds regardless of flavor. Since Py_DEBUG is not defined, pybind11 will always require release and it will be satisfied
  • To satisfy python as well, this change removes any explicit inclusions of Python.h on Windows instead relying on the fact that pybind11 headers will bring in what is needed

There are a few additional things that we could do but I rejected as unnecessary at this time:

  • define Py_DEBUG based on the CMAKE_BUILD_TYPE - this will *mostly* work, we'd have to think through multiconfig generators like VS, but it's possible. There doesn't seem to be a need to link against debug python at the moment, so I chose not to overcomplicate the build and always default to release
  • similar to above, but define Py_DEBUG based on the CMAKE_BUILD_TYPE *as well as* the presence of the debug python library (Python3_LIBRARY_DEBUG). Similar to above, this seems unnecessary right now. I think it's slightly better than above because most people don't actually have the debug version of python installed, so this would prevent breaks in that case.
  • similar to the two above, but add a cmake variable to control the logic
  • implement the pybind11 workaround directly in mlir (specifically in Interop.h) so that Python.h can still be included directly. This seems prone to error and a pain to maintain in lock step with pybind11
  • reorganize how the pybind11 headers are included and place at least one of them in Interop.h directly, so that the header has all of its dependencies included as was the original intention. I decided against this because it really doesn't need pybind11 logic and it's always included after pybind11 is, so we don't necessarily need the python includes

Diff Detail

Event Timeline

Herald added a project: Restricted Project. · View Herald TranscriptMay 9 2022, 6:07 PM
stella.stamenova requested review of this revision.May 9 2022, 6:07 PM
stellaraccident accepted this revision.May 9 2022, 6:34 PM

I am glad that an actual Windows dev (versus someone who just occasionally plays one on TV) is looking at this. I've never understood the windows debug/release/runtime version dichotomies when not just working on a stock VS based project that automatically does the right thing.

I'm fine with landing if we special case the header like I suggest. Otherwise, open to discuss more.

mlir/include/mlir-c/Bindings/Python/Interop.h
33–34

Ugh. Wdyt about #ifdefing the include to only be done on non-Windows? That would leave a nice place to put such a description and also makes clear exactly what would need to be backed out should this situation ever improve.

As a minor issue, we do have downstreams that do code quality/layering checks (not to mention IDE auto complete) based on including the right headers. Not the end of the world if that breaks for this case, but I would prefer that only the things that need the hack get it.

This revision is now accepted and ready to land.May 9 2022, 6:34 PM
stella.stamenova edited the summary of this revision. (Show Details)

I am glad that an actual Windows dev (versus someone who just occasionally plays one on TV) is looking at this. I've never understood the windows debug/release/runtime version dichotomies when not just working on a stock VS based project that automatically does the right thing.

I'm fine with landing if we special case the header like I suggest. Otherwise, open to discuss more.

To be honest, I don't think anyone understands this. It took me half a day of looking at source, building various bits and then looking at python, cmake and pybind11 sources to figure it out. I only hope I've documented enough of it so that next time we can skip most of it :)

I made the change to include the header on non-Windows platforms. I think a more comprehensive solution would be to refactor the inclusions in such a way that a pybind11 inclusion would make sense in interop.h instead of python.h, but the current solution works, it's documented and will hopefully be updated one day, when the cmake/python interaction is fixed.

I am glad that an actual Windows dev (versus someone who just occasionally plays one on TV) is looking at this. I've never understood the windows debug/release/runtime version dichotomies when not just working on a stock VS based project that automatically does the right thing.

I'm fine with landing if we special case the header like I suggest. Otherwise, open to discuss more.

To be honest, I don't think anyone understands this. It took me half a day of looking at source, building various bits and then looking at python, cmake and pybind11 sources to figure it out. I only hope I've documented enough of it so that next time we can skip most of it :)

There's so much history to this stuff...

Yeah, I predict the next time this gets looked at is when someone "fixes" something "better" upstream and we have to adapt to it. It'll be good to have the docs then.

I made the change to include the header on non-Windows platforms. I think a more comprehensive solution would be to refactor the inclusions in such a way that a pybind11 inclusion would make sense in interop.h instead of python.h, but the current solution works, it's documented and will hopefully be updated one day, when the cmake/python interaction is fixed.

Thanks for doing that. The interop.h file is special because it can be included by arbitrary Python extensions (not based on pybind) to interop Python/C. We actually do use it that way in some cases, so I think we don't want to be pulling pybind in there. So effectively, we treat the fact that we use pybind internally as an implementation detail. To things that interface, it is just the Python C API.

This revision was landed with ongoing or failed builds.May 9 2022, 7:48 PM
This revision was automatically updated to reflect the committed changes.