Skip to content

Build a self-contained Win32 Python wheel for Concordance#57

Draft
Ben-Meister wants to merge 9 commits into
jaymzh:mainfrom
Ben-Meister:win32wheel
Draft

Build a self-contained Win32 Python wheel for Concordance#57
Ben-Meister wants to merge 9 commits into
jaymzh:mainfrom
Ben-Meister:win32wheel

Conversation

@Ben-Meister

@Ben-Meister Ben-Meister commented Apr 17, 2024

Copy link
Copy Markdown
Contributor

This pull request builds a 32-bit Win32 Python wheel for libconcord, the bindings and all of its dependencies. This wheel simplifies the installation (and removal) process on Windows. It also fixes the DLL load path issue, including the changes to DLL load path made in Python 3.8+.

The wheel may be used individually, or as part of an installation process. In the companion pull request to Congruity, we consume this wheel to make an all-in-one installer for Congruity. It installs a self-contained 32-bit version of Python, Congruity, this wheel, and all other package and binary dependencies required.

Because the compiled DLLs are 32-bit, a 32-bit Python version is required to use them. However, the 32-bit Python (and our installer) will happily run on a 64-bit Windows system. The wheel is compatible with and tested on Windows 11, 10, and 8.1, both the 32- and 64-bit versions.

The wheel is built as part of the ci-windows.yml GitHub workflow via the added make_wheel.py, which dynamically assembles the package using the same files that are built for the NSIS installer. A test has been added for the wheel to this workflow as well, based on the similar test present in ci-linux.yml.

The wheel and bindings are compatible with Python versions both pre- and post-3.8. In Python 3.8 (released in 2019), the DLL search path was narrowed, and os.add_dll_directory was introduced (link). We use with os.add_dll_directory to add the package directory to Python’s DLL search path when loading the libconcord DLLs (after which it’s immediately removed). If an app wants the DLLs in a different directory, the app can call os.add_dll_directory before importing the libconcord bindings. Fallback logic has been added to perform a similar method using PATH (temporary for the calling process only) when the bindings are used on Python 3.7 or earlier and os.add_dll_directory is not available.

The binding behavior, Python package format, and version compatibility are unchanged for Linux/Mac. On these platforms, the changes to libconcord.py are silently ignored.

@swt2c

swt2c commented Apr 17, 2024

Copy link
Copy Markdown
Contributor

Thank you for your contribution. My initial quick thoughts: First of all, my opinion is that we don't need to support Python 3.7 as it has been EOL for a year or so, so you could probably drop that commit.

Second - why do we need separate pyproject.toml and setup.py files for Windows?

@Ben-Meister Ben-Meister force-pushed the win32wheel branch 2 times, most recently from a8ac713 to 0ed3274 Compare April 18, 2024 20:46
@Ben-Meister

Copy link
Copy Markdown
Contributor Author

Both changes have been made as requested.

@jaymzh jaymzh self-requested a review March 5, 2025 23:02

@jaymzh jaymzh left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@swt2c - thoughts here?

Comment thread .github/workflows/ci-windows.yml Outdated
run: |
pip install build
MINGW_SYSROOT_BIN="/usr/i686-w64-mingw32/sys-root/mingw/bin" concordance/win/make_wheel.py
- uses: actions/upload-artifact@v3

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'll need to move this to v4

Comment thread .github/workflows/ci-windows.yml Outdated
Start-Sleep -Seconds 5
cd "C:\Program Files (x86)\Concordance"
.\concordance.exe --version
- uses: actions/download-artifact@v3

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

@Ben-Meister

Copy link
Copy Markdown
Contributor Author

Thank you both for reviewing this. Should I rebase the PR to the current changes?

I’m glad that this project is still being kept up to date.

FYI: I also have changes planned for submission as a subsequent PR, that build upon this PR to produce a version with curl supporting schannel, to fix the issue uncovered during testing on Windows wherein the required root certificates were not being downloaded. Scott @swt2c and I discussed this in my pull request to congruity congruity/congruity#12 (comment) and they are in my https://github.com/Ben-Meister/concordance/tree/buildcurl branch (from before the v4 artifact change so I’ll have to update those too). Let me know how to proceed.

Unfortunately I was absent from this project for far too long; my apologies. I’ll need to untangle what I have in order to finish this up.

Comment thread concordance/win/make_wheel.py Outdated
dll = line.split('=>')[1].strip()
if dll != 'not found':
shutil.copy2(dll, subdir)
shutil.copy2(os.path.dirname(os.path.abspath(__file__)) + '/../.libs/concordance.exe', subdir)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you including concordance.exe in the wheel?

