| #! /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) |