Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/setup.py
2609 views
1
#!/usr/bin/env python
2
3
import os, sys, time, errno, platform
4
from distutils.core import setup
5
from distutils.extension import Extension
6
from glob import glob, fnmatch
7
from warnings import warn
8
9
#########################################################
10
### List of Extensions
11
###
12
### Since Sage 3.2 the list of extensions resides in
13
### module_list.py in the same directory as this file
14
### (augmented by the list of interpreters
15
### generated by sage/ext/gen_interpreters.py)
16
#########################################################
17
18
from module_list import ext_modules
19
import sage.ext.gen_interpreters
20
import warnings
21
22
#########################################################
23
### Configuration
24
#########################################################
25
26
if len(sys.argv) > 1 and sys.argv[1] == "sdist":
27
sdist = True
28
else:
29
sdist = False
30
31
if not os.environ.has_key('SAGE_ROOT'):
32
print " ERROR: The environment variable SAGE_ROOT must be defined."
33
sys.exit(1)
34
else:
35
SAGE_ROOT = os.environ['SAGE_ROOT']
36
SAGE_LOCAL = SAGE_ROOT + '/local'
37
SAGE_DEVEL = SAGE_ROOT + '/devel'
38
SAGE_INC = SAGE_LOCAL + '/include/'
39
40
if not os.environ.has_key('SAGE_VERSION'):
41
SAGE_VERSION=0
42
else:
43
SAGE_VERSION = os.environ['SAGE_VERSION']
44
45
try:
46
compile_result_dir = os.environ['XML_RESULTS']
47
keep_going = True
48
except KeyError:
49
compile_result_dir = None
50
keep_going = False
51
52
SITE_PACKAGES = '%s/lib/python%s/site-packages/'%(SAGE_LOCAL,platform.python_version().rsplit('.', 1)[0])
53
if not os.path.exists(SITE_PACKAGES):
54
raise RuntimeError, "Unable to find site-packages directory (see setup.py file in sage python code)."
55
56
if not os.path.exists('build/sage'):
57
os.makedirs('build/sage')
58
59
sage_link = SITE_PACKAGES + '/sage'
60
if not os.path.islink(sage_link) or not os.path.exists(sage_link):
61
os.system('rm -rf "%s"'%sage_link)
62
os.system('cd %s; ln -sf ../../../../devel/sage/build/sage .'%SITE_PACKAGES)
63
64
# search for dependencies and add to gcc -I<path>
65
include_dirs = ['%s/include'%SAGE_LOCAL,
66
'%s/include/csage'%SAGE_LOCAL,
67
'%s/sage/sage/ext'%SAGE_DEVEL]
68
69
# search for dependencies only
70
extra_include_dirs = [ '%s/include/python%s'%(SAGE_LOCAL,platform.python_version().rsplit('.', 1)[0]) ]
71
72
extra_compile_args = [ ]
73
extra_link_args = [ ]
74
75
# comment these four lines out to turn on warnings from gcc
76
import distutils.sysconfig
77
NO_WARN = True
78
if NO_WARN and distutils.sysconfig.get_config_var('CC').startswith("gcc"):
79
extra_compile_args.append('-w')
80
81
DEVEL = False
82
if DEVEL:
83
extra_compile_args.append('-ggdb')
84
85
# Generate interpreters
86
87
sage.ext.gen_interpreters.rebuild(SAGE_DEVEL + '/sage/sage/ext/interpreters')
88
ext_modules = ext_modules + sage.ext.gen_interpreters.modules
89
90
91
#########################################################
92
### Testing related stuff
93
#########################################################
94
95
class CompileRecorder(object):
96
97
def __init__(self, f):
98
self._f = f
99
self._obj = None
100
101
def __get__(self, obj, type=None):
102
# Act like a method...
103
self._obj = obj
104
return self
105
106
def __call__(self, *args):
107
t = time.time()
108
try:
109
if self._obj:
110
res = self._f(self._obj, *args)
111
else:
112
res = self._f(*args)
113
except Exception, ex:
114
print ex
115
res = ex
116
t = time.time() - t
117
118
errors = failures = 0
119
if self._f is compile_command0:
120
name = "cythonize." + args[0][1].name
121
failures = int(bool(res))
122
else:
123
name = "gcc." + args[0][1].name
124
errors = int(bool(res))
125
if errors or failures:
126
type = "failure" if failures else "error"
127
failure_item = """<%(type)s/>""" % locals()
128
else:
129
failure_item = ""
130
output = open("%s/%s.xml" % (compile_result_dir, name), "w")
131
output.write("""
132
<?xml version="1.0" ?>
133
<testsuite name="%(name)s" errors="%(errors)s" failures="%(failures)s" tests="1" time="%(t)s">
134
<testcase classname="%(name)s" name="compile">
135
%(failure_item)s
136
</testcase>
137
</testsuite>
138
""".strip() % locals())
139
output.close()
140
return res
141
142
if compile_result_dir:
143
record_compile = CompileRecorder
144
else:
145
record_compile = lambda x: x
146
147
# Remove (potentially invalid) star import caches
148
import sage.misc.lazy_import_cache
149
if os.path.exists(sage.misc.lazy_import_cache.get_cache_file()):
150
os.unlink(sage.misc.lazy_import_cache.get_cache_file())
151
152
153
######################################################################
154
# CODE for generating C/C++ code from Cython and doing dependency
155
# checking, etc. In theory distutils would run Cython, but I don't
156
# trust it at all, and it won't have the more sophisticated dependency
157
# checking that we need.
158
######################################################################
159
160
# Do not put all, but only the most common libraries and their headers
161
# (that are likely to change on an upgrade) here:
162
# [At least at the moment. Make sure the headers aren't copied with "-p",
163
# or explicitly touch them in the respective spkg's spkg-install.]
164
lib_headers = { "gmp": [ SAGE_INC+"gmp.h" ], # cf. #8664, #9896
165
"gmpxx": [ SAGE_INC+"gmpxx.h" ]
166
}
167
168
for m in ext_modules:
169
170
for lib in lib_headers.keys():
171
if lib in m.libraries:
172
m.depends += lib_headers[lib]
173
174
# FIMXE: Do NOT link the following libraries to each and
175
# every module (regardless of the language btw.):
176
m.libraries = ['csage'] + m.libraries + ['stdc++', 'ntl']
177
178
m.extra_compile_args += extra_compile_args
179
m.extra_link_args += extra_link_args
180
m.library_dirs += ['%s/lib' % SAGE_LOCAL]
181
182
183
184
#############################################
185
###### Parallel Cython execution
186
#############################################
187
188
def run_command(cmd):
189
"""
190
INPUT:
191
cmd -- a string; a command to run
192
193
OUTPUT:
194
prints cmd to the console and then runs os.system
195
"""
196
print cmd
197
return os.system(cmd)
198
199
def apply_pair(p):
200
"""
201
Given a pair p consisting of a function and a value, apply
202
the function to the value.
203
204
This exists solely because we can't pickle an anonymous function
205
in execute_list_of_commands_in_parallel below.
206
"""
207
return p[0](p[1])
208
209
def execute_list_of_commands_in_parallel(command_list, nthreads):
210
"""
211
Execute the given list of commands, possibly in parallel, using
212
``nthreads`` threads. Terminates ``setup.py`` with an exit code
213
of 1 if an error occurs in any subcommand.
214
215
INPUT:
216
217
- ``command_list`` -- a list of commands, each given as a pair of
218
the form ``[function, argument]`` of a function to call and its
219
argument
220
221
- ``nthreads`` -- integer; number of threads to use
222
223
WARNING: commands are run roughly in order, but of course successive
224
commands may be run at the same time.
225
"""
226
from multiprocessing import Pool
227
import twisted.persisted.styles #doing this import will allow instancemethods to be pickable
228
p = Pool(nthreads)
229
process_command_results(p.imap(apply_pair, command_list))
230
231
def process_command_results(result_values):
232
error = None
233
for r in result_values:
234
if r:
235
print "Error running command, failed with status %s."%r
236
if not keep_going:
237
sys.exit(1)
238
error = r
239
if error:
240
sys.exit(1)
241
242
def execute_list_of_commands(command_list):
243
"""
244
INPUT:
245
246
- ``command_list`` -- a list of strings or pairs
247
248
OUTPUT:
249
250
For each entry in command_list, we attempt to run the command.
251
If it is a string, we call ``os.system()``. If it is a pair [f, v],
252
we call f(v).
253
254
If the environment variable :envvar:`SAGE_NUM_THREADS` is set, use
255
that many threads.
256
"""
257
t = time.time()
258
# Determine the number of threads from the environment variable
259
# SAGE_NUM_THREADS, which is set automatically by sage-env
260
try:
261
nthreads = int(os.environ['SAGE_NUM_THREADS'])
262
except KeyError:
263
nthreads = 1
264
265
# normalize the command_list to handle strings correctly
266
command_list = [ [run_command, x] if isinstance(x, str) else x for x in command_list ]
267
268
# No need for more threads than there are commands, but at least one
269
nthreads = min(len(command_list), nthreads)
270
nthreads = max(1, nthreads)
271
272
def plural(n,noun):
273
if n == 1:
274
return "1 %s"%noun
275
return "%i %ss"%(n,noun)
276
277
print "Executing %s (using %s)"%(plural(len(command_list),"command"), plural(nthreads,"thread"))
278
execute_list_of_commands_in_parallel(command_list, nthreads)
279
print "Time to execute %s: %s seconds"%(plural(len(command_list),"command"), time.time() - t)
280
281
282
########################################################################
283
##
284
## Parallel gcc execution
285
##
286
## This code is responsible for making distutils dispatch the calls to
287
## build_ext in parallel. Since distutils doesn't seem to do this by
288
## default, we create our own extension builder and override the
289
## appropriate methods. Unfortunately, in distutils, the logic of
290
## deciding whether an extension needs to be recompiled and actually
291
## making the call to gcc to recompile the extension are in the same
292
## function. As a result, we can't just override one function and have
293
## everything magically work. Instead, we split this work between two
294
## functions. This works fine for our application, but it means that
295
## we can't use this modification to make the other parts of Sage that
296
## build with distutils call gcc in parallel.
297
##
298
########################################################################
299
300
from distutils.command.build_ext import build_ext
301
from distutils.dep_util import newer_group
302
from types import ListType, TupleType
303
from distutils import log
304
305
class sage_build_ext(build_ext):
306
307
def build_extensions(self):
308
309
from distutils.debug import DEBUG
310
311
if DEBUG:
312
print "self.compiler.compiler:"
313
print self.compiler.compiler
314
print "self.compiler.compiler_cxx:"
315
print self.compiler.compiler_cxx # currently not used
316
print "self.compiler.compiler_so:"
317
print self.compiler.compiler_so
318
print "self.compiler.linker_so:"
319
print self.compiler.linker_so
320
# There are further interesting variables...
321
sys.stdout.flush()
322
323
324
# At least on MacOS X, the library dir of the *original* Sage
325
# installation is "hard-coded" into the linker *command*, s.t.
326
# that directory is always searched *first*, which causes trouble
327
# after the Sage installation has been moved (or its directory simply
328
# been renamed), especially in conjunction with upgrades (cf. #9896).
329
# (In principle, the Python configuration should be modified on
330
# Sage relocations as well, but until that's done, we simply fix
331
# the most important.)
332
# Since the following is performed only once per call to "setup",
333
# and doesn't hurt on other systems, we unconditionally replace *any*
334
# library directory specified in the (dynamic) linker command by the
335
# current Sage library directory (if it doesn't already match that),
336
# and issue a warning message:
337
338
if True or sys.platform[:6]=="darwin":
339
340
sage_libdir = os.path.realpath(SAGE_LOCAL+"/lib")
341
ldso_cmd = self.compiler.linker_so # a list of strings, like argv
342
343
for i in range(1, len(ldso_cmd)):
344
345
if ldso_cmd[i][:2] == "-L":
346
libdir = os.path.realpath(ldso_cmd[i][2:])
347
self.debug_print(
348
"Library dir found in dynamic linker command: " +
349
"\"%s\"" % libdir)
350
if libdir != sage_libdir:
351
self.compiler.warn(
352
"Replacing library search directory in linker " +
353
"command:\n \"%s\" -> \"%s\"\n" % (libdir,
354
sage_libdir))
355
ldso_cmd[i] = "-L"+sage_libdir
356
357
if DEBUG:
358
print "self.compiler.linker_so (after fixing library dirs):"
359
print self.compiler.linker_so
360
sys.stdout.flush()
361
362
363
# First, sanity-check the 'extensions' list
364
self.check_extensions_list(self.extensions)
365
366
import time
367
t = time.time()
368
369
compile_commands = []
370
for ext in self.extensions:
371
need_to_compile, p = self.prepare_extension(ext)
372
if need_to_compile:
373
compile_commands.append((record_compile(self.build_extension), p))
374
375
execute_list_of_commands(compile_commands)
376
377
print "Total time spent compiling C/C++ extensions: ", time.time() - t, "seconds."
378
379
def prepare_extension(self, ext):
380
sources = ext.sources
381
if sources is None or type(sources) not in (ListType, TupleType):
382
raise DistutilsSetupError, \
383
("in 'ext_modules' option (extension '%s'), " +
384
"'sources' must be present and must be " +
385
"a list of source filenames") % ext.name
386
sources = list(sources)
387
388
fullname = self.get_ext_fullname(ext.name)
389
if self.inplace:
390
# ignore build-lib -- put the compiled extension into
391
# the source tree along with pure Python modules
392
393
modpath = string.split(fullname, '.')
394
package = string.join(modpath[0:-1], '.')
395
base = modpath[-1]
396
397
build_py = self.get_finalized_command('build_py')
398
package_dir = build_py.get_package_dir(package)
399
ext_filename = os.path.join(package_dir,
400
self.get_ext_filename(base))
401
relative_ext_filename = self.get_ext_filename(base)
402
else:
403
ext_filename = os.path.join(self.build_lib,
404
self.get_ext_filename(fullname))
405
relative_ext_filename = self.get_ext_filename(fullname)
406
407
# while dispatching the calls to gcc in parallel, we sometimes
408
# hit a race condition where two separate build_ext objects
409
# try to create a given directory at the same time; whoever
410
# loses the race then seems to throw an error, saying that
411
# the directory already exists. so, instead of fighting to
412
# fix the race condition, we simply make sure the entire
413
# directory tree exists now, while we're processing the
414
# extensions in serial.
415
relative_ext_dir = os.path.split(relative_ext_filename)[0]
416
prefixes = ['', self.build_lib, self.build_temp]
417
for prefix in prefixes:
418
path = os.path.join(prefix, relative_ext_dir)
419
try:
420
os.makedirs(path)
421
except OSError, e:
422
assert e.errno==errno.EEXIST, 'Cannot create %s.' % path
423
depends = sources + ext.depends
424
if not (self.force or newer_group(depends, ext_filename, 'newer')):
425
log.debug("skipping '%s' extension (up-to-date)", ext.name)
426
need_to_compile = False
427
else:
428
log.info("building '%s' extension", ext.name)
429
need_to_compile = True
430
431
return need_to_compile, (sources, ext, ext_filename)
432
433
def build_extension(self, p):
434
435
sources, ext, ext_filename = p
436
437
# First, scan the sources for SWIG definition files (.i), run
438
# SWIG on 'em to create .c files, and modify the sources list
439
# accordingly.
440
sources = self.swig_sources(sources, ext)
441
442
# Next, compile the source code to object files.
443
444
# XXX not honouring 'define_macros' or 'undef_macros' -- the
445
# CCompiler API needs to change to accommodate this, and I
446
# want to do one thing at a time!
447
448
# Two possible sources for extra compiler arguments:
449
# - 'extra_compile_args' in Extension object
450
# - CFLAGS environment variable (not particularly
451
# elegant, but people seem to expect it and I
452
# guess it's useful)
453
# The environment variable should take precedence, and
454
# any sensible compiler will give precedence to later
455
# command line args. Hence we combine them in order:
456
extra_args = ext.extra_compile_args or []
457
458
macros = ext.define_macros[:]
459
for undef in ext.undef_macros:
460
macros.append((undef,))
461
462
objects = self.compiler.compile(sources,
463
output_dir=self.build_temp,
464
macros=macros,
465
include_dirs=ext.include_dirs,
466
debug=self.debug,
467
extra_postargs=extra_args,
468
depends=ext.depends)
469
470
# XXX -- this is a Vile HACK!
471
#
472
# The setup.py script for Python on Unix needs to be able to
473
# get this list so it can perform all the clean up needed to
474
# avoid keeping object files around when cleaning out a failed
475
# build of an extension module. Since Distutils does not
476
# track dependencies, we have to get rid of intermediates to
477
# ensure all the intermediates will be properly re-built.
478
#
479
self._built_objects = objects[:]
480
481
# Now link the object files together into a "shared object" --
482
# of course, first we have to figure out all the other things
483
# that go into the mix.
484
if ext.extra_objects:
485
objects.extend(ext.extra_objects)
486
extra_args = ext.extra_link_args or []
487
488
# Detect target language, if not provided
489
language = ext.language or self.compiler.detect_language(sources)
490
491
self.compiler.link_shared_object(
492
objects, ext_filename,
493
libraries=self.get_libraries(ext),
494
library_dirs=ext.library_dirs,
495
runtime_library_dirs=ext.runtime_library_dirs,
496
extra_postargs=extra_args,
497
export_symbols=self.get_export_symbols(ext),
498
debug=self.debug,
499
build_temp=self.build_temp,
500
target_lang=language)
501
502
503
504
505
#############################################
506
###### Dependency checking
507
#############################################
508
509
CYTHON_INCLUDE_DIRS=[
510
SAGE_LOCAL + '/lib/python/site-packages/Cython/Includes/',
511
SAGE_LOCAL + '/lib/python/site-packages/Cython/Includes/Deprecated/',
512
]
513
514
# matches any dependency
515
import re
516
dep_regex = re.compile(r'^ *(?:(?:cimport +([\w\. ,]+))|(?:from +([\w.]+) +cimport)|(?:include *[\'"]([^\'"]+)[\'"])|(?:cdef *extern *from *[\'"]([^\'"]+)[\'"]))', re.M)
517
518
# system headers should have pointy brackets, as in
519
# cdef extern from "<math.h>":
520
# but we didn't add them consistently. Workaround:
521
system_header_files = \
522
['complex.h', 'signal.h', 'math.h', 'limits.h', 'stdlib.h',
523
'arpa/inet.h', 'float.h', 'string.h', 'stdint.h', 'stdio.h',
524
'dlfcn.h', 'setjmp.h' ]
525
526
class DependencyTree:
527
"""
528
This class stores all the information about the dependencies of a set of
529
Cython files. It uses a lot of caching so information only needs to be
530
looked up once per build.
531
"""
532
def __init__(self):
533
self._last_parse = {}
534
self._timestamps = {}
535
self._deps = {}
536
self._deps_all = {}
537
self.root = "%s/devel/sage/" % SAGE_ROOT
538
539
def __getstate__(self):
540
"""
541
Used for pickling.
542
543
Timestamps and deep dependencies may change between builds,
544
so we don't want to save those.
545
"""
546
state = dict(self.__dict__)
547
state['_timestamps'] = {}
548
state['_deps_all'] = {}
549
return state
550
551
def __setstate__(self, state):
552
"""
553
Used for unpickling.
554
"""
555
self.__dict__.update(state)
556
self._timestamps = {}
557
self._deps_all = {}
558
self.root = "%s/devel/sage/" % SAGE_ROOT
559
560
def timestamp(self, filename):
561
"""
562
Look up the last modified time of a file, with caching.
563
"""
564
if filename not in self._timestamps:
565
try:
566
self._timestamps[filename] = os.path.getmtime(filename)
567
except OSError:
568
self._timestamps[filename] = 0
569
return self._timestamps[filename]
570
571
def parse_deps(self, filename, ext_module, verify=True):
572
"""
573
Open a Cython file and extract all of its dependencies.
574
575
INPUT:
576
filename -- the file to parse
577
verify -- only return existing files (default True)
578
579
OUTPUT:
580
list of dependency files
581
"""
582
is_cython_file = lambda f:\
583
fnmatch.fnmatch(f,'*.pyx') or \
584
fnmatch.fnmatch(f,'*.pxd') or \
585
fnmatch.fnmatch(f,'*.pxi')
586
587
# only parse cython files
588
if not is_cython_file(filename):
589
return []
590
591
dirname = os.path.split(filename)[0]
592
deps = set()
593
if filename.endswith('.pyx'):
594
pxd_file = filename[:-4] + '.pxd'
595
if os.path.exists(pxd_file):
596
deps.add(pxd_file)
597
598
raw_deps = []
599
f = open(filename)
600
for m in dep_regex.finditer(open(filename).read()):
601
groups = m.groups()
602
modules = groups[0] or groups[1] # cimport or from ... cimport
603
if modules is not None:
604
for module in modules.split(','):
605
module = module.strip().split(' ')[0] # get rid of 'as' clause
606
if '.' in module:
607
path = module.replace('.', '/') + '.pxd'
608
base_dependency_name = path
609
else:
610
path = "%s/%s.pxd" % (dirname, module)
611
base_dependency_name = "%s.pxd"%module
612
raw_deps.append((path, base_dependency_name))
613
else: # include or extern from
614
extern_file = groups[2] or groups[3]
615
path = os.path.join(dirname, extern_file)
616
if not os.path.exists(path):
617
path = extern_file
618
raw_deps.append((path, extern_file))
619
620
for path, base_dependency_name in raw_deps:
621
# if we can find the file, add it to the dependencies.
622
path = os.path.normpath(path)
623
if os.path.exists(path):
624
deps.add(path)
625
# we didn't find the file locally, so check the
626
# Cython include path.
627
else:
628
found_include = path.startswith('<') and path.endswith('>')
629
if path in system_header_files:
630
found_include = True
631
for idir in ext_module.include_dirs + CYTHON_INCLUDE_DIRS + include_dirs + extra_include_dirs:
632
new_path = os.path.normpath(os.path.join(idir, base_dependency_name))
633
if os.path.exists(new_path):
634
deps.add(new_path)
635
found_include = True
636
break
637
new_path = os.path.normpath(idir + base_dependency_name[:-4] + "/__init__.pxd")
638
if os.path.exists(new_path):
639
deps.add(new_path)
640
found_include = True
641
break
642
# so we really couldn't find the dependency -- raise
643
# an exception.
644
if not found_include:
645
msg = 'could not find dependency %s included in %s.'%(path, filename)
646
if is_cython_file(path):
647
raise IOError, msg
648
else:
649
warnings.warn(msg+' I will assume it is a system C/C++ header.')
650
f.close()
651
return list(deps)
652
653
def immediate_deps(self, filename, ext_module):
654
"""
655
Returns a list of files directly referenced by this file.
656
"""
657
if (filename not in self._deps
658
or self.timestamp(filename) < self._last_parse[filename]):
659
self._deps[filename] = self.parse_deps(filename, ext_module)
660
self._last_parse[filename] = self.timestamp(filename)
661
return self._deps[filename]
662
663
def all_deps(self, filename, ext_module, path=None):
664
"""
665
Returns all files directly or indirectly referenced by this file.
666
667
A recursive algorithm is used here to maximize caching, but it is
668
still robust for circular cimports (via the path parameter).
669
"""
670
if filename not in self._deps_all:
671
circular = False
672
deps = set([filename])
673
if path is None:
674
path = set([filename])
675
else:
676
path.add(filename)
677
for f in self.immediate_deps(filename, ext_module):
678
if f not in path:
679
deps.update(self.all_deps(f, ext_module, path))
680
else:
681
circular = True
682
path.remove(filename)
683
if circular:
684
return deps # Don't cache, as this may be incomplete
685
else:
686
self._deps_all[filename] = deps
687
return self._deps_all[filename]
688
689
def newest_dep(self, filename, ext_module):
690
"""
691
Returns the most recently modified file that filename depends on,
692
along with its timestamp.
693
"""
694
nfile = filename
695
ntime = self.timestamp(filename)
696
for f in self.all_deps(filename, ext_module):
697
if self.timestamp(f) > ntime:
698
nfile = f
699
ntime = self.timestamp(f)
700
return nfile, ntime
701
702
703
#############################################
704
###### Build code
705
#############################################
706
707
def process_filename(f, m):
708
base, ext = os.path.splitext(f)
709
if ext == '.pyx':
710
if m.language == 'c++':
711
return base + '.cpp'
712
else:
713
return base + '.c'
714
else:
715
return f
716
717
def compile_command0(p):
718
"""
719
Given a pair p = (f, m), with a .pyx file f which is a part the
720
module m, call Cython on f
721
722
INPUT:
723
p -- a 2-tuple f, m
724
725
copy the file to SITE_PACKAGES, and return a string
726
which will call Cython on it.
727
"""
728
f, m = p
729
if f.endswith('.pyx'):
730
# process cython file
731
732
# find the right filename
733
outfile = f[:-4]
734
if m.language == 'c++':
735
outfile += ".cpp"
736
cplus = '--cplus'
737
else:
738
outfile += ".c"
739
cplus = ''
740
741
# call cython, abort if it failed
742
cmd = "python `which cython` %s --old-style-globals --disable-function-redefinition --embed-positions --directive cdivision=True,autotestdict=False,fast_getattr=True -I%s -o %s %s"%(cplus, os.getcwd(), outfile, f)
743
r = run_command(cmd)
744
if r:
745
return r
746
747
# if cython worked, copy the file to the build directory
748
pyx_inst_file = '%s/%s'%(SITE_PACKAGES, f)
749
retval = os.system('cp %s %s 2>/dev/null'%(f, pyx_inst_file))
750
# we could do this more elegantly -- load the files, use
751
# os.path.exists to check that they exist, etc. ... but the
752
# *vast* majority of the time, the copy just works. so this is
753
# just specializing for the most common use case.
754
if retval:
755
dirname, filename = os.path.split(pyx_inst_file)
756
try:
757
os.makedirs(dirname)
758
except OSError, e:
759
assert e.errno==errno.EEXIST, 'Cannot create %s.' % dirname
760
retval = os.system('cp %s %s 2>/dev/null'%(f, pyx_inst_file))
761
if retval:
762
raise OSError, "cannot copy %s to %s"%(f,pyx_inst_file)
763
print "%s --> %s"%(f, pyx_inst_file)
764
765
elif f.endswith(('.c','.cc','.cpp')):
766
# process C/C++ file
767
cmd = "touch %s"%f
768
r = run_command(cmd)
769
770
return r
771
772
# Can't pickle decorated functions.
773
compile_command = record_compile(compile_command0)
774
775
776
def compile_command_list(ext_modules, deps):
777
"""
778
Computes a list of commands needed to compile and link the
779
extension modules given in 'ext_modules'
780
"""
781
queue_compile_high = []
782
queue_compile_med = []
783
queue_compile_low = []
784
785
for m in ext_modules:
786
new_sources = []
787
for f in m.sources:
788
if f.endswith('.pyx'):
789
dep_file, dep_time = deps.newest_dep(f,m)
790
dest_file = "%s/%s"%(SITE_PACKAGES, f)
791
dest_time = deps.timestamp(dest_file)
792
if dest_time < dep_time:
793
if dep_file == f:
794
print "Building modified file %s."%f
795
queue_compile_high.append([compile_command, (f,m)])
796
elif dep_file == (f[:-4] + '.pxd'):
797
print "Building %s because it depends on %s."%(f, dep_file)
798
queue_compile_med.append([compile_command, (f,m)])
799
else:
800
print "Building %s because it depends on %s."%(f, dep_file)
801
queue_compile_low.append([compile_command, (f,m)])
802
new_sources.append(process_filename(f, m))
803
m.sources = new_sources
804
return queue_compile_high + queue_compile_med + queue_compile_low
805
806
807
## Note: the DependencyTree object created below was designed with
808
## the intention of pickling it and saving it between builds. However,
809
## this wasn't robust enough to handle all of the various cases we
810
## run into with the Sage build process, so caching of this information
811
## has been temporarily disabled (see trac #4647 and trac #4651). If
812
## you want to try this out, uncomment all the lines that begin with
813
## two hash marks below, and comment out the line that says
814
## "deps = DependencyTree()".
815
816
##import cPickle as pickle
817
##CYTHON_DEPS_FILE='.cython_deps'
818
819
if not sdist:
820
print "Updating Cython code...."
821
t = time.time()
822
## try:
823
## f = open(CYTHON_DEPS_FILE)
824
## deps = pickle.load(open(CYTHON_DEPS_FILE))
825
## f.close()
826
## except:
827
## deps = DependencyTree()
828
deps = DependencyTree()
829
queue = compile_command_list(ext_modules, deps)
830
execute_list_of_commands(queue)
831
## f = open(CYTHON_DEPS_FILE, 'w')
832
## pickle.dump(deps, f)
833
## f.close()
834
print "Finished compiling Cython code (time = %s seconds)"%(time.time() - t)
835
836
837
#########################################################
838
### Distutils
839
#########################################################
840
841
code = setup(name = 'sage',
842
843
version = SAGE_VERSION,
844
845
description = 'Sage: Open Source Mathematics Software',
846
847
license = 'GNU Public License (GPL)',
848
849
author = 'William Stein et al.',
850
851
author_email= 'http://groups.google.com/group/sage-support',
852
853
url = 'http://www.sagemath.org',
854
855
packages = ['sage',
856
857
'sage.algebras',
858
'sage.algebras.quatalg',
859
'sage.algebras.steenrod',
860
861
'sage.calculus',
862
863
'sage.categories',
864
'sage.categories.examples',
865
866
'sage.coding',
867
'sage.coding.source_coding',
868
869
'sage.combinat',
870
'sage.combinat.crystals',
871
'sage.combinat.designs',
872
'sage.combinat.sf',
873
'sage.combinat.root_system',
874
'sage.combinat.matrices',
875
'sage.combinat.posets',
876
'sage.combinat.species',
877
878
'sage.combinat.words',
879
880
'sage.combinat.iet',
881
882
'sage.crypto',
883
'sage.crypto.block_cipher',
884
'sage.crypto.mq',
885
'sage.crypto.public_key',
886
887
'sage.databases',
888
889
'sage.ext',
890
'sage.ext.interpreters',
891
892
'sage.finance',
893
894
'sage.functions',
895
896
'sage.geometry',
897
'sage.geometry.polyhedron',
898
'sage.geometry.triangulation',
899
900
'sage.games',
901
902
'sage.gsl',
903
904
'sage.graphs',
905
'sage.graphs.base',
906
'sage.graphs.modular_decomposition',
907
'sage.graphs.graph_decompositions',
908
'sage.graphs.graph_decompositions',
909
910
'sage.groups',
911
'sage.groups.abelian_gps',
912
'sage.groups.additive_abelian',
913
'sage.groups.matrix_gps',
914
'sage.groups.perm_gps',
915
'sage.groups.perm_gps.partn_ref',
916
917
'sage.homology',
918
919
'sage.interacts',
920
921
'sage.interfaces',
922
923
'sage.lfunctions',
924
925
'sage.libs',
926
'sage.libs.fplll',
927
'sage.libs.linbox',
928
'sage.libs.mwrank',
929
'sage.libs.ntl',
930
'sage.libs.flint',
931
'sage.libs.lrcalc',
932
'sage.libs.pari',
933
'sage.libs.singular',
934
'sage.libs.symmetrica',
935
'sage.libs.cremona',
936
'sage.libs.mpmath',
937
'sage.libs.lcalc',
938
939
'sage.logic',
940
941
'sage.matrix',
942
'sage.media',
943
'sage.misc',
944
945
'sage.modules',
946
'sage.modules.fg_pid',
947
948
'sage.modular',
949
'sage.modular.arithgroup',
950
'sage.modular.abvar',
951
'sage.modular.hecke',
952
'sage.modular.modform',
953
'sage.modular.modsym',
954
'sage.modular.quatalg',
955
'sage.modular.ssmod',
956
'sage.modular.overconvergent',
957
'sage.modular.local_comp',
958
959
'sage.monoids',
960
961
'sage.numerical',
962
'sage.numerical.backends',
963
964
'sage.plot',
965
'sage.plot.plot3d',
966
967
'sage.probability',
968
969
'sage.quadratic_forms',
970
'sage.quadratic_forms.genera',
971
972
'sage.rings',
973
'sage.rings.finite_rings',
974
'sage.rings.function_field',
975
'sage.rings.number_field',
976
'sage.rings.padics',
977
'sage.rings.polynomial',
978
'sage.rings.polynomial.padics',
979
'sage.rings.semirings',
980
981
'sage.tests',
982
'sage.tests.french_book',
983
984
'sage.sandpiles',
985
986
'sage.sets',
987
988
'sage.stats',
989
990
'sage.stats.hmm',
991
992
'sage.symbolic',
993
'sage.symbolic.integration',
994
995
'sage.parallel',
996
997
'sage.schemes',
998
'sage.schemes.generic',
999
'sage.schemes.jacobians',
1000
'sage.schemes.plane_curves',
1001
'sage.schemes.plane_conics',
1002
'sage.schemes.plane_quartics',
1003
'sage.schemes.elliptic_curves',
1004
'sage.schemes.hyperelliptic_curves',
1005
'sage.schemes.toric',
1006
1007
'sage.server',
1008
'sage.server.simple',
1009
'sage.server.notebook',
1010
'sage.server.notebook.compress',
1011
'sage.server.trac',
1012
1013
'sage.structure',
1014
'sage.structure.proof',
1015
1016
'sage.tensor'
1017
],
1018
scripts = [],
1019
1020
cmdclass = { 'build_ext': sage_build_ext },
1021
1022
ext_modules = ext_modules,
1023
include_dirs = include_dirs)
1024
1025
1026