Blame lang/python/setup.py.in

Packit d7e8d0
#!/usr/bin/env python
Packit d7e8d0
Packit d7e8d0
# Copyright (C) 2016-2017 g10 Code GmbH
Packit d7e8d0
# Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
Packit d7e8d0
# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
Packit d7e8d0
#
Packit d7e8d0
#    This library is free software; you can redistribute it and/or
Packit d7e8d0
#    modify it under the terms of the GNU Lesser General Public
Packit d7e8d0
#    License as published by the Free Software Foundation; either
Packit d7e8d0
#    version 2.1 of the License, or (at your option) any later version.
Packit d7e8d0
#
Packit d7e8d0
#    This library is distributed in the hope that it will be useful,
Packit d7e8d0
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d7e8d0
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit d7e8d0
#    Lesser General Public License for more details.
Packit d7e8d0
#
Packit d7e8d0
#    You should have received a copy of the GNU Lesser General Public
Packit d7e8d0
#    License along with this library; if not, write to the Free Software
Packit d7e8d0
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
Packit d7e8d0
Packit d7e8d0
from distutils.core import setup, Extension
Packit d7e8d0
import os, os.path, sys
Packit d7e8d0
import glob
Packit d7e8d0
import re
Packit d7e8d0
import shutil
Packit d7e8d0
import subprocess
Packit d7e8d0
Packit d7e8d0
# Out-of-tree build of the gpg bindings.
Packit d7e8d0
gpg_error_config = ["gpg-error-config"]
Packit d7e8d0
gpgme_config_flags = ["--thread=pthread"]
Packit d7e8d0
gpgme_config = ["gpgme-config"] + gpgme_config_flags
Packit d7e8d0
gpgme_h = ""
Packit d7e8d0
include_dirs = [os.getcwd()]
Packit d7e8d0
library_dirs = []
Packit d7e8d0
in_tree = False
Packit d7e8d0
extra_swig_opts = []
Packit d7e8d0
extra_macros = dict()
Packit d7e8d0
Packit d7e8d0
top_builddir = os.environ.get("top_builddir")
Packit d7e8d0
if top_builddir:
Packit d7e8d0
    # In-tree build.
Packit d7e8d0
    in_tree = True
Packit d7e8d0
    gpgme_config = [os.path.join(top_builddir, "src/gpgme-config")] + gpgme_config_flags
Packit d7e8d0
    gpgme_h = os.path.join(top_builddir, "src/gpgme.h")
Packit d7e8d0
    library_dirs = [os.path.join(top_builddir, "src/.libs")] # XXX uses libtool internals
Packit d7e8d0
    extra_macros.update(
Packit d7e8d0
        HAVE_CONFIG_H=1,
Packit d7e8d0
        HAVE_DATA_H=1,
Packit d7e8d0
        IN_TREE_BUILD=1,
Packit d7e8d0
    )
Packit d7e8d0
Packit d7e8d0
if hasattr(subprocess, "DEVNULL"):
Packit d7e8d0
    devnull = subprocess.DEVNULL
Packit d7e8d0
else:
Packit d7e8d0
    devnull = open(os.devnull, "w")
Packit d7e8d0
Packit d7e8d0
try:
Packit d7e8d0
    subprocess.check_call(gpgme_config + ['--version'],
Packit d7e8d0
                          stdout=devnull)
Packit d7e8d0
except:
Packit d7e8d0
    sys.exit("Could not find gpgme-config.  " +
Packit d7e8d0
             "Please install the libgpgme development package.")
Packit d7e8d0
Packit d7e8d0
def getconfig(what, config=gpgme_config):
Packit d7e8d0
    confdata = subprocess.Popen(config + ["--%s" % what],
Packit d7e8d0
                                stdout=subprocess.PIPE).communicate()[0]
Packit d7e8d0
    return [x for x in confdata.decode('utf-8').split() if x != '']
Packit d7e8d0
Packit d7e8d0
version = version_raw = getconfig("version")[0]
Packit d7e8d0
if '-' in version:
Packit d7e8d0
    version = version.split('-')[0]
