import os
import sys
import shutil
import sysconfig
import platform
import fnmatch
from setuptools import setup
from setuptools.dist import Distribution
from distutils.command.build_scripts import build_scripts as distutils_build_scripts
from setuptools.command.build_py import build_py as setuptools_build_py
from setuptools.command.editable_wheel import editable_wheel as setuptools_editable_wheel
from setuptools.errors import SetupError
Distribution._removed = staticmethod(lambda ep: True)
class build_py(setuptools_build_py):
def run(self):
HERE = os.path.dirname(__file__)
if self.editable_mode:
SAGE_ROOT = os.path.join(HERE, 'sage_root')
else:
SAGE_ROOT = self._create_writable_sage_root()
if os.environ.get('CONDA_PREFIX', ''):
SETENV = ':'
else:
SETENV = '. ./.homebrew-build-env 2> /dev/null'
SAGE_LOCAL = os.path.join(SAGE_ROOT, 'local')
if os.path.exists(os.path.join(SAGE_ROOT, 'config.status')):
print(f'Reusing configured SAGE_ROOT={SAGE_ROOT}')
else:
cmd = f"cd {SAGE_ROOT} && ({SETENV}; ./configure --prefix={SAGE_LOCAL} --with-python={sys.executable} --enable-build-as-root --enable-download-from-upstream-url --with-system-python3=force --with-sage-venv --disable-notebook --disable-sagelib --disable-sage_conf --disable-doc)"
print(f"Running {cmd}")
sys.stdout.flush()
if os.system(cmd) != 0:
print("configure failed; this may be caused by missing build prerequisites.")
sys.stdout.flush()
PREREQ_SPKG = "_prereq bzip2 xz libffi"
os.system(f'cd {SAGE_ROOT} && export SYSTEM=$(build/bin/sage-guess-package-system 2>/dev/null) && export PACKAGES="$(build/bin/sage-get-system-packages $SYSTEM {PREREQ_SPKG})" && [ -n "$PACKAGES" ] && echo "You can install the required build prerequisites using the following shell command" && echo "" && build/bin/sage-print-system-package-command $SYSTEM --verbose --sudo install $PACKAGES && echo ""')
raise SetupError("configure failed")
if self.editable_mode:
pass
else:
shutil.copyfile(os.path.join(SAGE_ROOT, 'pkgs', 'sage-conf', '_sage_conf', '_conf.py'),
os.path.join(HERE, '_sage_conf', '_conf.py'))
shutil.copyfile(os.path.join(SAGE_ROOT, 'src', 'bin', 'sage-env-config'),
os.path.join(HERE, 'bin', 'sage-env-config'))
SETMAKE = 'if [ -z "$MAKE" ]; then export MAKE="make -j$(PATH=build/bin:$PATH build/bin/sage-build-num-threads | cut -d" " -f 2)"; fi'
TARGETS = 'build'
cmd = f'cd {SAGE_ROOT} && ({SETENV}; {SETMAKE} && $MAKE V=0 ${{SAGE_CONF_TARGETS-{TARGETS}}})'
print(f"Running {cmd}", flush=True)
if os.system(cmd) != 0:
raise SetupError(f"make ${{SAGE_CONF_TARGETS-{TARGETS}}} failed")
setuptools_build_py.run(self)
def _create_writable_sage_root(self):
HERE = os.path.dirname(__file__)
DOT_SAGE = os.environ.get('DOT_SAGE', os.path.join(os.environ.get('HOME'), '.sage'))
with open(os.path.join(HERE, 'VERSION.txt')) as f:
sage_version = f.read().strip()
system = platform.system()
machine = platform.machine()
arch_tag = f'{system}-{machine}'
SAGE_ROOT = os.path.join(DOT_SAGE, f'sage-{sage_version}-{arch_tag}')
def ignore(path, names):
if fnmatch.fnmatch(path, '*/build/pkgs/*'):
return ['src']
return [name for name in names
if name in ('.tox', '.git', '__pycache__',
'prefix', 'local', 'venv', 'upstream',
'config.status', 'config.log', 'logs')]
if not os.path.exists(os.path.join(SAGE_ROOT, 'config.status')):
try:
shutil.copytree('sage_root', SAGE_ROOT,
ignore=ignore)
except Exception as e:
raise SetupError(f"the directory SAGE_ROOT={SAGE_ROOT} already exists but it is not configured ({e}). "
"Please either remove it and try again, or install in editable mode (pip install -e).")
return SAGE_ROOT
class build_scripts(distutils_build_scripts):
def run(self):
self.distribution.scripts.append(os.path.join('bin', 'sage-env-config'))
if not self.distribution.entry_points:
self.entry_points = self.distribution.entry_points = dict()
distutils_build_scripts.run(self)
class editable_wheel(setuptools_editable_wheel):
r"""
Customized so that exceptions raised by our build_py
do not lead to the "Customization incompatible with editable install" message
"""
_safely_run = setuptools_editable_wheel.run_command
setup(
cmdclass=dict(build_py=build_py,
build_scripts=build_scripts,
editable_wheel=editable_wheel)
)