Also are the setup.py and pyproject.toml needed either?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scott I appreciate your feedback.

Re: the EXE. I’ll make the change to remove it, but I see pros and cons.

Reasons to not include the EXE: it’s not required, we can reduce our surface/size etc. And I did produce a wheel without it, as I was getting false positives from VirusTotal when I included it.

Reasons to include the EXE: it only adds 160KB. Then you have concordance available. A nice option if you’d like to isolate problems to congruity or concordance, or simply want an alternative. Otherwise you must download and install concordance separately.

Re: setup.py and pyproject.toml: These are not included in the produced wheel. They are used in the staging process, since the make_wheel.py script puts together a temporary tree for the build (based off what you did for make_installer.py, thank you!) and then calls python3 -m build -w from that location.

@swt2c

swt2c commented Mar 6, 2025

Copy link
Copy Markdown
Contributor

I'm wondering more generally if we should just start building wheels (maybe Windows-only for now) and actually publish them to PyPI? We'd have to build for all Pythons, but the contents would actually be the same for all, probably.

I've got some recent experience in this area and can help, if this is too much scope creep for your original PR. :-)

@swt2c

swt2c commented Mar 7, 2025

Copy link
Copy Markdown
Contributor

I'm wondering more generally if we should just start building wheels (maybe Windows-only for now) and actually publish them to PyPI? We'd have to build for all Pythons, but the contents would actually be the same for all, probably.

I've got some recent experience in this area and can help, if this is too much scope creep for your original PR. :-)

Actually, strike that. Can we simplify this approach quite a bit by doing the following:
python3 -m build -w -C="--build-option=--plat-name" -C="--build-option=win32"
to build the wheel and then use delvewheel to "repair" the wheel (ie, include the .dlls it needs).

Then I think we don't need to the changes to setup.py and we don't need that custom wheel build script?

@Ben-Meister

Ben-Meister commented Mar 8, 2025

Copy link
Copy Markdown
Contributor Author

Publishing a Concordance wheel has value, as it would make it much easier to install and use from Python. And Scott, I like your PyPI idea for the future. I understand certainly that you have a lot of projects that you are responsible for. Is this something you would like to maintain? Unfortunately I will have to contribute on an as-available basis at this time.

My thought is that we can start with the minimum viable product that we have already been able to produce using this code right here: a Win32 wheel, published on GitHub. Then we can worry about other architectures, OS’s, and PyPI.

The delvewheel tool is a cool find (thanks!) but won’t work for our use case without nasty kludges. The reason is that we load the libconcord DLL dynamically using cffi cdll. delvewheel isn’t designed for that. It’s designed for a wheel that has PYDs already in it and needs their deps resolved. Unfortunately, telling delvewheel to add the DLL won’t "just work" for our use case. By the time we hack around everything, it will be more complex than this PR that we already have. I can go into more details if desired.

@swt2c

swt2c commented Mar 10, 2025

Copy link
Copy Markdown
Contributor

The delvewheel tool is a cool find (thanks!) but won’t work for our use case without nasty kludges. The reason is that we load the libconcord DLL dynamically using cffi cdll. delvewheel isn’t designed for that. It’s designed for a wheel that has PYDs already in it and needs their deps resolved. Unfortunately, telling delvewheel to add the DLL won’t "just work" for our use case. By the time we hack around everything, it will be more complex than this PR that we already have. I can go into more details if desired.

No, you are right, delvewheel is not a good fit.

@jaymzh

jaymzh commented Apr 21, 2026

Copy link
Copy Markdown
Owner

as a non-windows user, I have almost no opinion on this, but happy to merge it if we think it's worthwhile. @swt2c and @Ben-Meister ?

@Ben-Meister

Copy link
Copy Markdown
Contributor Author

I still think it's worthwhile. In lieu of PyPI, the wheel's the next best thing, and the work here would be considered a prereq for if we were to do PyPI. Makes usage a lot easier on Windows. And my all-in-one installer uses it too.

I'll ask the obvious question. Currently Windows still has support for 32-bit applications, and Python still has 32-bit builds for Windows, so this is fine for now. At some point in the future we might wind up doing 64-bit Windows builds. Are there any known code paths we would have to specifically watch for issues? Or is it a matter of try it, thoroughly test, and see what breaks?

@Ben-Meister Ben-Meister marked this pull request as draft April 28, 2026 04:32
@Ben-Meister

Copy link
Copy Markdown
Contributor Author

Marking this one as draft for now since it's hardcoded Win32

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants