python: Enable out-of-tree build of pyme bindings.
authorJustus Winter <justus@g10code.com>
Mon, 11 Jul 2016 14:38:37 +0000 (16:38 +0200)
committerJustus Winter <justus@g10code.com>
Mon, 11 Jul 2016 16:09:54 +0000 (18:09 +0200)
* lang/python/MANIFEST.in: Update manifest template.
* lang/python/Makefile.am: Copy more files, move generation of files
to Python build script, add 'sdist' target to build a Python source
distribution.
* lang/python/gpgme-h-clean.py: Add code to build 'errors.i'.
* lang/python/setup.py.in: Generate files, enable out-of-tree builds.

Signed-off-by: Justus Winter <justus@g10code.com>
lang/python/MANIFEST.in
lang/python/Makefile.am
lang/python/gpgme-h-clean.py
lang/python/setup.py.in

index f079538..abdc08f 100644 (file)
@@ -1,5 +1,4 @@
-recursive-include examples *.py *.glade *.gladep
+recursive-include examples *.py
 include gpgme-h-clean.py gpgme.i
 include helpers.c helpers.h
-include Makefile
 recursive-include pyme *.py
index 527212a..89b1c28 100644 (file)
@@ -26,34 +26,30 @@ EXTRA_DIST = \
 SUBDIRS = tests
 
 COPY_FILES = \
+       $(srcdir)/gpgme.i \
        $(srcdir)/README \
+       $(srcdir)/MANIFEST.in \
+       $(srcdir)/gpgme-h-clean.py \
        $(srcdir)/pyme \
+       $(srcdir)/examples \
        $(srcdir)/helpers.c $(srcdir)/helpers.h
 
-# Cleanup gpgme.h from deprecated functions and typedefs.
-gpgme.h: ../../src/gpgme.h $(srcdir)/gpgme-h-clean.py
-       $(PYTHON) $(srcdir)/gpgme-h-clean.py $< >$@
-
 # For VPATH builds we need to copy some files because Python's
 # distutils are not VPATH-aware.
 copystamp: $(COPY_FILES)
        if test "$(srcdir)" != "$(builddir)" ; then cp -r $^ . ; fi
        touch $@
 
-errors.i:
-       sed -n -e \
-         '/GPG_ERR_[^ ]* =/s/ *\(.*\) = .*/%constant long \1 = \1;/p' \
-         `$(GPG_ERROR_CONFIG) --prefix`/include/gpg-error.h >$@
-
-gpgme_wrap.c pyme/pygpgme.py: gpgme.i errors.i gpgme.h copystamp
-       $(SWIG) -python -py3 -builtin $(SWIGOPT) \
-         -o $(builddir)/gpgme_wrap.c -outdir $(builddir)/pyme \
-         $<
-
-all-local: gpgme_wrap.c pyme/pygpgme.py copystamp
+all-local: copystamp
        CFLAGS="$(CFLAGS) -I$(top_srcdir)" \
          $(PYTHON) setup.py build --verbose
 
+dist/pyme3-$(VERSION).tar.gz: copystamp
+       CFLAGS="$(CFLAGS) -I$(top_srcdir)" \
+         $(PYTHON) setup.py sdist --verbose
+
+sdist: dist/pyme3-$(VERSION).tar.gz
+
 CLEANFILES = gpgme.h errors.i gpgme_wrap.c pyme/pygpgme.py \
          copystamp
 
@@ -65,7 +61,7 @@ clean-local:
        rm -rf -- build
        if test "$(srcdir)" != "$(builddir)" ; then \
          find . -type d ! -perm -200 -exec chmod u+w {} ';' ; \
-         rm -rf README pyme helpers.c helpers.h ; \
+         for F in $(COPY_FILES); do rm -rf -- `basename $$F` ; done ; \
        fi
 
 install-exec-local:
index b7052ff..b29b2e1 100755 (executable)
@@ -19,8 +19,8 @@
 
 import sys, re
 
