diff options
Diffstat (limited to 'setup.py')
-rw-r--r-- | setup.py | 527 |
1 files changed, 527 insertions, 0 deletions
diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..5269e9d --- /dev/null +++ b/setup.py @@ -0,0 +1,527 @@ +#! /usr/bin/env python +# +# setup.py : Distutils setup script +# +# Part of the Python Cryptography Toolkit +# +# =================================================================== +# Portions Copyright (c) 2001, 2002, 2003 Python Software Foundation; +# All Rights Reserved +# +# This file contains code from the Python 2.2 setup.py module (the +# "Original Code"), with modifications made after it was incorporated +# into PyCrypto (the "Modifications"). +# +# To the best of our knowledge, the Python Software Foundation is the +# copyright holder of the Original Code, and has licensed it under the +# Python 2.2 license. See the file LEGAL/copy/LICENSE.python-2.2 for +# details. +# +# The Modifications to this file are dedicated to the public domain. +# To the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. No rights are +# reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +__revision__ = "$Id$" + +from distutils import core +from distutils.ccompiler import new_compiler +from distutils.core import Extension, Command +from distutils.command.build import build +from distutils.command.build_ext import build_ext +import distutils.sysconfig +import os, sys, re +import struct + +if sys.version[0:1] == '1': + raise RuntimeError ("The Python Cryptography Toolkit requires " + "Python 2.x or 3.x to build.") + +if sys.platform == 'win32': + HTONS_LIBS = ['ws2_32'] + plat_ext = [ + Extension("Crypto.Random.OSRNG.winrandom", + libraries = HTONS_LIBS + ['advapi32'], + include_dirs=['src/'], + sources=["src/winrand.c"]) + ] +else: + HTONS_LIBS = [] + plat_ext = [] + +# For test development: Set this to 1 to build with gcov support. +# Use "gcov -p -o build/temp.*/src build/temp.*/src/*.gcda" to build the +# .gcov files +USE_GCOV = 0 + + +try: + # Python 3 + from distutils.command.build_py import build_py_2to3 as build_py +except ImportError: + # Python 2 + from distutils.command.build_py import build_py + +# List of pure Python modules that will be excluded from the binary packages. +# The list consists of (package, module_name) tuples +if sys.version_info[0] == 2: + EXCLUDE_PY = [] +else: + EXCLUDE_PY = [ + # We don't want Py3k to choke on the 2.x compat code + ('Crypto.Util', 'py21compat'), + ] + if sys.platform != "win32": # Avoid nt.py, as 2to3 can't fix it w/o winrandom + EXCLUDE_PY += [('Crypto.Random.OSRNG','nt')] + +# Work around the print / print() issue with Python 2.x and 3.x. We only need +# to print at one point of the code, which makes this easy + +def PrintErr(*args, **kwd): + fout = kwd.get("file", sys.stderr) + w = fout.write + if args: + w(str(args[0])) + sep = kwd.get("sep", " ") + for a in args[1:]: + w(sep) + w(str(a)) + w(kwd.get("end", "\n")) + +def endianness_macro(): + s = struct.pack("@I", 0x33221100) + if s == "\x00\x11\x22\x33".encode(): # little endian + return ('PCT_LITTLE_ENDIAN', 1) + elif s == "\x33\x22\x11\x00".encode(): # big endian + return ('PCT_BIG_ENDIAN', 1) + raise AssertionError("Machine is neither little-endian nor big-endian") + +class PCTBuildExt (build_ext): + def build_extensions(self): + # Detect which modules should be compiled + self.detect_modules() + + # Tweak compiler options + if self.compiler.compiler_type in ('unix', 'cygwin', 'mingw32'): + # Make assert() statements always work + self.__remove_compiler_option("-DNDEBUG") + + if USE_GCOV: # TODO - move this to configure.ac + self.__add_compiler_option("-fprofile-arcs") + self.__add_compiler_option("-ftest-coverage") + self.compiler.libraries += ['gcov'] + + # Python 2.1 and 2.2 don't respect the LDFLAGS environment variable. Hack it. + if sys.version_info < (2, 3, 'final', 0): + if os.environ.get('LDFLAGS'): # Set from ./buildenv (ultimately provided by autoconf) + for opt in os.environ['LDFLAGS'].split(" "): + opt = opt.strip() + if not opt: continue + self.compiler.linker_so.append(opt) + + # Call the superclass's build_extensions method + build_ext.build_extensions(self) + + def detect_modules (self): + # Read the config.h file (usually generated by autoconf) + if self.compiler.compiler_type == 'msvc': + # Add special include directory for MSVC (because MSVC is special) + self.compiler.include_dirs.insert(0, "src/inc-msvc/") + ac = self.__read_autoconf("src/inc-msvc/config.h") + else: + ac = self.__read_autoconf("src/config.h") + + # Detect libgmp or libmpir and don't build _fastmath if both are missing. + if ac.get("HAVE_LIBGMP"): + # Default; no changes needed + pass + elif ac.get("HAVE_LIBMPIR"): + # Change library to libmpir if libgmp is missing + self.__change_extension_lib(["Crypto.PublicKey._fastmath"], + ['mpir']) + # And if this is MSVC, we need to add a linker option + # to make a static libmpir link well into a dynamic _fastmath + if self.compiler.compiler_type == 'msvc': + self.__add_extension_link_option(["Crypto.PublicKey._fastmath"], + ["/NODEFAULTLIB:LIBCMT"]) + else: + # No MP library; use _slowmath. + PrintErr ("warning: GMP or MPIR library not found; Not building "+ + "Crypto.PublicKey._fastmath.") + self.__remove_extensions(["Crypto.PublicKey._fastmath"]) + + # Detect if we have AES-NI instrincs available + if not ac.get("HAVE_WMMINTRIN_H"): + # AES-NI instrincs not available + self.__remove_extensions(["Crypto.Cipher._AESNI"]) + elif not (ac.get("HAVE_POSIX_MEMALIGN") or ac.get("HAVE_ALIGNED_ALLOC") + or ac.get("HAVE__ALIGNED_MALLOC")): + # no function to allocate aligned memory is available + self.__remove_extensions(["Crypto.Cipher._AESNI"]) + elif ac.get("HAVE_MAES"): + # -maes has to be passed to the compiler to use the AES-NI instrincs + self.__add_extension_compile_option(["Crypto.Cipher._AESNI"], + ["-maes"]) + + def __add_extension_compile_option(self, names, options): + """Add compiler options for the specified extension(s)""" + for extension in self.extensions: + if extension.name in names: + extension.extra_compile_args = options + + def __add_extension_link_option(self, names, options): + """Add linker options for the specified extension(s)""" + i = 0 + while i < len(self.extensions): + if self.extensions[i].name in names: + self.extensions[i].extra_link_args = options + i += 1 + + def __change_extension_lib(self, names, libs): + """Change the libraries to be used for the specified extension(s)""" + i = 0 + while i < len(self.extensions): + if self.extensions[i].name in names: + self.extensions[i].libraries = libs + i += 1 + + def __remove_extensions(self, names): + """Remove the specified extension(s) from the list of extensions + to build""" + i = 0 + while i < len(self.extensions): + if self.extensions[i].name in names: + del self.extensions[i] + continue + i += 1 + + def __remove_compiler_option(self, option): + """Remove the specified compiler option. + + Return true if the option was found. Return false otherwise. + """ + found = 0 + for attrname in ('compiler', 'compiler_so'): + compiler = getattr(self.compiler, attrname, None) + if compiler is not None: + while option in compiler: + compiler.remove(option) + found += 1 + return found + + def __add_compiler_option(self, option): + for attrname in ('compiler', 'compiler_so'): + compiler = getattr(self.compiler, attrname, None) + if compiler is not None: + compiler.append(option) + + def __read_autoconf(self, filename): + rx_define = re.compile(r"""^#define (\S+) (?:(\d+)|(".*"))$""") + + result = {} + f = open(filename, "r") + try: + config_lines = f.read().replace("\r\n", "\n").split("\n") + for line in config_lines: + m = rx_define.search(line) + if not m: continue + sym = m.group(1) + n = m.group(2) + s = m.group(3) + if n: + result[sym] = int(n) + elif s: + result[sym] = eval(s) # XXX - hack to unescape C-style string + else: + continue + finally: + f.close() + return result + + def run(self): + # Run the commands that this one depends on (i.e. build_configure) + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + class unmodified: pass # sentinel value + orig_cc = unmodified + try: + # Set environment variables generated by the configure script + if os.path.exists("buildenv"): + try: + f = open("buildenv", "r") + for line in f.readlines(): + if line.startswith("#") or not line.strip(): + continue + k, v = line.split("=", 1) + k, v = k.strip(), v.strip() + os.environ[k] = v + finally: + f.close() + + # Python 2.1 and 2.2 don't respect the CC environment variable by default. Monkey-patch it. + if sys.version_info < (2, 3, 'final', 0) and os.environ.get('CC'): + distutils.sysconfig.get_config_vars() # populates distutils.sysconfig._config_vars + orig_cc = distutils.sysconfig._config_vars['CC'] + distutils.sysconfig._config_vars['CC'] = os.environ['CC'] + + # Build the extension modules + build_ext.run(self) + + finally: + if orig_cc is not unmodified: + # Undo monkey-patch + distutils.sysconfig._config_vars['CC'] = orig_cc + + + def has_configure(self): + compiler = new_compiler(compiler=self.compiler) + return compiler.compiler_type != 'msvc' + + sub_commands = [ ('build_configure', has_configure) ] + build_ext.sub_commands + +class PCTBuildConfigure(Command): + description = "Generate config.h using ./configure (autoconf)" + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + if not os.path.exists("config.status"): + if hasattr(os, "chmod"): + import stat + os.chmod("configure", stat.S_IRUSR | stat.S_IWUSR | + stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | + stat.S_IROTH | stat.S_IXOTH) + cmd = "sh configure" # we use "sh" here so that it'll work on mingw32 with standard python.org binaries + if self.verbose < 1: + cmd += " -q" + if os.system(cmd) != 0: + raise RuntimeError("autoconf error") + +class PCTBuildPy(build_py): + def find_package_modules(self, package, package_dir, *args, **kwargs): + modules = build_py.find_package_modules(self, package, package_dir, + *args, **kwargs) + + # Exclude certain modules + retval = [] + for item in modules: + pkg, module = item[:2] + if (pkg, module) in EXCLUDE_PY: + continue + retval.append(item) + return retval + + +class TestCommand(Command): + + description = "Run self-test" + + # Long option name, short option name, description + user_options = [ + ('skip-slow-tests', None, + 'Skip slow tests'), + ('module=', 'm', 'Test a single module (e.g. Cipher, PublicKey)') + ] + + def initialize_options(self): + self.build_dir = None + self.skip_slow_tests = None + self.module = None + + def finalize_options(self): + self.set_undefined_options('install', ('build_lib', 'build_dir')) + self.config = {'slow_tests': not self.skip_slow_tests} + + def run(self): + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + # Run SelfTest + self.announce("running self-tests") + old_path = sys.path[:] + try: + sys.path.insert(0, self.build_dir) + from Crypto import SelfTest + moduleObj = None + if self.module: + if self.module.count('.')==0: + # Test a whole a sub-package + full_module = "Crypto.SelfTest." + self.module + module_name = self.module + else: + # Test only a module + # Assume only one dot is present + comps = self.module.split('.') + module_name = "test_" + comps[1] + full_module = "Crypto.SelfTest." + comps[0] + "." + module_name + # Import sub-package or module + moduleObj = __import__( full_module, globals(), locals(), module_name ) + SelfTest.run(module=moduleObj, verbosity=self.verbose, stream=sys.stdout, config=self.config) + finally: + # Restore sys.path + sys.path[:] = old_path + + # Run slower self-tests + self.announce("running extended self-tests") + + sub_commands = [ ('build', None) ] + +kw = {'name':"pycrypto", + 'version':"2.7a1", # See also: lib/Crypto/__init__.py + 'description':"Cryptographic modules for Python.", + 'author':"Dwayne C. Litzenberger", + 'author_email':"dlitz@dlitz.net", + 'url':"http://www.pycrypto.org/", + + 'cmdclass' : {'build_configure': PCTBuildConfigure, 'build_ext': PCTBuildExt, 'build_py': PCTBuildPy, 'test': TestCommand }, + 'packages' : ["Crypto", "Crypto.Hash", "Crypto.Cipher", "Crypto.Util", + "Crypto.Random", + "Crypto.Random.Fortuna", + "Crypto.Random.OSRNG", + "Crypto.SelfTest", + "Crypto.SelfTest.Cipher", + "Crypto.SelfTest.Hash", + "Crypto.SelfTest.Protocol", + "Crypto.SelfTest.PublicKey", + "Crypto.SelfTest.Random", + "Crypto.SelfTest.Random.Fortuna", + "Crypto.SelfTest.Random.OSRNG", + "Crypto.SelfTest.Util", + "Crypto.SelfTest.Signature", + "Crypto.SelfTest.IO", + "Crypto.Protocol", + "Crypto.PublicKey", + "Crypto.Signature", + "Crypto.IO"], + 'package_dir' : { "Crypto": "lib/Crypto" }, + 'ext_modules': plat_ext + [ + # _fastmath (uses GNU mp library) + Extension("Crypto.PublicKey._fastmath", + include_dirs=['src/'], + libraries=['gmp'], + sources=["src/_fastmath.c"]), + + # Hash functions + Extension("Crypto.Hash.MD2", + include_dirs=['src/'], + sources=["src/MD2.c"]), + Extension("Crypto.Hash.MD4", + include_dirs=['src/'], + sources=["src/MD4.c"]), + Extension("Crypto.Hash.SHA256", + include_dirs=['src/'], + sources=["src/SHA256.c"]), + Extension("Crypto.Hash.SHA224", + include_dirs=['src/'], + sources=["src/SHA224.c"]), + Extension("Crypto.Hash.SHA384", + include_dirs=['src/'], + sources=["src/SHA384.c"]), + Extension("Crypto.Hash.SHA512", + include_dirs=['src/'], + sources=["src/SHA512.c"]), + Extension("Crypto.Hash.RIPEMD160", + include_dirs=['src/'], + sources=["src/RIPEMD160.c"], + define_macros=[endianness_macro()]), + + # Block encryption algorithms + Extension("Crypto.Cipher._AES", + include_dirs=['src/'], + sources=["src/AES.c"]), + Extension("Crypto.Cipher._AESNI", + include_dirs=['src/'], + sources=["src/AESNI.c"]), + Extension("Crypto.Cipher._ARC2", + include_dirs=['src/'], + sources=["src/ARC2.c"]), + Extension("Crypto.Cipher._Blowfish", + include_dirs=['src/'], + sources=["src/Blowfish.c"]), + Extension("Crypto.Cipher._CAST", + include_dirs=['src/'], + sources=["src/CAST.c"]), + Extension("Crypto.Cipher._DES", + include_dirs=['src/', 'src/libtom/'], + sources=["src/DES.c"]), + Extension("Crypto.Cipher._DES3", + include_dirs=['src/', 'src/libtom/'], + sources=["src/DES3.c"]), + + # Stream ciphers + Extension("Crypto.Cipher._ARC4", + include_dirs=['src/'], + sources=["src/ARC4.c"]), + Extension("Crypto.Cipher._XOR", + include_dirs=['src/'], + sources=["src/XOR.c"]), + + # Utility modules + Extension("Crypto.Util.strxor", + include_dirs=['src/'], + sources=['src/strxor.c']), + Extension("Crypto.Util.cpuid", + include_dirs=['src/'], + sources=['src/cpuid.c']), + Extension("Crypto.Util.galois", + include_dirs=['src/'], + sources=['src/galois.c']), + + # Counter modules + Extension("Crypto.Util._counter", + include_dirs=['src/'], + sources=['src/_counter.c']), + ] +} + +# If we're running Python 2.3, add extra information +if hasattr(core, 'setup_keywords'): + if 'classifiers' in core.setup_keywords: + kw['classifiers'] = [ + 'Development Status :: 5 - Production/Stable', + 'License :: Public Domain', + 'Intended Audience :: Developers', + 'Operating System :: Unix', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: MacOS :: MacOS X', + 'Topic :: Security :: Cryptography', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 3', + ] + +core.setup(**kw) + +def touch(path): + import os, time + now = time.time() + try: + # assume it's there + os.utime(path, (now, now)) + except os.error: + PrintErr("Failed to update timestamp of "+path) + +# PY3K: Workaround for winrandom.pyd not existing during the first pass. +# It needs to be there for 2to3 to fix the import in nt.py +if (sys.platform == 'win32' and sys.version_info[0] == 3 and + 'build' in sys.argv[1:]): + PrintErr("\nSecond pass to allow 2to3 to fix nt.py. No cause for alarm.\n") + touch("./lib/Crypto/Random/OSRNG/nt.py") + core.setup(**kw) |