1#!/usr/bin/env python 2 3# 4# Copyright (c) 2008-2010 Stefan Krah. All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 13# 2. Redistributions in binary form must reproduce the above copyright 14# notice, this list of conditions and the following disclaimer in the 15# documentation and/or other materials provided with the distribution. 16# 17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND 18# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27# SUCH DAMAGE. 28# 29 30 31import sys, os, platform, re 32from shutil import copy2 33from glob import glob 34from distutils import sysconfig 35from distutils.core import setup, Extension 36 37 38DESCRIPTION = """\ 39Fast arbitrary precision correctly-rounded decimal floating point arithmetic.\ 40""" 41 42LONG_DESCRIPTION = """ 43Overview 44-------- 45 46The ``cdecimal`` package is a fast drop-in replacement for the ``decimal`` module 47in Python's standard library. Both modules provide complete implementations of 48Mike Cowlishaw/IBM's ``General Decimal Arithmetic Specification``. 49 50Testing 51------- 52 53Both ``cdecimal`` and the underlying library - ``libmpdec`` - are extremely 54well tested. ``libmpdec`` is one of the few open source projects with 100% 55code coverage. ``cdecimal`` is rigorously tested against ``decimal.py``. 56 57Short benchmarks 58---------------- 59 60Typical performance gains are between 30x for I/O heavy benchmarks 61and 80x for numerical programs. In a database benchmark, cdecimal 62exhibits a speedup of 12x over decimal.py. 63 64+---------+-------------+--------------+-------------+ 65| | decimal | cdecimal | speedup | 66+=========+=============+==============+=============+ 67| pi | 42.75s | 0.58s | 74x | 68+---------+-------------+--------------+-------------+ 69| telco | 172.19s | 5.68s | 30x | 70+---------+-------------+--------------+-------------+ 71| psycopg | 3.57s | 0.29s | 12x | 72+---------+-------------+--------------+-------------+ 73 74Documentation 75------------- 76 77Since ``cdecimal`` is compatible with ``decimal.py``, the official documentation 78is valid. For the few remaining differences, refer to the second link. 79 80* `Decimal module <http://docs.python.org/dev/py3k/library/decimal.html>`_ 81* `Differences between cdecimal and decimal <http://www.bytereef.org/mpdecimal/doc/cdecimal/index.html>`_ 82 83Linux Notes 84----------- 85 86The build process requires a working C compiler and a *full* Python install with 87development headers. Linux distributions often ship the Python header files as 88a separate package, called *python-dev* or *python-devel*. 89 90Install headers on Debian/Ubuntu: 91 92* ``sudo apt-get install python-dev`` 93 94Windows Notes 95------------- 96 97* `Binary installers <http://www.bytereef.org/mpdecimal/download.html>`_ 98 99Links 100----- 101 102* `cdecimal project homepage <http://www.bytereef.org/mpdecimal/index.html>`_ 103* `cdecimal benchmarks <http://www.bytereef.org/mpdecimal/benchmarks.html>`_ 104 105""" 106 107 108PY_MAJOR = sys.version_info[0] 109PY_MINOR = sys.version_info[1] 110ARCH = platform.architecture()[0] 111SYSTEM = sys.platform 112MACHINE = None 113 114def err_exit(mesg): 115 sys.stderr.write("setup.py: error: %s\n" % mesg) 116 sys.exit(1) 117 118def uninstall(): 119 def check_file(name, again=False): 120 inputfunc = input if PY_MAJOR >= 3 else raw_input 121 if re.search('cdecimal.*\.(so|pyd|egg)', name): 122 if again: 123 ans = inputfunc("Please answer 'y' or 'n': ") 124 else: 125 print("Previously installed version detected: %s" % name) 126 ans = inputfunc("Remove it [y/n]? ") 127 ans = ans.lower() 128 if ans == 'y': 129 os.unlink(name) 130 elif ans != 'n': 131 check_file(name, True) 132 for p in sys.path: 133 if os.path.isdir(p): 134 for item in os.listdir(p): 135 f = os.path.join(p, item) 136 check_file(f) 137 elif os.path.isfile(p): 138 check_file(p) 139 140 141if not ((PY_MAJOR == 2 and PY_MINOR >= 5) or PY_MAJOR >= 3): 142 err_exit("Python version >= 2.5 required.") 143if not (ARCH == '64bit' or ARCH == '32bit'): 144 err_exit("64-bit or 32-bit machine required.") 145 146if 'uninstall' in sys.argv: 147 uninstall() 148 sys.exit(0) 149 150 151define_macros = [] 152undef_macros = ['NDEBUG'] 153if 'build' in sys.argv or 'build_ext' in sys.argv: 154 sys.argv.append('--force') 155if '--without-threads' in sys.argv: 156 # In mpdecimal-1.2, the speed penalty for thread local contexts 157 # has been reduced to 8-16%. The default is now to enable thread 158 # local contexts. To get the old default (faster), use this option. 159 sys.argv.remove('--without-threads') 160 define_macros.append(('WITHOUT_THREADS', 1)) 161else: 162 if SYSTEM == 'win32': # also valid for 64-bit. 163 try: 164 import threading 165 except ImportError: 166 define_macros.append(('WITHOUT_THREADS', 1)) 167 elif not sysconfig.get_config_var('WITH_THREAD'): 168 define_macros.append(('WITHOUT_THREADS', 1)) 169 170# Force specific configuration: 171c = ('x64', 'uint128', 'ansi64', 'ppro', 'ansi32', 'ansi-legacy', 'universal') 172for i, v in enumerate(sys.argv[:]): 173 if '--with-machine=' in v: 174 MACHINE = v.split('=')[1] 175 sys.argv.pop(i) 176 break 177if MACHINE and not MACHINE in c: 178 err_exit("invalid machine: %s" % MACHINE) 179 180if not MACHINE and 'darwin' in SYSTEM: 181 # multi-arch support 182 MACHINE = 'universal' 183 184if SYSTEM == 'win32': 185 # For automated testing: remove empty string 186 sys.argv = [v for v in sys.argv if v != ""] 187 188 189# 190# Generally, it is also possible to build the static libmpdec.a and 191# use that to build the Python module. There are systems where this 192# approach leads to problems: 193# 194# Windows: Changes made by Python's setlocale() only affect the 195# state of the dynamically linked CRT. This means that 196# some features of the format() function will not work 197# properly. 198# 199# AIX: If built with the static library, the gcc-internals 200# __multi3 and __udivti3 are undefined. 201# 202MULTILIB_ERR_64 = """ 203## 204## Error: 64-bit Python detected, but ./configure generates 32-bit config. 205## 206## One of the following choices must be passed to setup.py: 207## 208## --with-machine={x64,uint128,ansi64} 209## 210## OS X only, universal build: 211## 212## --with-machine=universal 213## 214""" 215MULTILIB_ERR_32 = """ 216## 217## Error: 32-bit Python detected, but ./configure generates 64-bit config. 218## 219## One of the following choices must be passed to setup.py: 220## 221## --with-machine={ppro,ansi32} 222## 223## OS X only, universal build: 224## 225## --with-machine=universal 226## 227""" 228 229def configure(machine, cc, py_size_t): 230 os.chmod("./configure", 0x1ed) # pip removes execute permissions. 231 if machine: # string has been validated. 232 os.system("./configure MACHINE=%s" % machine) 233 elif 'sunos' in SYSTEM and py_size_t == 8: 234 # cc is from sysconfig. 235 os.system("./configure CC='%s -m64'" % cc) 236 else: 237 os.system("./configure") 238 f = open("config.h") 239 config_vars = {} 240 for line in f: 241 if line.startswith("#define"): 242 l = line.split() 243 try: 244 config_vars[l[1]] = int(l[2]) 245 except ValueError: 246 pass 247 elif line.startswith("/* #undef"): 248 l = line.split() 249 config_vars[l[2]] = 0 250 f.close() 251 return config_vars 252 253def cdecimal_ext(machine): 254 depends = [ 255 'basearith.h', 'bits.h', 'constants.h', 'convolute.h', 'crt.h', 256 'difradix2.h', 'docstrings.h', 'fnt.h', 'fourstep.h', 'io.h', 257 'memory.h', 'mpdecimal.h', 'mpdecimal32.h', 'mpdecimal64.h', 258 'mptypes.h', 'numbertheory.h', 'sixstep.h', 'transpose.h', 259 'typearith.h', 'umodarith.h' 260 ] 261 sources = [ 262 'basearith.c', 'constants.c', 'context.c', 'convolute.c', 263 'crt.c', 'difradix2.c', 'fnt.c', 'fourstep.c', 'io.c', 'memory.c', 264 'mpdecimal.c', 'numbertheory.c', 'sixstep.c', 'transpose.c' 265 ] 266 sources.append('cdecimal2.c' if PY_MAJOR == 2 else 'cdecimal3.c') 267 extra_compile_args = [] 268 extra_link_args = [] 269 extra_objects = [] 270 271 272 if SYSTEM == 'win32': 273 define_macros.append(('_CRT_SECURE_NO_WARNINGS', '1')) 274 if not machine: # command line option 275 if ARCH == '64bit': 276 machine = 'x64' 277 else: 278 machine = 'ppro' 279 280 if machine == 'x64': 281 if not (PY_MAJOR == 2 and PY_MINOR == 5): 282 # set the build environment for ml64 283 from distutils.msvc9compiler import MSVCCompiler 284 cc = MSVCCompiler() 285 cc.initialize() 286 del cc 287 define_macros.extend([('CONFIG_64', '1'), ('MASM', '1')]) 288 extra_objects = ['vcdiv64.obj'] 289 os.system("ml64 /c /Cx vcdiv64.asm") 290 copy2("mpdecimal64vc.h", "mpdecimal.h") 291 elif machine == 'ansi64': 292 define_macros.extend([('CONFIG_64', '1'), ('ANSI', '1')]) 293 copy2("mpdecimal64vc.h", "mpdecimal.h") 294 elif machine == 'ppro': 295 define_macros.extend([('CONFIG_32', '1'), ('PPRO', '1'), 296 ('MASM', '1')]) 297 copy2("mpdecimal32vc.h", "mpdecimal.h") 298 elif machine == 'ansi32': 299 define_macros.extend([('CONFIG_32', '1'), ('ANSI', '1')]) 300 copy2("mpdecimal32vc.h", "mpdecimal.h") 301 elif machine == 'ansi-legacy': 302 define_macros.extend([('CONFIG_32', '1'), ('ANSI', '1'), 303 ('LEGACY_COMPILER', '1')]) 304 copy2("mpdecimal32vc.h", "mpdecimal.h") 305 else: 306 err_exit("unsupported machine: %s" % machine) 307 308 else: 309 cc = sysconfig.get_config_var('CC') 310 py_size_t = sysconfig.get_config_var('SIZEOF_SIZE_T') 311 if py_size_t != 8 and py_size_t != 4: 312 err_exit("unsupported architecture: sizeof(size_t) must be 8 or 4") 313 314 depends.append('config.h') 315 config_vars = configure(machine, cc, py_size_t) 316 if config_vars['SIZEOF_SIZE_T'] != 8 and \ 317 config_vars['SIZEOF_SIZE_T'] != 4: 318 err_exit("unsupported architecture: sizeof(size_t) must be 8 or 4") 319 320 if not machine and py_size_t != config_vars['SIZEOF_SIZE_T']: 321 if py_size_t == 8: 322 print(MULTILIB_ERR_64) 323 elif py_size_t == 4: 324 print(MULTILIB_ERR_32) 325 sys.exit(1) 326 327 if not machine: 328 if config_vars['HAVE_GCC_ASM_FOR_X64']: 329 machine = 'x64' 330 elif config_vars['HAVE_UINT128_T']: 331 machine = 'uint128' 332 elif config_vars['HAVE_GCC_ASM_FOR_X87']: 333 machine = 'ppro' 334 335 if machine == 'universal': 336 add_macros = [('UNIVERSAL', '1')] 337 elif py_size_t == 8: 338 if machine == 'x64': 339 add_macros = [('CONFIG_64', '1'), ('ASM', '1')] 340 elif machine == 'uint128': 341 add_macros = [('CONFIG_64', '1'), ('ANSI', '1'), 342 ('HAVE_UINT128_T', '1')] 343 else: 344 add_macros = [('CONFIG_64', '1'), ('ANSI', '1')] 345 else: 346 if machine == 'ppro' and ('gcc' in cc or 'clang' in cc): 347 # suncc: register allocator cannot deal with the inline asm. 348 # icc >= 11.0 works as well. 349 add_macros = [('CONFIG_32', '1'), ('PPRO', '1'), ('ASM', '1')] 350 if config_vars['HAVE_IPA_PURE_CONST_BUG']: 351 # Some versions of gcc miscompile inline asm: 352 # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46491 353 # http://gcc.gnu.org/ml/gcc/2010-11/msg00366.html 354 extra_compile_args.extend(['-fno-ipa-pure-const']) 355 else: # ansi32 356 add_macros = [('CONFIG_32', '1'), ('ANSI', '1')] 357 if machine == 'ansi-legacy': 358 add_macros.append(('LEGACY_COMPILER', '1')) 359 360 # Uncomment for warnings: 361 # extra_compile_args.extend(['-Wall', '-W', '-Wno-missing-field-initializers']) 362 363 # Uncomment for a debug build: 364 # extra_compile_args.extend(['-O0', '-g']) 365 366 if os.environ.get("CFLAGS"): 367 # Distutils drops -fno-strict-aliasing if CFLAGS are set: 368 # http://bugs.python.org/issue10847 369 if 'xlc' in cc: 370 extra_compile_args.append('-qnoansialias') 371 else: 372 extra_compile_args.append('-fno-strict-aliasing') 373 374 define_macros.extend(add_macros) 375 376 if config_vars['HAVE_GLIBC_MEMMOVE_BUG']: 377 # _FORTIFY_SOURCE wrappers for memmove and bcopy are incorrect: 378 # http://sourceware.org/ml/libc-alpha/2010-12/msg00009.html 379 undef_macros.append('_FORTIFY_SOURCE') 380 381 ext = Extension ( 382 'cdecimal', 383 define_macros=define_macros, 384 undef_macros=undef_macros, 385 depends=depends, 386 extra_compile_args=extra_compile_args, 387 extra_link_args=extra_link_args, 388 sources=sources, 389 extra_objects=extra_objects 390 ) 391 return ext 392 393 394setup ( 395 name = 'cdecimal', 396 version = '2.3', 397 description = DESCRIPTION, 398 long_description = LONG_DESCRIPTION, 399 url = 'http://www.bytereef.org/mpdecimal/index.html', 400 author = 'Stefan Krah', 401 author_email = 'skrah@bytereef.org', 402 license = 'BSD License', 403 download_url = 'http://www.bytereef.org/software/mpdecimal/releases/cdecimal-2.3.tar.gz', 404 keywords = ["decimal", "floating point", "correctly-rounded", 405 "arithmetic", "arbitrary precision"], 406 platforms = ["Many"], 407 classifiers = [ 408 "Development Status :: 5 - Production/Stable", 409 "Intended Audience :: Developers", 410 "Intended Audience :: Education", 411 "Intended Audience :: End Users/Desktop", 412 "Intended Audience :: Financial and Insurance Industry", 413 "Intended Audience :: Science/Research", 414 "License :: OSI Approved :: BSD License", 415 "Programming Language :: C", 416 "Programming Language :: Python :: 2.5", 417 "Programming Language :: Python :: 2.6", 418 "Programming Language :: Python :: 2.7", 419 "Programming Language :: Python :: 3", 420 "Programming Language :: Python :: 3.1", 421 "Programming Language :: Python :: 3.2", 422 "Operating System :: OS Independent", 423 "Topic :: Scientific/Engineering :: Mathematics", 424 "Topic :: Software Development" 425 ], 426 ext_modules = [cdecimal_ext(MACHINE)] 427) 428 429 430PATHLIST = glob("build/lib.*/cdecimal*") 431if PATHLIST: 432 CDECIMAL = PATHLIST[0] 433 copy2(CDECIMAL, "python") 434 435 436 437