Packit d7e8d0
major, minor, patch = map(int, version.split('.'))
Packit d7e8d0
Packit d7e8d0
if not (major > 1 or (major == 1 and minor >= 7)):
Packit d7e8d0
    sys.exit('Need at least GPGME version 1.7, found {}.'.format(version_raw))
Packit d7e8d0
Packit d7e8d0
if not gpgme_h:
Packit d7e8d0
    gpgme_h = os.path.join(getconfig("prefix")[0], "include", "gpgme.h")
Packit d7e8d0
Packit d7e8d0
define_macros = []
Packit d7e8d0
libs = getconfig('libs')
Packit d7e8d0
Packit d7e8d0
# Define extra_macros for both the SWIG and C code
Packit d7e8d0
for k, v in extra_macros.items():
Packit d7e8d0
    extra_swig_opts.append("-D{0}={1}".format(k, v))
Packit d7e8d0
    define_macros.append((k, str(v)))
Packit d7e8d0
Packit d7e8d0
for item in getconfig('cflags'):
Packit d7e8d0
    if item.startswith("-I"):
Packit d7e8d0
        include_dirs.append(item[2:])
Packit d7e8d0
    elif item.startswith("-D"):
Packit d7e8d0
        defitem = item[2:].split("=", 1)
Packit d7e8d0
        if len(defitem)==2:
Packit d7e8d0
            define_macros.append((defitem[0], defitem[1]))
Packit d7e8d0
        else:
Packit d7e8d0
            define_macros.append((defitem[0], None))
Packit d7e8d0
Packit d7e8d0
# Adjust include and library locations in case of win32
Packit d7e8d0
uname_s = os.popen("uname -s").read()
Packit d7e8d0
if uname_s.startswith("MINGW32"):
Packit d7e8d0
   mnts = [x.split()[0:3:2] for x in os.popen("mount").read().split("\n") if x]
Packit d7e8d0
   tmplist = sorted([(len(x[1]), x[1], x[0]) for x in mnts])
Packit d7e8d0
   tmplist.reverse()
Packit d7e8d0
   extra_dirs = []
Packit d7e8d0
   for item in include_dirs:
Packit d7e8d0
       for ln, mnt, tgt in tmplist:
Packit d7e8d0
           if item.startswith(mnt):
Packit d7e8d0
               item = os.path.normpath(item[ln:])
Packit d7e8d0
               while item[0] == os.path.sep:
Packit d7e8d0
                   item = item[1:]
Packit d7e8d0
               extra_dirs.append(os.path.join(tgt, item))
Packit d7e8d0
               break
Packit d7e8d0
   include_dirs += extra_dirs
Packit d7e8d0
   for item in [x[2:] for x in libs if x.startswith("-L")]:
Packit d7e8d0
       for ln, mnt, tgt in tmplist:
Packit d7e8d0
           if item.startswith(mnt):
Packit d7e8d0
               item = os.path.normpath(item[ln:])
Packit d7e8d0
               while item[0] == os.path.sep:
Packit d7e8d0
                   item = item[1:]
Packit d7e8d0
               library_dirs.append(os.path.join(tgt, item))
Packit d7e8d0
               break
Packit d7e8d0
Packit d7e8d0
def in_srcdir(name):
Packit d7e8d0
    return os.path.join(os.environ.get("srcdir", ""), name)
Packit d7e8d0
def up_to_date(source, target):
Packit d7e8d0
    return (os.path.exists(target)
Packit d7e8d0
            and os.path.getmtime(source) <= os.path.getmtime(target))
Packit d7e8d0
Packit d7e8d0
# We build an Extension using SWIG, which generates a Python module.
Packit d7e8d0
# By default, the 'build_py' step is run before 'build_ext', and
Packit d7e8d0
# therefore the generated Python module is not copied into the build
Packit d7e8d0
# directory.
Packit d7e8d0
# Bug: http://bugs.python.org/issue1016626
Packit d7e8d0
# Workaround:
Packit d7e8d0
# http://stackoverflow.com/questions/12491328/python-distutils-not-include-the-swig-generated-module
Packit d7e8d0
from distutils.command.build import build
Packit d7e8d0
class BuildExtFirstHack(build):
Packit d7e8d0
Packit d7e8d0
    def _read_header(self, header, cflags):
