Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ SAGE_ROOT_LOGS = logs

# CONFIG_FILES lists all files that appear in AC_CONFIG_FILES in configure.ac;
# except for build/make/Makefile-auto, which is unused by the build system
CONFIG_FILES = build/make/Makefile src/bin/sage-env-config build/bin/sage-build-env-config pkgs/sage-conf/_sage_conf/_conf.py
CONFIG_FILES = build/make/Makefile src/bin/sage-env-config build/bin/sage-build-env-config

# SPKG_COLLECT_FILES contains the files that influence the *runtime* of the
# portions of the 'configure' script generated by the SAGE_SPKG_COLLECT macro
Expand Down
2 changes: 0 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -581,8 +581,6 @@ AC_SUBST(SAGE_PKG_CONFIG_PATH, [$SAGE_DARWIN_PKG_CONFIG_PATH'$SAGE_LOCAL/lib/pkg
AC_CONFIG_FILES([build/make/Makefile-auto build/make/Makefile])
AC_CONFIG_FILES([src/bin/sage-env-config src/bin/sage-src-env-config build/bin/sage-build-env-config])

AC_CONFIG_FILES([pkgs/sage-conf/_sage_conf/_conf.py])

dnl Create basic directories needed for Sage
AC_CONFIG_COMMANDS(mkdirs,
[
Expand Down
2 changes: 2 additions & 0 deletions configure_wrapper
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#! /bin/sh
cp conftest.py bak_conftest.py
./real_configure $@
realconf_result=$?
mv bak_conftest.py conftest.py
exit $realconf_result
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -217,5 +217,6 @@ for path in file_paths:
run_command(create_files_command, check: true)

root = meson.current_source_dir()
root_build = meson.current_build_dir()

subdir('src')
69 changes: 0 additions & 69 deletions pkgs/sage-conf/_sage_conf/_conf.py.in

This file was deleted.

149 changes: 149 additions & 0 deletions src/sage/config.py.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# @configure_input@
from pathlib import Path

import sage

VERSION = "@PACKAGE_VERSION@"

# The following must not be used during build to determine source or installation
# location of sagelib. See comments in SAGE_ROOT/src/Makefile.in
# These variables come first so that other substituted variable values can refer
# to it.
SAGE_LOCAL = "@prefix@"
SAGE_ROOT = "@SAGE_ROOT@"
SAGE_SHARE = "@SAGE_SHARE@"

# The semicolon-separated list of GAP root paths. This is the list of
# locations that are searched for GAP packages. This is passed directly
# to GAP via the -l flag.
GAP_ROOT_PATHS = "@GAP_ROOT_PATHS@".replace("${prefix}", SAGE_LOCAL)

# The path to the standalone maxima executable.
MAXIMA = "@SAGE_MAXIMA@".replace("${prefix}", SAGE_LOCAL)

# Set this to the empty string if your ECL can load maxima without
# further prodding.
MAXIMA_FAS = "@SAGE_MAXIMA_FAS@".replace("${prefix}", SAGE_LOCAL)
MAXIMA_SHARE = "@SAGE_MAXIMA_SHARE@".replace("${prefix}", SAGE_LOCAL)

# Delete this line if your ECL can load Kenzo without further prodding.
KENZO_FAS = "@SAGE_KENZO_FAS@".replace("${prefix}", SAGE_LOCAL)

NTL_INCDIR = "@NTL_INCDIR@"
NTL_LIBDIR = "@NTL_LIBDIR@"

# Path to the ecl-config script
ECL_CONFIG = "@SAGE_ECL_CONFIG@".replace("${prefix}", SAGE_LOCAL)

SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@"

SAGE_ECMBIN = "@SAGE_ECMBIN@"

# Names or paths of the 4ti2 executables
FOURTITWO_HILBERT = "@FOURTITWO_HILBERT@"
FOURTITWO_MARKOV = "@FOURTITWO_MARKOV@"
FOURTITWO_GRAVER = "@FOURTITWO_GRAVER@"
FOURTITWO_ZSOLVE = "@FOURTITWO_ZSOLVE@"
FOURTITWO_QSOLVE = "@FOURTITWO_QSOLVE@"
FOURTITWO_RAYS = "@FOURTITWO_RAYS@"
FOURTITWO_PPI = "@FOURTITWO_PPI@"
FOURTITWO_CIRCUITS = "@FOURTITWO_CIRCUITS@"
FOURTITWO_GROEBNER = "@FOURTITWO_GROEBNER@"

# Colon-separated list of pkg-config modules to search for cblas functionality.
# We hard-code it here as cblas because configure (build/pkgs/openblas/spkg-configure.m4)
# always provides cblas.pc, if necessary by creating a facade pc file for a system BLAS.
CBLAS_PC_MODULES = "cblas"

# for sage_setup.setenv
SAGE_ARCHFLAGS = "@SAGE_ARCHFLAGS@"
SAGE_PKG_CONFIG_PATH = "@SAGE_PKG_CONFIG_PATH@".replace("$SAGE_LOCAL", SAGE_LOCAL)

# Used in sage.repl.ipython_kernel.install
MATHJAX_DIR = "@SAGE_MATHJAX_DIR@".replace("${prefix}", SAGE_LOCAL)
THREEJS_DIR = SAGE_LOCAL + "/share/threejs-sage"

# OpenMP flags, if available.
OPENMP_CFLAGS = "@OPENMP_CFLAGS@"
OPENMP_CXXFLAGS = "@OPENMP_CXXFLAGS@"

# Installation location of wheels. This is determined at configuration time
# and does not depend on the installation location of sage-conf.
SAGE_SPKG_WHEELS = (
"@SAGE_VENV@".replace("${SAGE_LOCAL}", SAGE_LOCAL) + "/var/lib/sage/wheels"
)


def is_editable_install() -> bool:
"""
Check whether this is an editable install of Sage.

EXAMPLES::

sage: from sage.config import is_editable_install
sage: is_editable_install()
False
"""
# This function relies on the fact that meson-python sets up a custom
# loader for editable installs
# Alternatively, one could use the distribution metadata as in:
# https://github.com/scientific-python/spin/blob/89e581c7201d0f6597ffc92c3e84894f99fc133b/spin/cmds/meson.py#L39
return type(sage.__loader__).__module__ == "_sagemath_editable_loader"


def get_editable_root() -> tuple[Path, Path] | None:
"""
Return the path to the Sage directory when using an editable
install.
Both the actual source directory and the build directory are returned, and are
guaranteed to exist.
If not using an editable install, or if the source/build directories do not
exist, return None.

EXAMPLES::

sage: from sage.config import get_editable_root
sage: get_editable_root()
(WindowsPath('<path_to_sage>/sage'), WindowsPath('<path_to_sage>/sage/build/cp312'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can this test ever pass outside Windows?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It cannot. Do you run tests on .py.in files? Then we should add a random decoration as below.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normally speaking .in files should not be tested.

Copy link
Contributor

@antonio-rojas antonio-rojas Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I run tests on the installed sage (with python -m sage.doctest -a), where this is already a .py file.

"""
if (
not is_editable_install()
or r"@EDITABLE_SRC@" == ""
or r"@EDITABLE_BUILD@" == ""
):
return None

src = Path(r"@EDITABLE_SRC@").resolve()
build = Path(r"@EDITABLE_BUILD@").resolve()
if src.is_dir() and build.is_dir():
return src, build
return None


def get_include_dirs() -> list[Path]:
"""
Return a list of directories to be used as include directories
when compiling Cython extensions that depend on Sage.

Headers should be included with the prefix "sage/", e.g.,
``#include <sage/cpython/cython_metaclass.h>``.

EXAMPLES::

sage: from sage.config import get_include_dirs
sage: dirs = get_include_dirs()
sage: dirs # random
[
WindowsPath('<python>/site-packages'),
WindowsPath('<path_to_sage>/src'),
WindowsPath('<path_to_sage>/build/cp312/src'),
WindowsPath('<python>/site-packages/numpy/core/include')
]
"""
dirs: list[Path] = [Path(dir).parent for dir in sage.__path__]
editable_root = get_editable_root()
if editable_root is not None:
# We return both the source and build directory,
# because some headers are generated in the build directory.
dirs.extend([root / "src" for root in editable_root])
return [dir for dir in dirs if dir.is_dir()]
14 changes: 14 additions & 0 deletions src/sage/config_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from sage.config import get_include_dirs


def test_cython_metaclass_header_found():
dirs = get_include_dirs()
assert any(
(dir / "sage" / "cpython" / "cython_metaclass.h").is_file() for dir in dirs
)


def test_get_include_dirs_returns_existing_dirs():
dirs = get_include_dirs()
for dir in dirs:
assert dir.is_dir(), f"Directory {dir} does not exist"
9 changes: 9 additions & 0 deletions src/sage/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
....: cmd += f"s1 = samefile(SAGE_ROOT, '{SAGE_ROOT}');"
sage: cmd += f"s2 = samefile(SAGE_LOCAL, '{SAGE_LOCAL}');"
sage: cmd += "print(s1 and s2);"
sage: out = check_output([sys.executable, "-c", cmd], env=env).decode().strip() # long time

Check failure on line 22 in src/sage/env.py

View workflow job for this annotation

GitHub Actions / Conda (ubuntu, Python 3.12, new)

Failed example:

Failed example:: Exception raised: Traceback (most recent call last): File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/doctest/forker.py", line 733, in _run self.compile_and_execute(example, compiler, test.globs) File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/doctest/forker.py", line 1157, in compile_and_execute exec(compiled, globs) File "<doctest sage.env[8]>", line 1, in <module> out = check_output([sys.executable, "-c", cmd], env=env).decode().strip() # long time ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/subprocess.py", line 466, in check_output return run(*popenargs, stdout=PIPE, timeout=timeout, check=True, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/subprocess.py", line 571, in run raise CalledProcessError(retcode, process.args, subprocess.CalledProcessError: Command '['/usr/share/miniconda/envs/sage-dev/bin/python3', '-c', "from sage.all import SAGE_ROOT, SAGE_LOCAL;from os.path import samefile;s1 = samefile(SAGE_ROOT, '/home/runner/work/sage/sage');s2 = samefile(SAGE_LOCAL, '/usr/share/miniconda/envs/sage-dev');print(s1 and s2);"]' returned non-zero exit status 1.
sage: out == "True" # long time

Check failure on line 23 in src/sage/env.py

View workflow job for this annotation

GitHub Actions / Conda (ubuntu, Python 3.12, new)

Failed example:

Failed example:: Exception raised: Traceback (most recent call last): File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/doctest/forker.py", line 733, in _run self.compile_and_execute(example, compiler, test.globs) File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/doctest/forker.py", line 1157, in compile_and_execute exec(compiled, globs) File "<doctest sage.env[9]>", line 1, in <module> out == "True" # long time ^^^ NameError: name 'out' is not defined
True

AUTHORS:
Expand Down Expand Up @@ -49,6 +49,7 @@
from platformdirs import site_data_dir, user_data_dir

from sage import version
from sage.config import get_include_dirs

# All variables set by var() appear in this SAGE_ENV dict
SAGE_ENV = dict()
Expand Down Expand Up @@ -307,6 +308,9 @@

sage: import sage.env
sage: sage.env.sage_include_directories()
doctest:warning...
DeprecationWarning: use sage.config.get_include_dirs() instead
...
['...',
'.../numpy/...core/include',
'.../include/python...']
Expand All @@ -327,6 +331,9 @@
sage: any(os.path.isfile(os.path.join(d, file)) for d in dirs)
True
"""
from sage.misc.superseded import deprecation
deprecation(40765, 'use sage.config.get_include_dirs() instead')

if use_sources:
dirs = [SAGE_SRC]
else:
Expand All @@ -341,6 +348,8 @@

dirs.append(sysconfig.get_config_var('INCLUDEPY'))

dirs.extend([dir.as_posix() for dir in get_include_dirs()])

return dirs


Expand Down
6 changes: 3 additions & 3 deletions src/sage/features/meson_editable.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
r"""
Feature for testing if Meson editable install is used.
"""
from sage.config import is_editable_install

from . import Feature, FeatureTestResult


Expand Down Expand Up @@ -35,9 +37,7 @@ def _is_present(self):
sage: MesonEditable()._is_present() # random
FeatureTestResult('meson_editable', True)
"""
import sage
result = type(sage.__loader__).__module__ == '_sagemath_editable_loader'
return FeatureTestResult(self, result)
return FeatureTestResult(self, is_editable_install())


def all_features():
Expand Down
5 changes: 4 additions & 1 deletion src/sage/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ sage_install_dir = py.get_install_dir() / 'sage'

# Generate the configuration file
conf_data = configuration_data()
conf_data.set('EDITABLE_SRC', root)
conf_data.set('EDITABLE_BUILD', root_build)
conf_data.set('PACKAGE_VERSION', '1.2.3')
# We use Python's prefix here to make it work with conda
prefix = fs.as_posix(py.get_variable('prefix', ''))
Expand Down Expand Up @@ -92,6 +94,7 @@ py.install_sources(
'all__sagemath_sirocco.py',
'all__sagemath_tdlib.py',
'all_cmdline.py',
'config_test.py',
'env.py',
'version.py',
subdir: 'sage',
Expand Down Expand Up @@ -156,7 +159,7 @@ configure_file(
# Write config file
# Should be last so that subdir calls can modify the config data
config_file = configure_file(
input: '../../pkgs/sage-conf/_sage_conf/_conf.py.in',
input: 'config.py.in',
output: 'config.py',
install_dir: sage_install_dir,
install: true,
Expand Down
5 changes: 3 additions & 2 deletions src/sage/misc/cython.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
import webbrowser
from pathlib import Path

from sage.env import SAGE_LOCAL, SAGE_SRC, cython_aliases, sage_include_directories
from sage.config import get_include_dirs
from sage.env import SAGE_LOCAL, cython_aliases
from sage.misc.cachefunc import cached_function
from sage.misc.sage_ostools import redirection, restore_cwd
from sage.misc.temporary_file import spyx_tmp, tmp_filename
Expand Down Expand Up @@ -58,7 +59,7 @@ def _standard_libs_libdirs_incdirs_aliases():
if SAGE_LOCAL:
standard_libdirs.append(os.path.join(SAGE_LOCAL, "lib"))
standard_libdirs.extend(aliases["CBLAS_LIBDIR"] + aliases["NTL_LIBDIR"])
standard_incdirs = sage_include_directories(use_sources=True) + [SAGE_SRC] + aliases["CBLAS_INCDIR"] + aliases["NTL_INCDIR"]
standard_incdirs = [dir.as_posix() for dir in get_include_dirs()] + aliases["CBLAS_INCDIR"] + aliases["NTL_INCDIR"]
return standard_libs, standard_libdirs, standard_incdirs, aliases

################################################################
Expand Down
Loading