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 | |
---|
8 | import os, platform, re, sys |
---|
9 | |
---|
10 | from setuptools import Extension, setup |
---|
11 | from setuptools import Command |
---|
12 | from distutils.util import get_platform |
---|
13 | from setuptools.command.test import ScanningLoader |
---|
14 | import unittest |
---|
15 | |
---|
16 | PKG='pycryptopp' |
---|
17 | VERSION_PY_FNAME = os.path.join('src', PKG, '_version.py') |
---|
18 | |
---|
19 | import versioneer |
---|
20 | |
---|
21 | # ECDSA=False |
---|
22 | ECDSA=True |
---|
23 | |
---|
24 | DEBUG=False |
---|
25 | if "--debug" in sys.argv: |
---|
26 | DEBUG=True |
---|
27 | sys.argv.remove("--debug") |
---|
28 | |
---|
29 | DISABLE_EMBEDDED_CRYPTOPP=False |
---|
30 | if "--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. |
---|
38 | if os.environ.get('PYCRYPTOPP_DISABLE_EMBEDDED_CRYPTOPP') == "1": |
---|
39 | DISABLE_EMBEDDED_CRYPTOPP=True |
---|
40 | |
---|
41 | EMBEDDED_CRYPTOPP_DIR='src-cryptopp' |
---|
42 | |
---|
43 | BUILD_DOUBLE_LOAD_TESTER=False |
---|
44 | BDLTARG="--build-double-load-tester" |
---|
45 | if 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 | |
---|
53 | extra_compile_args=[] |
---|
54 | extra_link_args=[] |
---|
55 | define_macros=[] |
---|
56 | undef_macros=[] |
---|
57 | libraries=[] |
---|
58 | ext_modules=[] |
---|
59 | include_dirs=[] |
---|
60 | library_dirs=[] |
---|
61 | extra_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 | # |
---|
67 | if 'openbsd' in platform.system().lower(): |
---|
68 | extra_link_args.append("-fpic") |
---|
69 | |
---|
70 | if 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') |
---|
76 | else: |
---|
77 | extra_compile_args.append("-w") |
---|
78 | |
---|
79 | if 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 | |
---|
106 | else: |
---|
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 | |
---|
126 | trove_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 | |
---|
144 | srcs = ['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 | ] |
---|
150 | if ECDSA: |
---|
151 | srcs.append('src/pycryptopp/publickey/ecdsamodule.cpp') |
---|
152 | if BUILD_DOUBLE_LOAD_TESTER: |
---|
153 | srcs.append('_doubleloadtester.cpp', ) |
---|
154 | |
---|
155 | ext_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 |
---|
160 | sources = [os.path.join("src-ed25519","glue","ed25519module.c")] |
---|
161 | sources.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"]) |
---|
164 | m = Extension("pycryptopp.publickey.ed25519._ed25519", |
---|
165 | include_dirs=[os.path.join("src-ed25519","supercop-ref")], |
---|
166 | sources=sources) |
---|
167 | ext_modules.append(m) |
---|
168 | |
---|
169 | |
---|
170 | if 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 | |
---|
175 | miscdeps=os.path.join(os.getcwd(), 'misc', 'dependencies') |
---|
176 | dependency_links=[os.path.join(miscdeps, t) for t in os.listdir(miscdeps) if t.endswith(".tar")] |
---|
177 | setup_requires = [] |
---|
178 | install_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. |
---|
182 | if '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 |
---|
187 | if "sdist_dsc" in sys.argv: |
---|
188 | setup_requires.append('stdeb') |
---|
189 | |
---|
190 | data_fnames=['COPYING.GPL', 'COPYING.TGPPL.rst', 'COPYING.MIT.txt', 'COPYING.SPL.txt', 'README.rst'] |
---|
191 | |
---|
192 | readmetext = open('README.rst').read() |
---|
193 | if readmetext[:3] == '\xef\xbb\xbf': |
---|
194 | # utf-8 "BOM" |
---|
195 | readmetext = readmetext[3:] |
---|
196 | |
---|
197 | try: |
---|
198 | readmetext = readmetext.decode('utf-8') |
---|
199 | except 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". |
---|
204 | doc_loc = "share/doc/" + PKG |
---|
205 | data_files = [(doc_loc, data_fnames)] |
---|
206 | |
---|
207 | commands = {} |
---|
208 | |
---|
209 | ###### Version updating code |
---|
210 | |
---|
211 | CPP_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 | |
---|
221 | def 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 | |
---|
262 | def 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 | |
---|
273 | EXTRAVERSION_H_FNAME = os.path.join(EMBEDDED_CRYPTOPP_DIR, 'extraversion.h') |
---|
274 | |
---|
275 | VERSION_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 | |
---|
285 | class 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 | |
---|
348 | version = UpdateVersion().run() |
---|
349 | |
---|
350 | class 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()) |
---|
371 | commands["test"] = Test |
---|
372 | |
---|
373 | class 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 |
---|
390 | commands["bench"] = Bench |
---|
391 | |
---|
392 | def _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 | |
---|
424 | try: |
---|
425 | _setup(readmetext) |
---|
426 | except UnicodeEncodeError: |
---|
427 | _setup(repr(readmetext)) |
---|