source: trunk/setup.py

Last change on this file was 0cf36ab, checked in by Jean-Paul Calderone <exarkun@…>, at 2017-02-24T16:56:22Z

Don't put the git sha1 in the version number.

This changes the version format.

It was:

x.y.z.<git sha1>.post<commits since tag>

Now it is:

x.y.z.post<commits since tag>

The git sha1 goes up and down randomly.
The old scheme therefore produced version number sequences which were
not monotonic.

  • Property mode set to 100644
File size: 14.5 KB
Line 
1# -*- coding: utf-8 -*-
2
3# Copyright © 2009-2013 Zooko Wilcox-O'Hearn
4# Author: Zooko Wilcox-O'Hearn
5#
6# See README.rst for licensing information.
7
8import os, platform, re, sys
9
10from setuptools import Extension, setup
11from setuptools import Command
12from distutils.util import get_platform
13from setuptools.command.test import ScanningLoader
14import unittest
15
16PKG='pycryptopp'
17VERSION_PY_FNAME = os.path.join('src', PKG, '_version.py')
18
19import versioneer
20
21# ECDSA=False
22ECDSA=True
23
24DEBUG=False
25if "--debug" in sys.argv:
26    DEBUG=True
27    sys.argv.remove("--debug")
28
29DISABLE_EMBEDDED_CRYPTOPP=False
30if "--disable-embedded-cryptopp" in sys.argv:
31    DISABLE_EMBEDDED_CRYPTOPP=True
32    sys.argv.remove("--disable-embedded-cryptopp")
33
34# Unfortunately stdeb v0.3 doesn't seem to offer a way to pass command-line
35# arguments to setup.py when building for Debian, but it does offer a way to
36# pass environment variables, so we here check for that in addition to the
37# command-line argument check above.
38if os.environ.get('PYCRYPTOPP_DISABLE_EMBEDDED_CRYPTOPP') == "1":
39    DISABLE_EMBEDDED_CRYPTOPP=True
40
41EMBEDDED_CRYPTOPP_DIR='src-cryptopp'
42
43BUILD_DOUBLE_LOAD_TESTER=False
44BDLTARG="--build-double-load-tester"
45if BDLTARG in sys.argv:
46    BUILD_DOUBLE_LOAD_TESTER=True
47    sys.argv.remove(BDLTARG)
48
49# There are two ways that this setup.py script can build pycryptopp, either by using the
50# Crypto++ source code bundled in the pycryptopp source tree, or by linking to a copy of the
51# Crypto++ library that is already installed on the system.
52
53extra_compile_args=[]
54extra_link_args=[]
55define_macros=[]
56undef_macros=[]
57libraries=[]
58ext_modules=[]
59include_dirs=[]
60library_dirs=[]
61extra_srcs=[] # This is for Crypto++ .cpp files if they are needed.
62
63#
64# Fix the build on OpenBSD
65# https://tahoe-lafs/trac/pycryptopp/ticket/32
66#
67if 'openbsd' in platform.system().lower():
68    extra_link_args.append("-fpic")
69
70if DEBUG:
71    extra_compile_args.append("-O0")
72    extra_compile_args.append("-g")
73    extra_compile_args.append("-Wall")
74    extra_link_args.append("-g")
75    undef_macros.append('NDEBUG')
76else:
77    extra_compile_args.append("-w")
78
79if DISABLE_EMBEDDED_CRYPTOPP:
80    define_macros.append(('DISABLE_EMBEDDED_CRYPTOPP', 1))
81
82    # Link with a Crypto++ library that is already installed on the system.
83
84    for inclpath in ["/usr/local/include/cryptopp", "/usr/include/cryptopp"]:
85        if os.path.exists(inclpath):
86            libraries.append("cryptopp")
87            incldir = os.path.dirname(inclpath)
88            include_dirs.append(incldir)
89            libdir = os.path.join(os.path.dirname(incldir), "lib")
90            library_dirs.append(libdir)
91            break
92
93    if not libraries:
94        print "Did not locate libcryptopp in the usual places."
95        print "Adding /usr/local/{include,lib} and -lcryptopp in the hopes"
96        print "that they will work."
97
98        # Note that when using cygwin build tools (including gcc) to build
99        # Windows-native binaries, the os.path.exists() will not see the
100        # /usr/local/include/cryptopp directory but the subsequent call to g++
101        # will.
102        libraries.append("cryptopp")
103        include_dirs.append("/usr/local/include")
104        library_dirs.append("/usr/local/lib")
105
106else:
107    # Build the bundled Crypto++ library which is included by source
108    # code in the pycryptopp tree and link against it.
109    define_macros.append(('CRYPTOPP_DISABLE_ASM', 1))
110
111    include_dirs.append(".")
112
113    if 'sunos' in platform.system().lower():
114        extra_compile_args.append('-Wa,--divide') # allow use of "/" operator
115
116    cryptopp_src = [ os.path.join(EMBEDDED_CRYPTOPP_DIR, x) for x in os.listdir(EMBEDDED_CRYPTOPP_DIR) if x.endswith('.cpp') ]
117
118    # Mac OS X extended attribute files when written to a non-Mac-OS-X
119    # filesystem come out as "._$FNAME", for example "._rdtables.cpp",
120    # and those files contain uncompilable data that is not C++, thus
121    # on occasion causing the build to fail. This works-around that:
122    cryptopp_src = [ c for c in cryptopp_src if not os.path.basename(c).startswith('._') ]
123
124    extra_srcs.extend(cryptopp_src)
125
126trove_classifiers=[
127    "Environment :: Console",
128    "License :: OSI Approved :: GNU General Public License (GPL)", # See README.rst for alternative licensing.
129    "License :: DFSG approved",
130    "Intended Audience :: Developers",
131    "Operating System :: Microsoft :: Windows",
132    "Operating System :: Unix",
133    "Operating System :: MacOS :: MacOS X",
134    "Natural Language :: English",
135    "Programming Language :: C",
136    "Programming Language :: C++",
137    "Programming Language :: Python",
138    "Programming Language :: Python :: 2",
139    "Programming Language :: Python :: 2.6",
140    "Programming Language :: Python :: 2.7",
141    "Topic :: Software Development :: Libraries",
142    ]
143
144srcs = ['src/pycryptopp/_pycryptoppmodule.cpp',
145        'src/pycryptopp/publickey/rsamodule.cpp',
146        'src/pycryptopp/hash/sha256module.cpp',
147        'src/pycryptopp/cipher/aesmodule.cpp',
148        'src/pycryptopp/cipher/xsalsa20module.cpp',
149        ]
150if ECDSA:
151    srcs.append('src/pycryptopp/publickey/ecdsamodule.cpp')
152if BUILD_DOUBLE_LOAD_TESTER:
153    srcs.append('_doubleloadtester.cpp', )
154
155ext_modules.append(
156    Extension('pycryptopp._pycryptopp', extra_srcs + srcs, include_dirs=include_dirs, library_dirs=library_dirs, libraries=libraries, extra_link_args=extra_link_args, extra_compile_args=extra_compile_args, define_macros=define_macros, undef_macros=undef_macros)
157    )
158
159# python-ed25519
160sources = [os.path.join("src-ed25519","glue","ed25519module.c")]
161sources.extend([os.path.join("src-ed25519","supercop-ref",s)
162                for s in os.listdir(os.path.join("src-ed25519","supercop-ref"))
163                if s.endswith(".c") and s!="test.c"])
164m = Extension("pycryptopp.publickey.ed25519._ed25519",
165              include_dirs=[os.path.join("src-ed25519","supercop-ref")],
166              sources=sources)
167ext_modules.append(m)
168
169
170if BUILD_DOUBLE_LOAD_TESTER:
171    ext_modules.append(
172        Extension('_doubleloadtester', extra_srcs + srcs, include_dirs=include_dirs, library_dirs=library_dirs, libraries=libraries, extra_link_args=extra_link_args, extra_compile_args=extra_compile_args, define_macros=define_macros, undef_macros=undef_macros)
173        )
174
175miscdeps=os.path.join(os.getcwd(), 'misc', 'dependencies')
176dependency_links=[os.path.join(miscdeps, t) for t in os.listdir(miscdeps) if t.endswith(".tar")]
177setup_requires = []
178install_requires = ['setuptools >= 0.6a9'] # for pkg_resources for loading test vectors for unit tests
179
180# setuptools_pyflakes is needed only if you want "./setup.py flakes" to run
181# pyflakes on all the pycryptopp modules.
182if 'flakes' in sys.argv[1:]:
183    setup_requires.append('setuptools_pyflakes >= 1.0.0')
184
185# stdeb is required to produce Debian files with "sdist_dsc".
186# http://github.com/astraw/stdeb/tree/master
187if "sdist_dsc" in sys.argv:
188    setup_requires.append('stdeb')
189
190data_fnames=['COPYING.GPL', 'COPYING.TGPPL.rst', 'COPYING.MIT.txt', 'COPYING.SPL.txt', 'README.rst']
191
192readmetext = open('README.rst').read()
193if readmetext[:3] == '\xef\xbb\xbf':
194    # utf-8 "BOM"
195    readmetext = readmetext[3:]
196
197try:
198    readmetext = readmetext.decode('utf-8')
199except UnicodeDecodeError:
200    pass
201
202# In case we are building for a .deb with stdeb's sdist_dsc command, we put the
203# docs in "share/doc/pycryptopp".
204doc_loc = "share/doc/" + PKG
205data_files = [(doc_loc, data_fnames)]
206
207commands = {}
208
209###### Version updating code
210
211CPP_GIT_VERSION_BODY = '''
212/* This _version.py is generated from git metadata by the pycryptopp
213 * setup.py. The main version number is taken from the most recent release
214 * tag. If some patches have been added since the last release, this will
215 * have a -NN "build number" suffix, or else a -rNN "revision number" suffix.
216 */
217
218#define CRYPTOPP_EXTRA_VERSION "%(pkgname)s-%(pkgversion)s"
219'''
220
221def get_normalized_version(versions):
222    pieces = versions['version'].split("-")
223
224# examples: versions:  {'version': '2.3.4-dirty', 'full': '5ebdca46cf83a185710ecb9b29d46ec8ac70de61-dirty'}
225# examples versions:  {'version': '0.5.29-108-g5ebdca4-dirty', 'full': '5ebdca46cf83a185710ecb9b29d46ec8ac70de61-dirty'}
226# examples: pieces: ['0.5.29', '108', 'g5ebdca4', 'dirty']
227# examples: pieces: ['2.3.4', 'dirty']
228# examples: pieces: ['2.3.4']
229   
230    normalized_version = []
231    normalized_version.append(pieces.pop(0))
232
233    postrelease = None
234    dirty = False
235
236    while len(pieces) > 0:
237        nextpiece = pieces.pop(0)
238        if re.match('\d+$', nextpiece):
239            postrelease = nextpiece
240        elif nextpiece.startswith('g'):
241            continue
242            # Use the full version instead ,below
243        elif nextpiece == 'dirty':
244            dirty = True
245
246    dev = False
247    if dirty:
248        dev = True
249        if postrelease is None:
250            postrelease = "0"
251        else:
252            postrelease = str(int(postrelease) + 1)
253
254    if postrelease is not None:
255        normalized_version.append('.post'+postrelease)
256
257    if dev:
258        normalized_version.append('.dev0')
259
260    return ''.join(normalized_version)
261
262def read_version_py(infname):
263    try:
264        verstrline = open(infname, "rt").read()
265    except EnvironmentError:
266        return None
267    else:
268        VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
269        mo = re.search(VSRE, verstrline, re.M)
270        if mo:
271            return mo.group(1)
272
273EXTRAVERSION_H_FNAME = os.path.join(EMBEDDED_CRYPTOPP_DIR, 'extraversion.h')
274
275VERSION_BODY = '''
276# This is the version of this tree, as created by %(versiontool)s from the
277# git information: the main version number is taken from the most recent
278# release tag. If some patches have been added since the last release, this
279# will have a -NN "build number" suffix, followed by -gXXX "revid" suffix.
280
281__pkgname__ = "%(pkgname)s"
282__version__ = "%(pkgversion)s"
283'''
284
285class UpdateVersion(object):
286    def run(self):
287
288        versions = versioneer.versions_from_vcs(PKG+'-', '.')
289        assert isinstance(versions, dict)
290
291        vers_f_file = read_version_py(VERSION_PY_FNAME)
292
293        if not versions and vers_f_file is None:
294            raise Exception("problem: couldn't get version information from revision control history, and there is no version information in '%s'. Stopping." % (VERSION_PY_FNAME,))
295
296        if versions:
297            version = get_normalized_version(versions)
298        else:
299            version = vers_f_file
300
301        # Let's avoid touching the change time (ctime) on the files unless
302        # they actually need to be updated.
303
304        if self.read_extraversion_h(EXTRAVERSION_H_FNAME) != version:
305            self.write_extraversion_h(
306                PKG,
307                version,
308                EXTRAVERSION_H_FNAME,
309                CPP_GIT_VERSION_BODY
310                )
311
312        if read_version_py(VERSION_PY_FNAME) != version:
313            self.write_version_py(
314                PKG,
315                version,
316                VERSION_PY_FNAME,
317                VERSION_BODY,
318                "pycryptopp's setup.py"
319                )
320
321        return version
322
323    def write_version_py(self, pkgname, version, outfname, body, EXE_NAME):
324        f = open(outfname, "wb+")
325        f.write(body % {
326                'versiontool': EXE_NAME,
327                'pkgversion': version,
328                'pkgname': pkgname,
329                })
330        f.close()
331
332    def write_extraversion_h(self, pkgname, version, outfname, body):
333        f = open(outfname, "wb")
334        f.write(body % {"pkgname": pkgname, "pkgversion": version})
335        f.close()
336
337    def read_extraversion_h(self, infname):
338        try:
339            verstrline = open(infname, "rt").read()
340        except EnvironmentError:
341            return None
342        else:
343            VSRE = r"^#define CRYPTOPP_EXTRA_VERSION +\"([^\"]*)\""
344            mo = re.search(VSRE, verstrline, re.M)
345            if mo:
346                return mo.group(1)
347
348version = UpdateVersion().run()
349
350class Test(Command):
351    description = "run tests"
352    user_options = []
353    def initialize_options(self):
354        self.test_suite = None
355    def finalize_options(self):
356        if self.test_suite is None:
357            self.test_suite = self.distribution.test_suite
358    def setup_path(self):
359        # copied from distutils/command/build.py
360        self.plat_name = get_platform()
361        plat_specifier = ".%s-%s" % (self.plat_name, sys.version[0:3])
362        self.build_lib = os.path.join("build", "lib"+plat_specifier)
363        sys.path.insert(0, self.build_lib)
364    def run(self):
365        self.setup_path()
366        loader = ScanningLoader()
367        test = loader.loadTestsFromName(self.test_suite)
368        runner = unittest.TextTestRunner(verbosity=2)
369        result = runner.run(test)
370        sys.exit(not result.wasSuccessful())
371commands["test"] = Test
372
373class Bench(Command):
374    description = "run benchmarks"
375    user_options = []
376    def initialize_options(self):
377        pass
378    def finalize_options(self):
379        pass
380    def setup_path(self):
381        # copied from distutils/command/build.py
382        self.plat_name = get_platform()
383        plat_specifier = ".%s-%s" % (self.plat_name, sys.version[0:3])
384        self.build_lib = os.path.join("build", "lib"+plat_specifier)
385        sys.path.insert(0, self.build_lib)
386    def run(self):
387        self.setup_path()
388        from pycryptopp.bench import bench_algs
389        bench_algs.bench(MAXTIME=1.0) # Requires pyutil ≤ 1.9.7
390commands["bench"] = Bench
391
392def _setup(longdescription):
393    setup(name=PKG,
394          version=version,
395          description='Python wrappers for a few algorithms from the Crypto++ library',
396          long_description=longdescription,
397          author='Zooko Wilcox',
398          author_email='zookog@gmail.com',
399          url='https://tahoe-lafs.org/trac/' + PKG,
400          license='GNU GPL', # see README.rst for details -- there is also an alternative licence
401          packages=["pycryptopp",
402                    "pycryptopp.cipher",
403                    "pycryptopp.hash",
404                    "pycryptopp.publickey",
405                    "pycryptopp.publickey.ed25519",
406                    "pycryptopp.test",
407                    ],
408          include_package_data=True,
409          exclude_package_data={
410              '': [ '*.cpp', '*.hpp', ]
411              },
412          data_files=data_files,
413          package_dir={"pycryptopp": "src/pycryptopp"},
414          setup_requires=setup_requires,
415          install_requires=install_requires,
416          dependency_links=dependency_links,
417          classifiers=trove_classifiers,
418          ext_modules=ext_modules,
419          test_suite=PKG+".test",
420          zip_safe=False, # I prefer unzipped for easier access.
421          cmdclass=commands,
422          )
423
424try:
425    _setup(readmetext)
426except UnicodeEncodeError:
427    _setup(repr(readmetext))
Note: See TracBrowser for help on using the repository browser.