Packit d7e8d0
        tmp_include = self._in_build_base("include1.h")
Packit d7e8d0
        with open(tmp_include, 'w') as f:
Packit d7e8d0
            f.write("#include <%s>" % header)
Packit d7e8d0
        return subprocess.check_output(os.environ.get('CPP', 'cc -E').split() + cflags + [tmp_include]).decode('utf-8')
Packit d7e8d0
Packit d7e8d0
    def _write_if_unchanged(self, target, content):
Packit d7e8d0
        if os.path.exists(target):
Packit d7e8d0
            with open(target) as f:
Packit d7e8d0
                if f.read() == content:
Packit d7e8d0
                    return
Packit d7e8d0
Packit d7e8d0
        with open(target, "w") as sink:
Packit d7e8d0
            sink.write(content)
Packit d7e8d0
Packit d7e8d0
    def _generate_gpgme_h(self, source_name, sink_name):
Packit d7e8d0
        if up_to_date(source_name, sink_name):
Packit d7e8d0
            return
Packit d7e8d0
Packit d7e8d0
        print("Using gpgme.h from {}".format(source_name))
Packit d7e8d0
Packit d7e8d0
        deprec_func = re.compile(r'^(.*typedef.*|.*\(.*\)|[^#]+\s+.+)'
Packit d7e8d0
                                 + r'\s*_GPGME_DEPRECATED(_OUTSIDE_GPGME)?\(.*\);\s*',
Packit d7e8d0
                                 re.S)
Packit d7e8d0
        line_break = re.compile(';|\\$|\\x0c|^\s*#|{')
Packit d7e8d0
Packit d7e8d0
        with open(sink_name, "w") as sink, open(source_name) as source:
Packit d7e8d0
            text = ''
Packit d7e8d0
            for line in source:
Packit d7e8d0
                text += re.sub(' class ', ' _py_obsolete_class ', line)
Packit d7e8d0
                if line_break.search(line):
Packit d7e8d0
                    if not deprec_func.search(text):
Packit d7e8d0
                        sink.write(text)
Packit d7e8d0
                    text = ''
Packit d7e8d0
            sink.write(text)
Packit d7e8d0
Packit d7e8d0
    def _generate_errors_i(self):
Packit d7e8d0
Packit d7e8d0
        try:
Packit d7e8d0
            subprocess.check_call(gpg_error_config + ['--version'],
Packit d7e8d0
                                  stdout=devnull)
Packit d7e8d0
        except:
Packit d7e8d0
            sys.exit("Could not find gpg-error-config.  " +
Packit d7e8d0
                     "Please install the libgpg-error development package.")
Packit d7e8d0
Packit d7e8d0
        gpg_error_content = self._read_header("gpg-error.h", getconfig("cflags", config=gpg_error_config))
Packit d7e8d0
Packit d7e8d0
        filter_re = re.compile(r'GPG_ERR_[^ ]* =')
Packit d7e8d0
        rewrite_re = re.compile(r' *(.*) = .*')
Packit d7e8d0
Packit d7e8d0
        errors_i_content = ''
Packit d7e8d0
        for line in gpg_error_content.splitlines():
Packit d7e8d0
            if not filter_re.search(line):
Packit d7e8d0
                continue
Packit d7e8d0
            errors_i_content += rewrite_re.sub(r'%constant long \1 = \1;'+'\n', line.strip())
Packit d7e8d0
Packit d7e8d0
        self._write_if_unchanged(self._in_build_base("errors.i"), errors_i_content)
Packit d7e8d0
Packit d7e8d0
    def _in_build_base(self, name):
Packit d7e8d0
        return os.path.join(self.build_base, name)
Packit d7e8d0
Packit d7e8d0
    def _generate(self):
Packit d7e8d0
        # Cleanup gpgme.h from deprecated functions and typedefs.
Packit d7e8d0
        if not os.path.exists(self.build_base):
Packit d7e8d0
            os.makedirs(self.build_base)
Packit d7e8d0
Packit d7e8d0
        self._generate_gpgme_h(gpgme_h, self._in_build_base("gpgme.h"))
Packit d7e8d0
        self._generate_errors_i()
