Skip to content

Commit f8a01d1

Browse files
committed
minimal changes to provide cython/C headers
1 parent 14abfbd commit f8a01d1

File tree

4 files changed

+168
-1
lines changed

4 files changed

+168
-1
lines changed

meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,5 +217,6 @@ for path in file_paths:
217217
run_command(create_files_command, check: true)
218218

219219
root = meson.current_source_dir()
220+
root_build = meson.current_build_dir()
220221

221222
subdir('src')

src/sage/config.py.in

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# @configure_input@
2+
from pathlib import Path
3+
4+
import sage
5+
6+
VERSION = "@PACKAGE_VERSION@"
7+
8+
# The following must not be used during build to determine source or installation
9+
# location of sagelib. See comments in SAGE_ROOT/src/Makefile.in
10+
# These variables come first so that other substituted variable values can refer
11+
# to it.
12+
SAGE_LOCAL = "@prefix@"
13+
SAGE_ROOT = "@SAGE_ROOT@"
14+
SAGE_SHARE = "@SAGE_SHARE@"
15+
16+
# The semicolon-separated list of GAP root paths. This is the list of
17+
# locations that are searched for GAP packages. This is passed directly
18+
# to GAP via the -l flag.
19+
GAP_ROOT_PATHS = "@GAP_ROOT_PATHS@".replace("${prefix}", SAGE_LOCAL)
20+
21+
# The path to the standalone maxima executable.
22+
MAXIMA = "@SAGE_MAXIMA@".replace("${prefix}", SAGE_LOCAL)
23+
24+
# Set this to the empty string if your ECL can load maxima without
25+
# further prodding.
26+
MAXIMA_FAS = "@SAGE_MAXIMA_FAS@".replace("${prefix}", SAGE_LOCAL)
27+
MAXIMA_SHARE = "@SAGE_MAXIMA_SHARE@".replace("${prefix}", SAGE_LOCAL)
28+
29+
# Delete this line if your ECL can load Kenzo without further prodding.
30+
KENZO_FAS = "@SAGE_KENZO_FAS@".replace("${prefix}", SAGE_LOCAL)
31+
32+
NTL_INCDIR = "@NTL_INCDIR@"
33+
NTL_LIBDIR = "@NTL_LIBDIR@"
34+
35+
# Path to the ecl-config script
36+
ECL_CONFIG = "@SAGE_ECL_CONFIG@".replace("${prefix}", SAGE_LOCAL)
37+
38+
SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@"
39+
40+
SAGE_ECMBIN = "@SAGE_ECMBIN@"
41+
42+
# Names or paths of the 4ti2 executables
43+
FOURTITWO_HILBERT = "@FOURTITWO_HILBERT@"
44+
FOURTITWO_MARKOV = "@FOURTITWO_MARKOV@"
45+
FOURTITWO_GRAVER = "@FOURTITWO_GRAVER@"
46+
FOURTITWO_ZSOLVE = "@FOURTITWO_ZSOLVE@"
47+
FOURTITWO_QSOLVE = "@FOURTITWO_QSOLVE@"
48+
FOURTITWO_RAYS = "@FOURTITWO_RAYS@"
49+
FOURTITWO_PPI = "@FOURTITWO_PPI@"
50+
FOURTITWO_CIRCUITS = "@FOURTITWO_CIRCUITS@"
51+
FOURTITWO_GROEBNER = "@FOURTITWO_GROEBNER@"
52+
53+
# Colon-separated list of pkg-config modules to search for cblas functionality.
54+
# We hard-code it here as cblas because configure (build/pkgs/openblas/spkg-configure.m4)
55+
# always provides cblas.pc, if necessary by creating a facade pc file for a system BLAS.
56+
CBLAS_PC_MODULES = "cblas"
57+
58+
# for sage_setup.setenv
59+
SAGE_ARCHFLAGS = "@SAGE_ARCHFLAGS@"
60+
SAGE_PKG_CONFIG_PATH = "@SAGE_PKG_CONFIG_PATH@".replace("$SAGE_LOCAL", SAGE_LOCAL)
61+
62+
# Used in sage.repl.ipython_kernel.install
63+
MATHJAX_DIR = "@SAGE_MATHJAX_DIR@".replace("${prefix}", SAGE_LOCAL)
64+
THREEJS_DIR = SAGE_LOCAL + "/share/threejs-sage"
65+
66+
# OpenMP flags, if available.
67+
OPENMP_CFLAGS = "@OPENMP_CFLAGS@"
68+
OPENMP_CXXFLAGS = "@OPENMP_CXXFLAGS@"
69+
70+
# Installation location of wheels. This is determined at configuration time
71+
# and does not depend on the installation location of sage-conf.
72+
SAGE_SPKG_WHEELS = (
73+
"@SAGE_VENV@".replace("${SAGE_LOCAL}", SAGE_LOCAL) + "/var/lib/sage/wheels"
74+
)
75+
76+
77+
def is_editable_install() -> bool:
78+
"""
79+
Check whether this is an editable install of Sage.
80+
81+
EXAMPLES::
82+
83+
sage: from sage.config import is_editable_install
84+
sage: is_editable_install()
85+
False
86+
"""
87+
# This function relies on the fact that meson-python sets up a custom
88+
# loader for editable installs
89+
# Alternatively, one could use the distribution metadata as in:
90+
# https://github.com/scientific-python/spin/blob/89e581c7201d0f6597ffc92c3e84894f99fc133b/spin/cmds/meson.py#L39
91+
return type(sage.__loader__).__module__ == "_sagemath_editable_loader"
92+
93+
94+
def get_editable_root() -> tuple[Path, Path] | None:
95+
"""
96+
Return the path to the Sage directory when using an editable
97+
install.
98+
Both the actual source directory and the build directory are returned, and are
99+
guaranteed to exist.
100+
If not using an editable install, or if the source/build directories do not
101+
exist, return None.
102+
103+
EXAMPLES::
104+
105+
sage: from sage.config import get_editable_root
106+
sage: get_editable_root()
107+
(WindowsPath('<path_to_sage>/sage'), WindowsPath('<path_to_sage>/sage/build/cp312'))
108+
"""
109+
if (
110+
not is_editable_install()
111+
or r"@EDITABLE_SRC@" == ""
112+
or r"@EDITABLE_BUILD@" == ""
113+
):
114+
return None
115+
116+
src = Path(r"@EDITABLE_SRC@").resolve()
117+
build = Path(r"@EDITABLE_BUILD@").resolve()
118+
if src.is_dir() and build.is_dir():
119+
return src, build
120+
return None
121+
122+
123+
def get_include_dirs() -> list[Path]:
124+
"""
125+
Return a list of directories to be used as include directories
126+
when compiling Cython extensions that depend on Sage.
127+
128+
Headers should be included with the prefix "sage/", e.g.,
129+
``#include <sage/cpython/cython_metaclass.h>``.
130+
131+
EXAMPLES::
132+
133+
sage: from sage.config import get_include_dirs
134+
sage: dirs = get_include_dirs()
135+
sage: dirs # random
136+
[
137+
WindowsPath('<python>/site-packages'),
138+
WindowsPath('<path_to_sage>/src'),
139+
WindowsPath('<path_to_sage>/build/cp312/src'),
140+
WindowsPath('<python>/site-packages/numpy/core/include')
141+
]
142+
"""
143+
dirs: list[Path] = [Path(dir).parent for dir in sage.__path__]
144+
editable_root = get_editable_root()
145+
if editable_root is not None:
146+
# We return both the source and build directory,
147+
# because some headers are generated in the build directory.
148+
dirs.extend([root / "src" for root in editable_root])
149+
return [dir for dir in dirs if dir.is_dir()]

