Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download
Views: 184607
1
#################################################################################
2
#
3
# (c) Copyright 2010 William Stein
4
#
5
# This file is part of PSAGE
6
#
7
# PSAGE is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# PSAGE is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
#
20
#################################################################################
21
22
23
# A setuptools-based build system. See the comment before the line
24
# "build_system.cythonize(ext_modules)" in setup.py about how this
25
# code can probably be replaced by code in Cython soon.
26
27
import os, sys
28
from setuptools import setup
29
import setuptools
30
31
def time_stamp(filename):
32
try:
33
return os.path.getmtime(filename)
34
except OSError, msg:
35
print msg
36
return 0
37
38
def cython(f_pyx, language, include_dirs, force):
39
assert f_pyx.endswith('.pyx')
40
# output filename
41
dir, f = os.path.split(f_pyx)
42
ext = 'cpp' if language == 'c++' else 'c'
43
outfile = os.path.splitext(f)[0] + '.' + ext
44
full_outfile = dir + '/' + outfile
45
if not force:
46
if os.path.exists(full_outfile) and time_stamp(f_pyx) <= time_stamp(full_outfile):
47
# Already compiled
48
return full_outfile, []
49
include_dirs.append("%s/src/sage/ext/"%os.environ['SAGE_ROOT'])
50
include_dirs.append("%s/src/"%os.environ['SAGE_ROOT'])
51
includes = ''.join(["-I '%s' "%x for x in include_dirs])
52
# call cython
53
cmd = "cd %s && python `which cython` --embed-positions --directive cdivision=False %s -o %s %s"%(
54
dir, includes, outfile, f)
55
return full_outfile, [cmd]
56
57
class Extension(setuptools.Extension):
58
def __init__(self, module, sources, include_dirs,
59
language="c", force=False, **kwds):
60
self.cython_cmds = []
61
for i in range(len(sources)):
62
f = sources[i]
63
if f.endswith('.pyx'):
64
sources[i], cmds = cython(f, language, include_dirs, force)
65
for c in cmds:
66
self.cython_cmds.append(c)
67
setuptools.Extension.__init__(self, module, sources, language=language,
68
include_dirs=include_dirs, **kwds)
69
70
def apply_pair(p):
71
"""
72
Given a pair p consisting of a function and a value, apply
73
the function to the value.
74
75
This exists solely because we can't pickle an anonymous function
76
in execute_list_of_commands_in_parallel below.
77
"""
78
return p[0](p[1])
79
80
def execute_list_of_commands_in_parallel(command_list, nthreads):
81
"""
82
INPUT:
83
command_list -- a list of pairs, consisting of a
84
function to call and its argument
85
nthreads -- integer; number of threads to use
86
87
OUTPUT:
88
Executes the given list of commands, possibly in parallel,
89
using nthreads threads. Terminates setup.py with an exit code of 1
90
if an error occurs in any subcommand.
91
92
WARNING: commands are run roughly in order, but of course successive
93
commands may be run at the same time.
94
"""
95
print "Execute %s commands (using %s threads)"%(len(command_list), min(len(command_list),nthreads))
96
from multiprocessing import Pool
97
p = Pool(nthreads)
98
print command_list
99
for r in p.imap(apply_pair, command_list):
100
if r:
101
print "Parallel build failed with status %s."%r
102
sys.exit(1)
103
104
def number_of_threads():
105
"""
106
Try to determine the number of threads one can run at once on this
107
system (e.g., the number of cores). If successful return that
108
number. Otherwise return 0 to indicate failure.
109
110
OUTPUT:
111
int
112
"""
113
if hasattr(os, "sysconf") and os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"): # Linux and Unix
114
n = os.sysconf("SC_NPROCESSORS_ONLN")
115
if isinstance(n, int) and n > 0:
116
return n
117
try:
118
return int(os.popen2("sysctl -n hw.ncpu")[1].read().strip())
119
except:
120
return 0
121
122
def execute_list_of_commands_in_serial(command_list):
123
"""
124
INPUT:
125
command_list -- a list of commands, each given as a pair
126
of the form [command, argument].
127
128
OUTPUT:
129
the given list of commands are all executed in serial
130
"""
131
for f,v in command_list:
132
r = f(v)
133
if r != 0:
134
print "Error running command, failed with status %s."%r
135
sys.exit(1)
136
137
def cythonize(ext_modules):
138
cmds = sum([E.cython_cmds for E in ext_modules], [])
139
cmds = [(os.system, c) for c in cmds]
140
n = number_of_threads()
141
if n == 1:
142
execute_list_of_commands_in_serial(cmds)
143
else:
144
execute_list_of_commands_in_parallel(cmds, n)
145
146
147