Page MenuHome GnuPG

Port GPGME's Python bindings to Windows
Open, NormalPublic

Event Timeline

Clearly getting SWIG and Windows to play together nicely is a bit of a big ask, but it may be possible to leverage GPGME's compiled libraries with something like CFFI's ABI calling method (yeah, I know, ABI is never ideal, but it's better than what Windows has now).

Testing this is already on my todo list, but it'd probably require a very static build so that it can be made to work on whatever type of Windows system people have. As distinct from the complete lack of them here (unsurprisingly).

Though a CFFI/ABI solution may be the only option, it would still be preferable to get SWIG working under Windows. The reasons for this are many, but not least of which would include not needing to duplicate effort to accommodate Windows, no functionality mismatch due to using the Windows version and not needing to implement every function manually since CFFI can't generate low level bindings the same way that SWIG does.

This has meant delving a little deeper into SWIG's documentation and one matter which needs confirmation relates to the compilers used to build GPGME, SWIG and Python on Windows systems. It is possible that for at least those three things and possibly for the rest of the GnuPG stack as well, they all need to be the same compiler and libraries. SWIG, however, includes a requirement that it must be built with the GNU utilities and nothing else. The GnuPG stack has no problems with that restriction if necessary and Python can be compiled with it too. It is, however, highly likely that Windows users are not installing Python by compiling it from source themselves, it is more likely that they're using the Windows installers available from the Python website and for fairly understandable reasons. Still, it could very well be that that precompiled version of Python is what cannot work with SWIG rather than Windows in general.

Unfortunately confirmation of this theory requires access to a Windows environment to develop and test in which is presently unavailable.

Whereas it is possible that a variation on a possible solution for another platform which could also be developed on a non-Windows platform could become an alternative option here. This, however, requires a little more investigation too which may be conducted during the course of other aspects of the bindings development.

Windows 10 was obtained last week and the process of preparing a Windows build env began earlier today.

I now remember why I loathe this operating system.

Commit 8613727f1ee985c3cfa2c815523312914f033ffd adds considerable detail on both the issues affecting compiling and installing a Windows version of the bindings and what it would take to actually resolve it.

A subsequent discussion with @aheinecke led to raising the possibility of including a precompiled build of the bindings for whatever the latest stable CPython minor version is released from python.org and including that as an optional extra with GPG4Win.

If testing of this is successful then we can at least provide an “out of the box” solution for Windows users with whatever is current at the time and, of course, a replicatable build process for others to adapt to their own circumstances if necessary.

It will not, however, be feasible to provide multiple CPython version builds simultaneously, as can currently be built by default with installations on POSIX systems. On the other hand, it would mean we could maintain the standard answer regarding doing anything with GnuPG on Windows and cite Intevation and GPG4Win as the solution.

Though not directly related to our issues, this bug report on the MSYS2 site reported by their users encountering trouble with GPGME provides additional weight to irreconcilable differences between MSYS2 and GnuPG:

https://sourceforge.net/p/msys2/discussion/general/thread/86b16f3f/

Specifically that the MSYS2 devs are explicitly not supporting 64-bit architecture.

So that's that. It also indicates that their project will dead end in early 2038, if not before.

Reading through this issue and the related documentation: Thanks for writing this all down and adding links!

Especially interesting is
https://dev.gnupg.org/source/gpgme/browse/master/lang/python/doc/src/gpgme-python-howto;1bdab961c51fc038c33ae8116595aa0213ab1dd5$476

How can we move forward?
Guidance maybe provided by the question: What would be the platform most useful to users?

What about trying to build versions for the current stable version of the default CPython build?
According to https://www.python.org/downloads/windows/ this would be Python 3.7.2
and as most new versions are 64 bit, so we'd have the following list, in decreasing importance:

  1. CPython 3.7.2 x86-64 (for both Intel and AMD according to https://www.python.org/downloads/release/python-372/
  2. CPython 3.7.2 32bit
  3. CPython 2.7.16 32bit (probably also useful on 64bit operating systems)

There are Visual Studio 2015 or later Azure images, which could be rented for automated builds in theory.
We could also ask the Python Software Foundation how they are preparing their releases.

Once we have build one useful version, we could identify the next useful one.
And we could also evaluate our options. Beside CFFI there is also the possibility to hand-craft a python extension module.

CFFI has no real means of generating the needed bindings on the fly
like SWIG does, except via its ABI methods, but those are inferior to
what SWIG does. It also can't handle all the ifdefs (or really any of
the ifdefs) in gpgme.h.

If this weren't the case, there wouldn't be that section on why it
can't (yet) be used with GPGME. Basically, though, it boils down to
the C parser used by CFFI being incomplete. Until it can handle all
of C99, it won't help.

The only other non-SWIG option is Python's built-in c-types. That,
however, would require integrating with all existing C code and would
need to be maintained with each update to the C code manually. This
would probably work, but would *vastly* increase the amount of effort
required in the standard development process. It is doubtful this
would gain anything for the amount of work required.

All three methods would still require building Python, GPGME, and the
Python bindings with the same Windows C runtime. Which brings us back
to the current situation.

I still think the most expeditious means of achieving this is to try
building GPG4Win with the relevant runtime and, for at least the first
such test, try compiling Python from source as part of that build. If
it works then simply integrating future GPG4Win builds with GPGME
built against the most common modern Windows C runtime (from Visual
Studio 2017) should work with Python 3.7 and above. Using the 2015
version should work with Python 3.5 and 3.6.

Since it still does not yet work, but the cause has been identified;
it's acceptable to make a cut off point for Python versions on Windows
that's more recent than is supported on POSIX systems. So getting it
to work with Python 3.7 and above, then only supporting that for
Windows isn't too unreasonable. Especially if the process is
documented well enough that people could "back port" it to earlier
Python versions by either compiling Python with the same runtime or
compiling GPGME with the runtime that matches the earlier official
Python release.

Andre probably knows more about which C runtime is used in GPG4Win,
though, and whether that's any of the Visual Studio runtimes. If it
doesn't use an official Microsoft runtime in the build process, then
that will make things somewhat trickier. Not impossible, but
definitely trickier.

FWIW, GPGME is basically C90 and we only recently started to use C99 variadic macros - they are a cpp feature, though.

Okay.

On further thought, it's possible that something closer to what
Bernhard wants (and incidentally more along the lines of what I was
thinking of in some of our discussions just after the initial port)
might be achievable with Cython.

It would still be a huge undertaking to fully replicate what SWIG is
doing, but it would be a lot easier than working with both SWIG and
CPython's ctypes directly. It's also feature complete, unlike Python's
implementation of CFFI. The reason for the constant calls to use CFFI
anyway is because it's *insanely* easy to work with for those parts of
C it can actually work with. Which is why it's used with libsodium
and PyNaCl.

werner lowered the priority of this task from High to Normal.Jan 5 2021, 10:59 AM

Given all the resources we had put on this Python bindings I'd suggest to bite the bullet and replace Swig by handcrafted bindings. More work but we do it for the other bindings as well.

Hi Werner,

we do it for the other bindings as well.

can you elaborate?

The C++, CL, Javascript and QT Bindings are all written by hand.