import os, sys, time, errno, platform, subprocess
from distutils.core import setup
from distutils.extension import Extension
from glob import glob, fnmatch
from warnings import warn
from module_list import ext_modules
import sage.ext.gen_interpreters
import warnings
from sage.env import *
if len(sys.argv) > 1 and sys.argv[1] == "sdist":
sdist = True
else:
sdist = False
try:
compile_result_dir = os.environ['XML_RESULTS']
keep_going = True
except KeyError:
compile_result_dir = None
keep_going = False
SAGE_INC = os.path.join(SAGE_LOCAL,'include')
include_dirs = [SAGE_INC,
os.path.join(SAGE_INC, 'csage'),
SAGE_SRC,
os.path.join(SAGE_SRC, 'sage', 'ext')]
extra_include_dirs = [ os.path.join(SAGE_INC,'python'+platform.python_version().rsplit('.', 1)[0]) ]
extra_compile_args = [ "-fno-strict-aliasing" ]
extra_link_args = [ ]
import distutils.sysconfig
NO_WARN = True
if NO_WARN and distutils.sysconfig.get_config_var('CC').startswith("gcc"):
extra_compile_args.append('-w')
DEVEL = False
if DEVEL:
extra_compile_args.append('-ggdb')
if subprocess.call("""$CC --version | grep -i 'gcc.* 4[.][89]' >/dev/null """, shell=True) == 0:
extra_compile_args.append('-fno-tree-dominator-opts')
sage.ext.gen_interpreters.rebuild(os.path.join(SAGE_SRC, 'sage', 'ext', 'interpreters'))
ext_modules = ext_modules + sage.ext.gen_interpreters.modules
class CompileRecorder(object):
def __init__(self, f):
self._f = f
self._obj = None
def __get__(self, obj, type=None):
self._obj = obj
return self
def __call__(self, *args):
t = time.time()
try:
if self._obj:
res = self._f(self._obj, *args)
else:
res = self._f(*args)
except Exception, ex:
print ex
res = ex
t = time.time() - t
errors = failures = 0
if self._f is compile_command0:
name = "cythonize." + args[0][1].name
failures = int(bool(res))
else:
name = "gcc." + args[0][1].name
errors = int(bool(res))
if errors or failures:
type = "failure" if failures else "error"
failure_item = """<%(type)s/>""" % locals()
else:
failure_item = ""
output = open("%s/%s.xml" % (compile_result_dir, name), "w")
output.write("""
<?xml version="1.0" ?>
<testsuite name="%(name)s" errors="%(errors)s" failures="%(failures)s" tests="1" time="%(t)s">
<testcase classname="%(name)s" name="compile">
%(failure_item)s
</testcase>
</testsuite>
""".strip() % locals())
output.close()
return res
if compile_result_dir:
record_compile = CompileRecorder
else:
record_compile = lambda x: x
import sage.misc.lazy_import_cache
if os.path.exists(sage.misc.lazy_import_cache.get_cache_file()):
os.unlink(sage.misc.lazy_import_cache.get_cache_file())
lib_headers = { "gmp": [ os.path.join(SAGE_INC,'gmp.h') ],
"gmpxx": [ os.path.join(SAGE_INC,'gmpxx.h') ]
}
for m in ext_modules:
for lib in lib_headers.keys():
if lib in m.libraries:
m.depends += lib_headers[lib]
m.libraries = ['csage'] + m.libraries + ['stdc++', 'ntl']
m.extra_compile_args += extra_compile_args
m.extra_link_args += extra_link_args
m.library_dirs += ['%s/lib' % SAGE_LOCAL]
def run_command(cmd):
"""
INPUT:
cmd -- a string; a command to run
OUTPUT:
prints cmd to the console and then runs os.system
"""
print cmd
return os.system(cmd)
def apply_pair(p):
"""
Given a pair p consisting of a function and a value, apply
the function to the value.
This exists solely because we can't pickle an anonymous function
in execute_list_of_commands_in_parallel below.
"""
return p[0](p[1])
def execute_list_of_commands_in_parallel(command_list, nthreads):
"""
Execute the given list of commands, possibly in parallel, using
``nthreads`` threads. Terminates ``setup.py`` with an exit code
of 1 if an error occurs in any subcommand.
INPUT:
- ``command_list`` -- a list of commands, each given as a pair of
the form ``[function, argument]`` of a function to call and its
argument
- ``nthreads`` -- integer; number of threads to use
WARNING: commands are run roughly in order, but of course successive
commands may be run at the same time.
"""
from multiprocessing import Pool
import fpickle_setup
p = Pool(nthreads)
process_command_results(p.imap(apply_pair, command_list))
def process_command_results(result_values):
error = None
for r in result_values:
if r:
print "Error running command, failed with status %s."%r
if not keep_going:
sys.exit(1)
error = r
if error:
sys.exit(1)
def execute_list_of_commands(command_list):
"""
INPUT:
- ``command_list`` -- a list of strings or pairs
OUTPUT:
For each entry in command_list, we attempt to run the command.
If it is a string, we call ``os.system()``. If it is a pair [f, v],
we call f(v).
If the environment variable :envvar:`SAGE_NUM_THREADS` is set, use
that many threads.
"""
t = time.time()
try:
nthreads = int(os.environ['SAGE_NUM_THREADS'])
except KeyError:
nthreads = 1
command_list = [ [run_command, x] if isinstance(x, str) else x for x in command_list ]
nthreads = min(len(command_list), nthreads)
nthreads = max(1, nthreads)
def plural(n,noun):
if n == 1:
return "1 %s"%noun
return "%i %ss"%(n,noun)
print "Executing %s (using %s)"%(plural(len(command_list),"command"), plural(nthreads,"thread"))
execute_list_of_commands_in_parallel(command_list, nthreads)
print "Time to execute %s: %s seconds"%(plural(len(command_list),"command"), time.time() - t)
from distutils.command.build_ext import build_ext
from distutils.dep_util import newer_group
from types import ListType, TupleType
from distutils import log
class sage_build_ext(build_ext):
def build_extensions(self):
from distutils.debug import DEBUG
if DEBUG:
print "self.compiler.compiler:"
print self.compiler.compiler
print "self.compiler.compiler_cxx:"
print self.compiler.compiler_cxx
print "self.compiler.compiler_so:"
print self.compiler.compiler_so
print "self.compiler.linker_so:"
print self.compiler.linker_so
sys.stdout.flush()
if True or sys.platform[:6]=="darwin":
sage_libdir = os.path.realpath(SAGE_LOCAL+"/lib")
ldso_cmd = self.compiler.linker_so
for i in range(1, len(ldso_cmd)):
if ldso_cmd[i][:2] == "-L":
libdir = os.path.realpath(ldso_cmd[i][2:])
self.debug_print(
"Library dir found in dynamic linker command: " +
"\"%s\"" % libdir)
if libdir != sage_libdir:
self.compiler.warn(
"Replacing library search directory in linker " +
"command:\n \"%s\" -> \"%s\"\n" % (libdir,
sage_libdir))
ldso_cmd[i] = "-L"+sage_libdir
if DEBUG:
print "self.compiler.linker_so (after fixing library dirs):"
print self.compiler.linker_so
sys.stdout.flush()
self.check_extensions_list(self.extensions)
import time
t = time.time()
compile_commands = []
for ext in self.extensions:
need_to_compile, p = self.prepare_extension(ext)
if need_to_compile:
compile_commands.append((record_compile(self.build_extension), p))
execute_list_of_commands(compile_commands)
print "Total time spent compiling C/C++ extensions: ", time.time() - t, "seconds."
def prepare_extension(self, ext):
sources = ext.sources
if sources is None or type(sources) not in (ListType, TupleType):
raise DistutilsSetupError, \
("in 'ext_modules' option (extension '%s'), " +
"'sources' must be present and must be " +
"a list of source filenames") % ext.name
sources = list(sources)
fullname = self.get_ext_fullname(ext.name)
if self.inplace:
modpath = string.split(fullname, '.')
package = string.join(modpath[0:-1], '.')
base = modpath[-1]
build_py = self.get_finalized_command('build_py')
package_dir = build_py.get_package_dir(package)
ext_filename = os.path.join(package_dir,
self.get_ext_filename(base))
relative_ext_filename = self.get_ext_filename(base)
else:
ext_filename = os.path.join(self.build_lib,
self.get_ext_filename(fullname))
relative_ext_filename = self.get_ext_filename(fullname)
relative_ext_dir = os.path.split(relative_ext_filename)[0]
prefixes = ['', self.build_lib, self.build_temp]
for prefix in prefixes:
path = os.path.join(prefix, relative_ext_dir)
try:
os.makedirs(path)
except OSError, e:
assert e.errno==errno.EEXIST, 'Cannot create %s.' % path
depends = sources + ext.depends
if not (self.force or newer_group(depends, ext_filename, 'newer')):
log.debug("skipping '%s' extension (up-to-date)", ext.name)
need_to_compile = False
else:
log.info("building '%s' extension", ext.name)
need_to_compile = True
return need_to_compile, (sources, ext, ext_filename)
def build_extension(self, p):
sources, ext, ext_filename = p
sources = self.swig_sources(sources, ext)
extra_args = ext.extra_compile_args or []
macros = ext.define_macros[:]
for undef in ext.undef_macros:
macros.append((undef,))
objects = self.compiler.compile(sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=ext.include_dirs,
debug=self.debug,
extra_postargs=extra_args,
depends=ext.depends)
self._built_objects = objects[:]
if ext.extra_objects:
objects.extend(ext.extra_objects)
extra_args = ext.extra_link_args or []
language = ext.language or self.compiler.detect_language(sources)
self.compiler.link_shared_object(
objects, ext_filename,
libraries=self.get_libraries(ext),
library_dirs=ext.library_dirs,
runtime_library_dirs=ext.runtime_library_dirs,
extra_postargs=extra_args,
export_symbols=self.get_export_symbols(ext),
debug=self.debug,
build_temp=self.build_temp,
target_lang=language)
if not sdist:
print "Updating Cython code...."
t = time.time()
from Cython.Build import cythonize
import Cython.Compiler.Options
import Cython.Compiler.Main
Cython.Compiler.Options.embed_pos_in_docstring = True
Cython.Compiler.Options.directive_defaults['autotestdict'] = False
Cython.Compiler.Options.directive_defaults['cdivision'] = True
Cython.Compiler.Options.directive_defaults['fast_getattr'] = True
Cython.Compiler.Options.old_style_globals = True
if os.environ.get('SAGE_DEBUG', None) != 'no':
Cython.Compiler.Main.default_options['gdb_debug'] = True
Cython.Compiler.Main.default_options['output_dir'] = 'build'
CYCACHE_DIR = os.environ.get('CYCACHE_DIR', os.path.join(DOT_SAGE,'cycache'))
if os.path.exists(os.path.join(CYCACHE_DIR, os.pardir)):
Cython.Compiler.Main.default_options['cache'] = CYCACHE_DIR
force = True
version_file = os.path.join(os.path.dirname(__file__), '.cython_version')
if os.path.exists(version_file) and open(version_file).read() == Cython.__version__:
force = False
for ext_module in ext_modules:
ext_module.include_dirs += include_dirs
ext_modules = cythonize(
ext_modules,
nthreads = int(os.environ.get('SAGE_NUM_THREADS', 0)),
build_dir = None,
force=force)
open(version_file, 'w').write(Cython.__version__)
print "Finished compiling Cython code (time = %s seconds)" % (time.time() - t)
sys.stdout.flush()
def python_packages():
packages = []
root = os.path.join(os.path.dirname(__file__))
for dirpath, dirnames, filenames in os.walk(os.path.join(root, 'sage')):
if '__init__.py' in filenames:
packages.append(dirpath.replace(os.path.sep, '.'))
return packages
code = setup(name = 'sage',
version = SAGE_VERSION,
description = 'Sage: Open Source Mathematics Software',
license = 'GNU Public License (GPL)',
author = 'William Stein et al.',
author_email= 'http://groups.google.com/group/sage-support',
url = 'http://www.sagemath.org',
packages = python_packages(),
scripts = [],
cmdclass = { 'build_ext': sage_build_ext },
ext_modules = ext_modules,
include_dirs = include_dirs)