1import sys
2import os
3from distutils.core import setup, Extension
4from distutils.command.clean import clean
5from distutils.command.build_ext import build_ext
6
7def writeln(s):
8    sys.stdout.write('%s\n' % s)
9    sys.stdout.flush()
10
11# Some operating systems may use a different library directory under the
12# prefix specified by --shared. It must be manually changed.
13
14lib_path = '/lib'
15
16# Fail gracefully for old versions of Python.
17
18if sys.version[:3] < '2.6':
19    writeln("GMPY2 requires Python 2.6 or later.")
20    writeln("Please use GMPY 1.x for earlier versions of Python.")
21    sys.exit()
22
23# Improved clean command.
24
25class gmpy_clean(clean):
26
27    def run(self):
28        self.all = True
29        clean.run(self)
30
31# Define a custom build class to force a new build.
32
33class gmpy_build_ext(build_ext):
34
35    def check_versions(self):
36        # Check the specified list of include directories to verify that valid
37        # versions of MPFR and MPC are available. If so, add entries to the
38        # appropriate lists
39
40        # Find the directory specfied for SHARED or STATIC.
41        prefix = []
42        for d in self.extensions[0].define_macros:
43            if d[0] in ('SHARED', 'STATIC'):
44                if d[1]:
45                    prefix.extend(map(os.path.expanduser, d[1].split(":")))
46                    try:
47                        self.extensions[0].define_macros.remove(d)
48                    except ValueError:
49                        pass
50
51        if sys.version.find('MSC') == -1:
52            windows = False
53            base_dir = ['/usr']
54            addin_dirs = ['/usr/local']
55        else:
56            windows = True
57            base_dir = []
58            addin_dirs = []
59
60        if prefix:
61            search_dirs = base_dir + addin_dirs + prefix
62        else:
63            search_dirs = base_dir + addin_dirs
64
65        if 'gmp' in self.extensions[0].libraries:
66            mplib = 'gmp'
67        else:
68            mplib = 'mpir'
69
70        # these two lines were not used anywhere
71        # use_mpfr = 'mpfr' in self.extensions[0].libraries
72        # use_mpc = 'mpc' in self.extensions[0].libraries
73
74        if not search_dirs:
75            return
76
77        gmp_found = ''
78        mpfr_found = ''
79        mpc_found = ''
80
81        for adir in search_dirs:
82            lookin = os.path.join(adir, 'include')
83
84            if os.path.isfile(os.path.join(lookin, mplib + '.h')):
85                gmp_found = adir
86
87            if os.path.isfile(os.path.join(lookin, 'mpfr.h')):
88                mpfr_found = adir
89
90            if os.path.isfile(os.path.join(lookin, 'mpc.h')):
91                mpc_found = adir
92
93        # Add the directory information for location where valid versions were
94        # found. This can cause confusion if there are multiple installations of
95        # the same version of Python on the system.
96
97        for adir in (gmp_found, mpfr_found, mpc_found):
98            if not adir:
99                continue
100            if adir in base_dir:
101                continue
102            if os.path.join(adir, 'include') in self.extensions[0].include_dirs:
103                continue
104            self.extensions[0].include_dirs += [os.path.join(adir, 'include')]
105            self.extensions[0].library_dirs += [os.path.join(adir, lib_path)]
106            if shared and not windows:
107                self.extensions[0].runtime_library_dirs += [os.path.join(adir, lib_path)]
108
109    def finalize_options(self):
110        build_ext.finalize_options(self)
111        gmpy_build_ext.check_versions(self)
112        # Check if --force was specified.
113        for i,d in enumerate(self.extensions[0].define_macros[:]):
114            if d[0] == 'FORCE':
115                self.force = 1
116                try:
117                    self.extensions[0].define_macros.remove(d)
118                except ValueError:
119                    pass
120
121# Several command line options can be used to modify compilation of GMPY2. To
122# maintain backwards compatibility with older versions of setup.py, the old
123# options are still supported.
124#
125# New-style options
126#
127#  --force         -> ignore timestamps and recompile
128#  --mpir          -> use MPIR instead of GMP (GMP is the default on
129#                     non-Windows operating systems)
130#  --gmp           -> use GMP instead of MPIR
131#  --lib64         -> use /<...>/lib64 instead of /<...>/lib
132#  --shared=<...>  -> add the specified directory prefix to the beginning of
133#                     the list of directories that are searched for GMP, MPFR,
134#                     and MPC shared libraries
135#  --static=<...>  -> create a statically linked library using static files from
136#                     the specified directory
137
138
139# Windows build defaults to using MPIR.
140
141if sys.version.find('MSC') == -1:
142    mplib = 'gmp'
143else:
144    mplib = 'mpir'
145
146# If 'clean' is the only argument to setup.py then we want to skip looking for
147# header files.
148
149if sys.argv[1].lower() in ['build', 'build_ext', 'install']:
150    do_search = True
151else:
152    do_search = False
153
154# Parse command line arguments. If custom prefix location is specified, it is
155# passed as a define so it can be processed in the custom build_ext defined
156# above.
157
158defines = []
159
160# Beginning with v2.1.0, MPFR and MPC will be required.
161
162force = False
163static = False
164shared = False
165
166for token in sys.argv[:]:
167    if token.lower() == '--force':
168        force = True
169        sys.argv.remove(token)
170
171    if token.lower() == '--lib64':
172        lib_path = '/lib64'
173        sys.argv.remove(token)
174
175    if token.lower() == '--mpir':
176        mplib = 'mpir'
177        sys.argv.remove(token)
178
179    if token.lower() == '--gmp':
180        mplib = 'gmp'
181        sys.argv.remove(token)
182
183    if token.lower().startswith('--shared'):
184        shared = True
185        try:
186            defines.append(('SHARED', token.split('=')[1]))
187        except IndexError:
188            defines.append(('SHARED', None))
189        sys.argv.remove(token)
190
191    if token.lower().startswith('--static'):
192        static = True
193        try:
194            defines.append(('STATIC', token.split('=')[1]))
195        except IndexError:
196            defines.append(('STATIC', None))
197        sys.argv.remove(token)
198
199incdirs = ['./src']
200libdirs = []
201rundirs = []
202extras = []
203
204# Specify extra link arguments for Windows.
205
206if sys.version.find('MSC') == -1:
207    my_extra_link_args = None
208else:
209    my_extra_link_args = ["/MANIFEST"]
210
211mp_found = False
212
213prefix = ''
214
215for i,d in enumerate(defines[:]):
216    if d[0] in ('SHARED', 'STATIC'):
217        if d[1]:
218            prefix = d[1]
219        defines.append((d[0], None))
220
221writeln(prefix)
222
223if force:
224    defines.append(('FORCE', None))
225
226if mplib == 'mpir':
227    defines.append(('MPIR', None))
228    libs = ['mpir']
229    if static:
230        extras.append(os.path.join(prefix, lib_path, 'libmpir.a'))
231else:
232    libs = ['gmp']
233    if static:
234        extras.append(os.path.join(prefix, lib_path, 'libgmp.a'))
235
236libs.append('mpfr')
237if static:
238    extras.append(os.path.join(prefix, lib_path, 'libmpfr.a'))
239
240libs.append('mpc')
241if static:
242    extras.append(os.path.join(prefix, lib_path, 'libmpc.a'))
243
244writeln(str(defines))
245
246# decomment next line (w/gcc, only!) to support gcov
247#   os.environ['CFLAGS'] = '-fprofile-arcs -ftest-coverage -O0'
248
249# prepare the extension for building
250
251my_commands = {'clean' : gmpy_clean, 'build_ext' : gmpy_build_ext}
252
253gmpy2_ext = Extension('gmpy2',
254                      sources=[os.path.join('src', 'gmpy2.c')],
255                      libraries=libs,
256                      define_macros = defines,
257                      extra_objects = extras,
258                      extra_link_args = my_extra_link_args)
259
260setup(name = "gmpy2",
261      version = "2.1.0a3",
262      maintainer = "Case Van Horsen",
263      maintainer_email = "casevh@gmail.com",
264      url = "http://code.google.com/p/gmpy/",
265      description = "GMP/MPIR, MPFR, and MPC interface to Python 2.6+ and 3.x",
266      classifiers = [
267        'Development Status :: 3 - Alpha',
268        'Intended Audience :: Developers',
269        'Intended Audience :: Science/Research',
270        'License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)',
271        'Natural Language :: English',
272        'Operating System :: MacOS :: MacOS X',
273        'Operating System :: Microsoft :: Windows',
274        'Operating System :: POSIX',
275        'Programming Language :: C',
276        'Programming Language :: Python :: 2',
277        'Programming Language :: Python :: 3',
278        'Programming Language :: Python :: Implementation :: CPython',
279        'Topic :: Scientific/Engineering :: Mathematics',
280        'Topic :: Software Development :: Libraries :: Python Modules',
281      ],
282      cmdclass = my_commands,
283      ext_modules = [gmpy2_ext]
284)
285