Packit d7e8d0
Packit d7e8d0
        # Copy due to http://bugs.python.org/issue2624
Packit d7e8d0
        # Avoid creating in srcdir
Packit d7e8d0
        for source, target in ((in_srcdir(n), self._in_build_base(n))
Packit d7e8d0
                               for n in ('gpgme.i', 'helpers.c', 'private.h', 'helpers.h')):
Packit d7e8d0
            if not up_to_date(source, target):
Packit d7e8d0
                shutil.copy2(source, target)
Packit d7e8d0
Packit d7e8d0
        # Append generated files via build_base
Packit d7e8d0
        if not os.path.exists(os.path.join(self.build_lib, "gpg")):
Packit d7e8d0
            os.makedirs(os.path.join(self.build_lib, "gpg"))
Packit d7e8d0
        shutil.copy2("version.py", os.path.join(self.build_lib, "gpg"))
Packit d7e8d0
Packit d7e8d0
    def run(self):
Packit d7e8d0
        self._generate()
Packit d7e8d0
Packit d7e8d0
        swig_sources.extend((self._in_build_base('gpgme.i'), self._in_build_base('helpers.c')))
Packit d7e8d0
        swig_opts.extend(['-I' + self.build_base,
Packit d7e8d0
                          '-outdir', os.path.join(self.build_lib, 'gpg')])
Packit d7e8d0
        include_dirs.insert(0, self.build_base)
Packit d7e8d0
Packit d7e8d0
        self.run_command('build_ext')
Packit d7e8d0
        build.run(self)
Packit d7e8d0
Packit d7e8d0
py3 = [] if sys.version_info.major < 3 else ['-py3']
Packit d7e8d0
swig_sources = []
Packit d7e8d0
swig_opts = ['-threads'] + py3 + extra_swig_opts
Packit d7e8d0
swige = Extension("gpg._gpgme",
Packit d7e8d0
                  sources = swig_sources,
Packit d7e8d0
                  swig_opts = swig_opts,
Packit d7e8d0
                  include_dirs = include_dirs,
Packit d7e8d0
                  define_macros = define_macros,
Packit d7e8d0
                  library_dirs = library_dirs,
Packit d7e8d0
                  extra_link_args = libs)
Packit d7e8d0
Packit d7e8d0
setup(name="gpg",
Packit d7e8d0
      cmdclass={'build': BuildExtFirstHack},
Packit d7e8d0
      version="@VERSION@",
Packit d7e8d0
      description='Python bindings for GPGME GnuPG cryptography library',
Packit d7e8d0
      # XXX add a long description
Packit d7e8d0
      #long_description=long_description,
Packit d7e8d0
      author='The GnuPG hackers',
Packit d7e8d0
      author_email='gnupg-devel@gnupg.org',
Packit d7e8d0
      url='https://www.gnupg.org',
Packit d7e8d0
      ext_modules=[swige],
Packit d7e8d0
      packages = ['gpg', 'gpg.constants', 'gpg.constants.data',
Packit d7e8d0
                  'gpg.constants.keylist', 'gpg.constants.sig',
Packit d7e8d0
                  'gpg.constants.tofu'],
Packit d7e8d0
      license="LGPL2.1+ (the library), GPL2+ (tests and examples)",
Packit d7e8d0
      classifiers=[
Packit d7e8d0
          'Development Status :: 4 - Beta',
Packit d7e8d0
          'Intended Audience :: Developers',
Packit d7e8d0
          'License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)',
Packit d7e8d0
          'Programming Language :: Python :: 2',
Packit d7e8d0
          'Programming Language :: Python :: 2.7',
Packit d7e8d0
          'Programming Language :: Python :: 3',
Packit d7e8d0
          'Programming Language :: Python :: 3.4',
Packit d7e8d0
          'Programming Language :: Python :: 3.5',
Packit d7e8d0
          'Programming Language :: Python :: 3.6',
Packit d7e8d0
          'Operating System :: POSIX',
Packit d7e8d0
          'Operating System :: Microsoft :: Windows',
Packit d7e8d0
          'Topic :: Communications :: Email',
Packit d7e8d0
          'Topic :: Security :: Cryptography',
Packit d7e8d0
      ],
Packit d7e8d0
)