diff --git a/lang/python/MANIFEST.in b/lang/python/MANIFEST.in index 8f63640a..ff38172d 100644 --- a/lang/python/MANIFEST.in +++ b/lang/python/MANIFEST.in @@ -1,4 +1,4 @@ recursive-include examples *.py -include gpgme-h-clean.py gpgme.i +include gpgme.i include helpers.c helpers.h private.h recursive-include gpg *.py diff --git a/lang/python/Makefile.am b/lang/python/Makefile.am index b9145f5b..42beeee2 100644 --- a/lang/python/Makefile.am +++ b/lang/python/Makefile.am @@ -21,12 +21,20 @@ EXTRA_DIST = \ MANIFEST.in \ gpgme.i \ helpers.c helpers.h private.h \ - gpgme-h-clean.py \ examples \ gpg SUBDIRS = . tests +COPY_FILES_GPG = \ + $(srcdir)/gpg/callbacks.py \ + $(srcdir)/gpg/constants \ + $(srcdir)/gpg/core.py \ + $(srcdir)/gpg/errors.py \ + $(srcdir)/gpg/__init__.py \ + $(srcdir)/gpg/results.py \ + $(srcdir)/gpg/util.py + .PHONY: prepare prepare: copystamp @@ -35,12 +43,14 @@ prepare: copystamp copystamp: ln -sf "$(abs_top_srcdir)/src/data.h" . ln -sf "$(abs_top_builddir)/config.h" . + if test $(srcdir) != . ; then cp -R $(COPY_FILES_GPG) gpg ; fi touch $@ all-local: copystamp set -e ; set $(PYTHONS); for VERSION in $(PYTHON_VERSIONS); do \ PYTHON="$$1" ; shift ; \ CFLAGS="$(CFLAGS)" \ + srcdir="$(srcdir)" \ abs_top_builddir="$(abs_top_builddir)" \ $$PYTHON setup.py build --verbose --build-base=python$${VERSION}-gpg ; \ done @@ -48,6 +58,7 @@ all-local: copystamp python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc: copystamp $(MKDIR_P) python$(PYTHON_VERSION)-gpg-dist CFLAGS="$(CFLAGS)" \ + srcdir="$(srcdir)" \ abs_top_builddir="$(abs_top_builddir)" \ $(PYTHON) setup.py sdist --verbose --dist-dir=python$(PYTHON_VERSION)-gpg-dist \ --manifest=python$(PYTHON_VERSION)-gpg-dist/MANIFEST @@ -73,6 +84,12 @@ CLEANFILES = copystamp \ # permissions. clean-local: rm -rf -- build + if test $(srcdir) != . ; then \ + find gpg -type d ! -perm -200 -exec chmod u+w {} ';' ; \ + for FILE in $(COPY_FILES_GPG); do \ + rm -rf -- gpg/$$(basename $$FILE) ; \ + done \ + fi for VERSION in $(PYTHON_VERSIONS); do \ find python$${VERSION}-gpg -type d ! -perm -200 -exec chmod u+w {} ';' ; \ rm -rf -- python$${VERSION}-gpg ; \ @@ -81,20 +98,18 @@ clean-local: install-exec-local: set -e ; set $(PYTHONS); for VERSION in $(PYTHON_VERSIONS); do \ PYTHON="$$1" ; shift ; \ + srcdir="$(srcdir)" \ abs_top_builddir="$(abs_top_builddir)" \ $$PYTHON setup.py \ build \ --build-base=python$${VERSION}-gpg \ install \ --prefix "$(DESTDIR)$(prefix)" \ - --record files.txt \ --verbose ; \ - cat files.txt >> install_files.txt ; \ - rm files.txt ; \ done - $(MKDIR_P) "$(DESTDIR)$(pythondir)/gpg" - mv install_files.txt "$(DESTDIR)$(pythondir)/gpg" uninstall-local: - xargs < "$(DESTDIR)$(pythondir)/gpg/install_files.txt" -- rm -rf -- - rm -rf -- "$(DESTDIR)$(pythondir)/gpg" + GV=$$(echo $(VERSION) | tr - _); for PV in $(PYTHON_VERSIONS); do \ + rm -rf -- "$(DESTDIR)$(prefix)/lib/python$$PV/site-packages/gpg" \ +"$(DESTDIR)$(prefix)/lib/python$$PV/site-packages/gpg-$$GV-py$$PV.egg-info" ; \ + done diff --git a/lang/python/gpgme-h-clean.py b/lang/python/gpgme-h-clean.py deleted file mode 100755 index 52f86764..00000000 --- a/lang/python/gpgme-h-clean.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python - -# Copyright (C) 2016 g10 Code GmbH -# Copyright (C) 2004,2008 Igor Belyi -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -from __future__ import absolute_import, print_function, unicode_literals -del absolute_import, print_function, unicode_literals - -import sys, re - -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+.+)' - + r'\s*_GPGME_DEPRECATED(_OUTSIDE_GPGME)?\(.*\);\s*', - re.S) -line_break = re.compile(';|\\$|\\x0c|^\s*#|{'); - -if 'gpgme.h' in sys.argv[1]: - gpgme = open(sys.argv[1]) - tmp = gpgme.readline() - text = '' - while tmp: - text += re.sub(' class ', ' _py_obsolete_class ', tmp) - if line_break.search(tmp): - if not deprec_func.search(text): - sys.stdout.write(text) - text = '' - tmp = gpgme.readline() - sys.stdout.write(text) - gpgme.close() -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())) diff --git a/lang/python/setup.py.in b/lang/python/setup.py.in index 6692de6f..2114aafe 100755 --- a/lang/python/setup.py.in +++ b/lang/python/setup.py.in @@ -1,7 +1,7 @@ #!/usr/bin/env python -# Copyright (C) 2016 g10 Code GmbH -# Copyright (C) 2004 Igor Belyi +# Copyright (C) 2016-2017 g10 Code GmbH +# Copyright (C) 2004,2008 Igor Belyi # Copyright (C) 2002 John Goerzen # # This library is free software; you can redistribute it and/or @@ -19,11 +19,18 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from distutils.core import setup, Extension +import argparse import os, os.path, sys import glob +import re import shutil import subprocess +# We parse a subset of the arguments. +parser = argparse.ArgumentParser(add_help=False) +parser.add_argument('--build-base', default='') +options, _ = parser.parse_known_args() + # Out-of-tree build of the gpg bindings. gpg_error_config = ["gpg-error-config"] gpgme_config_flags = ["--thread=pthread"] @@ -31,6 +38,7 @@ gpgme_config = ["gpgme-config"] + gpgme_config_flags gpgme_h = "" include_dirs = [os.getcwd()] library_dirs = [] +vpath_build = os.environ.get('srcdir', '.') != '.' in_tree = False extra_swig_opts = [] extra_macros = dict() @@ -133,6 +141,14 @@ if uname_s.startswith("MINGW32"): library_dirs.append(os.path.join(tgt, item)) break +def in_srcdir(name): + return os.path.join(os.environ.get("srcdir", ""), name) +def in_build_base(name): + return os.path.join(options.build_base, name) +def up_to_date(source, target): + return (os.path.exists(target) + and os.path.getmtime(source) <= os.path.getmtime(target)) + # 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 @@ -143,25 +159,60 @@ if uname_s.startswith("MINGW32"): from distutils.command.build import build class BuildExtFirstHack(build): + def _generate_gpgme_h(self, source_name, sink_name): + if up_to_date(source_name, sink_name): + return + + deprec_func = re.compile(r'^(.*typedef.*|.*\(.*\)|[^#]+\s+.+)' + + r'\s*_GPGME_DEPRECATED(_OUTSIDE_GPGME)?\(.*\);\s*', + re.S) + line_break = re.compile(';|\\$|\\x0c|^\s*#|{') + + with open(sink_name, "w") as sink, open(source_name) as source: + text = '' + for line in source: + text += re.sub(' class ', ' _py_obsolete_class ', line) + if line_break.search(line): + if not deprec_func.search(text): + sink.write(text) + text = '' + sink.write(text) + + def _generate_errors_i(self, source_name, sink_name): + if up_to_date(source_name, sink_name): + return + + filter_re = re.compile(r'GPG_ERR_[^ ]* =') + rewrite_re = re.compile(r' *(.*) = .*') + + with open(sink_name, "w") as sink, open(source_name) as source: + for line in source: + if not filter_re.search(line): + continue + sink.write(rewrite_re.sub(r'%constant long \1 = \1;'+'\n', line.strip())) + def _generate(self): print("Building python gpg module using {} and {}.".format(gpgme_h, gpg_error_h)) # Cleanup gpgme.h from deprecated functions and typedefs. - # Keep timestamp to avoid rebuild if not os.path.exists(self.build_base): os.makedirs(self.build_base) - for src, dst in ( - (gpgme_h, os.path.join(self.build_base, "gpgme.h")), - (gpg_error_h, os.path.join(self.build_base, "errors.i")) - ): - subprocess.check_call([sys.executable, "gpgme-h-clean.py", src], - stdout=open(dst, "w")) - shutil.copystat(src, dst) + self._generate_gpgme_h(gpgme_h, in_build_base("gpgme.h")) + self._generate_errors_i(gpg_error_h, in_build_base("errors.i")) + + # Keep timestamp to avoid rebuild + for source, target in ((gpgme_h, in_build_base("gpgme.h")), + (gpg_error_h, in_build_base("errors.i"))): + if not up_to_date(source, target): + shutil.copystat(source, target) # Copy due to http://bugs.python.org/issue2624 # Avoid creating in srcdir - shutil.copy2("gpgme.i", self.build_base) + for source, target in ((in_srcdir(n), in_build_base(n)) + for n in ('gpgme.i', 'helpers.c')): + if not up_to_date(source, target): + shutil.copy2(source, target) def run(self): self._generate() @@ -171,14 +222,18 @@ class BuildExtFirstHack(build): os.makedirs(os.path.join(self.build_lib, "gpg")) swig_sources.append(os.path.join(self.build_base, 'gpgme.i')) - swig_opts.extend(['-I' + self.build_base, '-outdir', os.path.join(self.build_lib, 'gpg')]) + swig_opts.extend(['-I' + self.build_base, + '-I' + in_srcdir('.'), + '-outdir', os.path.join(self.build_lib, 'gpg')]) + if vpath_build: + include_dirs.append(in_srcdir('.')) include_dirs.append(self.build_base) self.run_command('build_ext') build.run(self) py3 = [] if sys.version_info.major < 3 else ['-py3'] -swig_sources = ['helpers.c'] +swig_sources = [in_build_base('helpers.c')] swig_opts = ['-threads'] + py3 + extra_swig_opts swige = Extension("gpg._gpgme", sources = swig_sources,