-if len(sys.argv) < 2:
-    sys.stderr.write("Usage: %s gpgme.h\n" % sys.argv[0])
+if len(sys.argv) != 2:
+    sys.stderr.write("Usage: %s path/to/[gpgme|gpg-error].h\n" % sys.argv[0])
     sys.exit(1)
 
 deprec_func = re.compile(r'^(.*typedef.*|.*\(.*\)|[^#]+\s+.+)'
@@ -28,7 +28,7 @@ deprec_func = re.compile(r'^(.*typedef.*|.*\(.*\)|[^#]+\s+.+)'
                          re.S)
 line_break = re.compile(';|\\$|\\x0c|^\s*#|{');
 
-try:
+if 'gpgme.h' in sys.argv[1]:
     gpgme = open(sys.argv[1])
     tmp = gpgme.readline()
     text = ''
@@ -41,6 +41,10 @@ try:
         tmp = gpgme.readline()
     sys.stdout.write(text)
     gpgme.close()
-except IOError as errmsg:
-    sys.stderr.write("%s: %s\n" % (sys.argv[0], errmsg))
-    sys.exit(1)
+else:
+    filter_re = re.compile(r'GPG_ERR_[^ ]* =')
+    rewrite_re = re.compile(r' *(.*) = .*')
+    for line in open(sys.argv[1]):
+        if not filter_re.search(line):
+            continue
+        print(rewrite_re.sub(r'%constant long \1 = \1;', line.strip()))
index 9e6e008..4667a9d 100755 (executable)
 
 from distutils.core import setup, Extension
 import os, os.path, sys
+import glob
 import subprocess
 
-def getconfig(what):
-    confdata = subprocess.Popen(["../../src/gpgme-config", "--%s" % what],
+# Out-of-tree build of the pyme3 bindings.
+gpg_error_config = "gpg-error-config"
+gpgme_config = "gpgme-config"
+gpgme_h = ""
+library_dirs = []
+extra_swig_opts = []
+
+if os.path.exists("../../src/gpgme-config"):
+    # In-tree build.
+    in_tree = True
+    gpgme_config = "../../src/gpgme-config"
+    gpgme_h = "../../src/gpgme.h"
+    library_dirs = ["../../src/.libs"] # XXX uses libtool internals
+    extra_swig_opts = ["-DHAVE_DATA_H=1"]
+
+try:
+    subprocess.check_call([gpg_error_config, '--version'],
+                          stdout=subprocess.DEVNULL)
+except:
+    sys.exit("Could not find gpg-error-config.  " +
+             "Please install the libgpg-error development package.")
+
+try:
+    subprocess.check_call([gpgme_config, '--version'],
+                          stdout=subprocess.DEVNULL)
+except:
+    sys.exit("Could not find gpgme-config.  " +
+             "Please install the libgpgme development package.")
+
+def getconfig(what, config=gpgme_config):
+    confdata = subprocess.Popen([config, "--%s" % what],
                                 stdout=subprocess.PIPE).communicate()[0]
     return [x for x in confdata.decode('utf-8').split() if x != '']
 
+version = version_raw = getconfig("version")[0]
+if '-' in version:
+    version = version.split('-')[0]
+major, minor, patch = map(int, version.split('.'))
+
+if not (major > 1 or (major == 1 and minor >= 6)):
+    sys.exit('Need at least GPGME version 1.6, found {}.'.format(version_raw))
+
+if not gpgme_h:
+    gpgme_h = os.path.join(getconfig("prefix")[0], "include", "gpgme.h")
+
+gpg_error_prefix = getconfig("prefix", config=gpg_error_config)[0]
+gpg_error_h = os.path.join(gpg_error_prefix, "include", "gpg-error.h")
+if not os.path.exists(gpg_error_h):
+    gpg_error_h = \
+        glob.glob(os.path.join(gpg_error_prefix, "include",
+                               "*", "gpg-error.h"))[0]
+
+print("Building pyme3 using {} and {}.".format(gpgme_h, gpg_error_h))
+
+# Cleanup gpgme.h from deprecated functions and typedefs.
+subprocess.check_call(["python3", "gpgme-h-clean.py", gpgme_h],
+                      stdout=open("gpgme.h", "w"))
+subprocess.check_call(["python3", "gpgme-h-clean.py", gpg_error_h],
+                      stdout=open("errors.i", "w"))
+
 include_dirs = [os.getcwd()]
 define_macros = []
-library_dirs = ["../../src/.libs"] # XXX uses libtool internals
 libs = getconfig('libs')
 
 for item in getconfig('cflags'):
@@ -67,13 +122,29 @@ if uname_s.startswith("MINGW32"):
                library_dirs.append(os.path.join(tgt, item))
                break
 
-swige = Extension("pyme._pygpgme", ["gpgme_wrap.c", "helpers.c"],
+# We build an Extension using SWIG, which generates a Python module.
+# By default, the 'build_py' step is run before 'build_ext', and
+# therefore the generated Python module is not copied into the build
+# directory.
+# Bug: http://bugs.python.org/issue1016626
+# Workaround:
+# http://stackoverflow.com/questions/12491328/python-distutils-not-include-the-swig-generated-module
+from distutils.command.build import build
+class BuildExtFirstHack(build):
+    def run(self):
+        self.run_command('build_ext')
+        build.run(self)
+
+swige = Extension("pyme._pygpgme", ["gpgme.i", "helpers.c"],
+                  swig_opts = ['-py3', '-builtin',
+                               '-outdir', 'pyme'] + extra_swig_opts,
                   include_dirs = include_dirs,
                   define_macros = define_macros,
                   library_dirs = library_dirs,
                   extra_link_args = libs)
 
-setup(name = "pyme",
+setup(name="pyme3",
+      cmdclass={'build': BuildExtFirstHack},
       version="@VERSION@",
       description='Python bindings for GPGME GnuPG cryptography library',
       author='The GnuPG hackers',