src/sage/config_test.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from sage.config import get_include_dirs
2+
3+
4+
def test_cython_metaclass_header_found():
5+
dirs = get_include_dirs()
6+
assert any(
7+
(dir / "sage" / "cpython" / "cython_metaclass.h").is_file() for dir in dirs
8+
)
9+
10+
11+
def test_get_include_dirs_returns_existing_dirs():
12+
dirs = get_include_dirs()
13+
for dir in dirs:
14+
assert dir.is_dir(), f"Directory {dir} does not exist"

src/sage/meson.build

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ sage_install_dir = py.get_install_dir() / 'sage'
22

33
# Generate the configuration file
44
conf_data = configuration_data()
5+
conf_data.set('EDITABLE_SRC', root)
6+
conf_data.set('EDITABLE_BUILD', root_build)
57
conf_data.set('PACKAGE_VERSION', '1.2.3')
68
# We use Python's prefix here to make it work with conda
79
prefix = fs.as_posix(py.get_variable('prefix', ''))
@@ -83,6 +85,7 @@ py.install_sources(
8385
'__init__.py',
8486
'all.py',
8587
'all_cmdline.py',
88+
'config_test.py',
8689
'env.py',
8790
'version.py',
8891
subdir: 'sage',
@@ -147,7 +150,7 @@ configure_file(
147150
# Write config file
148151
# Should be last so that subdir calls can modify the config data
149152
config_file = configure_file(
150-
input: '../../pkgs/sage-conf/_sage_conf/_conf.py.in',
153+
input: 'config.py.in',
151154
output: 'config.py',
152155
install_dir: sage_install_dir,
153156
install: true,

0 commit comments

Comments
 (0)