diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index daee66f990f..c88b3e4da37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,18 +3,18 @@ name: CI Linux on: push: tags: - - '*' + - "*" pull_request: paths: - - '**.build' - - 'uv.lock' - - 'subprojects/**' - - '.github/workflows/ci.yml' + - "**.build" + - "uv.lock" + - "subprojects/**" + - ".github/workflows/ci.yml" workflow_dispatch: # Allow to run manually jobs: - build: + linux: name: Build and Test runs-on: ubuntu-latest strategy: @@ -112,10 +112,10 @@ jobs: id: clear-container-name if: failure() || success() run: | - # Output the container name, but with ":" and "/" replaced by "-" - # This is needed to avoid issues with the upload-artifact action - CONTAINER_NAME=$(echo "${{ matrix.container }}" | tr ':/' '--') - echo "CONTAINER_NAME=$CONTAINER_NAME" >> $GITHUB_OUTPUT + # Output the container name, but with ":" and "/" replaced by "-" + # This is needed to avoid issues with the upload-artifact action + CONTAINER_NAME=$(echo "${{ matrix.container }}" | tr ':/' '--') + echo "CONTAINER_NAME=$CONTAINER_NAME" >> $GITHUB_OUTPUT - name: Upload log uses: actions/upload-artifact@v4.5.0 @@ -125,3 +125,76 @@ jobs: path: | build/cp313/meson-logs/ build/cp313/meson-info/ + + mingw: + name: Build and Test (Mingw) + runs-on: windows-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v6.7.0 + + - name: Set up Python + run: | + uv python install + uv venv + .venv\Scripts\activate + echo PATH=$PATH >> $GITHUB_ENV + + - name: Install dependencies + uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + update: true + install: >- + base-devel + mingw-w64-ucrt-x86_64-toolchain + mingw-w64-ucrt-x86_64-ninja + mingw-w64-ucrt-x86_64-gmp + mingw-w64-ucrt-x86_64-openblas + mingw-w64-ucrt-x86_64-m4ri + mingw-w64-ucrt-x86_64-flint + mingw-w64-ucrt-x86_64-mpfi + mingw-w64-ucrt-x86_64-gsl + mingw-w64-ucrt-x86_64-libgd + mingw-w64-ucrt-x86_64-gsl + mingw-w64-ucrt-x86_64-libgd + mingw-w64-ucrt-x86_64-ntl + mingw-w64-ucrt-x86_64-boost + mingw-w64-ucrt-x86_64-nauty + mingw-w64-ucrt-x86_64-libhomfly + mingw-w64-ucrt-x86_64-symmetrica + mingw-w64-ucrt-x86_64-glpk + mingw-w64-ucrt-x86_64-gc + path-type: inherit + + - name: Build + shell: msys2 {0} + run: | + # Install build dependencies manually as workaround for https://github.com/astral-sh/uv/issues/1516 + uv pip install \ + meson-python \ + "cysignals >=1.11.2, != 1.12.0" \ + "cython >=3.0, != 3.0.3, < 3.1.0" \ + "gmpy2 >=2.1.5" \ + memory_allocator \ + "numpy >=1.25" \ + jinja2 \ + setuptools + uv sync --frozen --inexact --no-build-isolation -v --no-editable --config-settings=builddir=builddir + + - name: Test + shell: msys2 {0} + run: | + uv run --frozen ./sage -t --all -p4 || true + + - name: Upload log + uses: actions/upload-artifact@v4.5.0 + if: failure() || success() + with: + name: mingw-log + path: | + build/cp313/meson-logs/ + build/cp313/meson-info/ diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index e049edd0bde..a0bff433779 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -172,6 +172,7 @@ To compile and install Sage in editable install, then just use: $ uv pip install \ meson-python \ "cypari2 >=2.2.1" \ + "cysignals >=1.11.2, != 1.12.0" \ "cython >=3.0, != 3.0.3, != 3.1.0" \ "gmpy2 >=2.1.5" \ memory_allocator \ diff --git a/src/sage/config.py.in b/src/sage/config.py.in index d7ae0db56d4..d38c1a5c828 100644 --- a/src/sage/config.py.in +++ b/src/sage/config.py.in @@ -34,8 +34,8 @@ NTL_LIBDIR = "@NTL_LIBDIR@" # Path to the ecl-config script ECL_CONFIG = "@SAGE_ECL_CONFIG@".replace("${prefix}", SAGE_LOCAL) -# Path to the nauty binaries; of the form "/path/to/" or "/path/to/nauty-" -SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@" +# Path to the nauty binaries; of the form "/path/to" or "/path/to/nauty-" +SAGE_NAUTY_BINS_PREFIX = r"@SAGE_NAUTY_BINS_PREFIX@" SAGE_ECMBIN = "@SAGE_ECMBIN@" diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py index df9bdf6d892..e8a3a96fc05 100644 --- a/src/sage/features/__init__.py +++ b/src/sage/features/__init__.py @@ -657,7 +657,7 @@ def absolute_filename(self) -> str: class Executable(FileFeature): r""" - A feature describing an executable in the ``PATH``. + A feature describing an executable. In an installation of Sage with ``SAGE_LOCAL`` different from ``SAGE_VENV``, the executable is searched first in ``SAGE_VENV/bin``, then in ``SAGE_LOCAL/bin``, @@ -680,7 +680,9 @@ class Executable(FileFeature): sage: Executable(name='does-not-exist', executable='does-not-exist-xxxxyxyyxyy').is_present() FeatureTestResult('does-not-exist', False) """ - def __init__(self, name, executable, **kwds): + executable: Path + + def __init__(self, name: str, executable: Path | str, **kwds) -> None: r""" TESTS:: @@ -689,9 +691,9 @@ def __init__(self, name, executable, **kwds): True """ Feature.__init__(self, name, **kwds) - self.executable = executable + self.executable = Path(executable) - def _is_present(self): + def _is_present(self) -> FeatureTestResult: r""" Test whether the executable is on the current PATH and functional. @@ -708,7 +710,7 @@ def _is_present(self): return result return self.is_functional() - def is_functional(self): + def is_functional(self) -> FeatureTestResult: r""" Return whether an executable in the path is functional. @@ -744,22 +746,31 @@ def absolute_filename(self) -> str: sage.features.FeatureNotPresentError: does-not-exist is not available. Executable 'does-not-exist-xxxxyxyyxyy' not found on PATH. """ - if SAGE_LOCAL: - if Path(SAGE_VENV).resolve() != Path(SAGE_LOCAL).resolve(): - # As sage.env currently gives SAGE_LOCAL a fallback value from SAGE_VENV, - # SAGE_LOCAL is never unset. So we only use it if it differs from SAGE_VENV. - search_path = ':'.join([os.path.join(SAGE_VENV, 'bin'), - os.path.join(SAGE_LOCAL, 'bin')]) - path = shutil.which(self.executable, path=search_path) - if path is not None: - return path + if self.executable.is_absolute() and self.executable.exists(): + return str(self.executable.resolve()) + + if ( + SAGE_LOCAL + and SAGE_VENV + and Path(SAGE_VENV).resolve() != Path(SAGE_LOCAL).resolve() + ): + # As sage.env currently gives SAGE_LOCAL a fallback value from SAGE_VENV, + # SAGE_LOCAL is never unset. So we only use it if it differs from SAGE_VENV. + search_paths = os.pathsep.join( + [os.path.join(SAGE_VENV, "bin"), os.path.join(SAGE_LOCAL, "bin")] + ) + path = shutil.which(self.executable, path=search_paths) + if path is not None: + return path # Now look up in the regular PATH. path = shutil.which(self.executable) if path is not None: return path - raise FeatureNotPresentError(self, - reason="Executable {executable!r} not found on PATH.".format(executable=self.executable), - resolution=self.resolution()) + raise FeatureNotPresentError( + self, + reason=f"Executable '{str(self.executable)}' not found on PATH.", + resolution=self.resolution(), + ) class StaticFile(FileFeature): diff --git a/src/sage/features/nauty.py b/src/sage/features/nauty.py index 207c177971d..0b07fb2c3ed 100644 --- a/src/sage/features/nauty.py +++ b/src/sage/features/nauty.py @@ -11,6 +11,8 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** +from pathlib import Path + from sage.env import SAGE_NAUTY_BINS_PREFIX from . import Executable @@ -36,10 +38,16 @@ def __init__(self, name): sage: isinstance(NautyExecutable('geng'), NautyExecutable) True """ + if SAGE_NAUTY_BINS_PREFIX is None: + raise ValueError("SAGE_NAUTY_BINS_PREFIX is not set.") + if SAGE_NAUTY_BINS_PREFIX.endswith("-"): + executable = f"{SAGE_NAUTY_BINS_PREFIX}{name}" + else: + executable = Path(SAGE_NAUTY_BINS_PREFIX) / name Executable.__init__( self, name=f"nauty_{name}", - executable=f"{SAGE_NAUTY_BINS_PREFIX}{name}", + executable=executable, spkg="nauty", type="standard", ) diff --git a/src/sage/graphs/base/static_dense_graph.pyx b/src/sage/graphs/base/static_dense_graph.pyx index f417b7e666c..b54a53c9545 100644 --- a/src/sage/graphs/base/static_dense_graph.pyx +++ b/src/sage/graphs/base/static_dense_graph.pyx @@ -612,7 +612,7 @@ def connected_full_subgraphs(G, edges_only=False, labels=False, raise ValueError("the input (di)graph is not connected") for d in G.degree(): - if d >= 8 * sizeof(unsigned long) - 1: + if d >= 8 * sizeof(mp_limb_t) - 1: raise ValueError("the degree of the graph is too large for this method") cdef Py_ssize_t n = G.order() @@ -640,7 +640,7 @@ def connected_full_subgraphs(G, edges_only=False, labels=False, cdef MemoryAllocator mem = MemoryAllocator() cdef int * order = mem.calloc(n, sizeof(int)) - cdef mp_bitcnt_t * n_cpt = mem.calloc(n, sizeof(mp_bitcnt_t)) + cdef mp_limb_t * n_cpt = mem.calloc(n, sizeof(mp_limb_t)) # We use several bitsets to store the current boundary and active neighbors. # We also need another bitset that we create at the same time @@ -659,10 +659,10 @@ def connected_full_subgraphs(G, edges_only=False, labels=False, bitset_complement(active, active) bitset_discard(active, 0) bitset_copy(neighborhoods.rows[0], DG.rows[0]) - n_cpt[0] = 1 << bitset_len(DG.rows[0]) + n_cpt[0] = (1) << bitset_len(DG.rows[0]) cdef long u, v, j - cdef mp_bitcnt_t c + cdef mp_limb_t c cdef Py_ssize_t num_edges = 0 cdef list E = [] cdef list edges @@ -734,7 +734,10 @@ def connected_full_subgraphs(G, edges_only=False, labels=False, # prepare neighborhood of u bitset_and(neighborhoods.rows[i], active, DG.rows[u]) j = bitset_len(neighborhoods.rows[i]) - n_cpt[i] = bool(j) << j # 0 if not j else 2^j + if j: + n_cpt[i] = (1) << j # 2^j possible neighborhoods + else: + n_cpt[i] = 0 sig_on() binary_matrix_free(boundaries) diff --git a/src/sage/meson.build b/src/sage/meson.build index d43e636a4e8..3b356cf1fdc 100644 --- a/src/sage/meson.build +++ b/src/sage/meson.build @@ -79,7 +79,12 @@ foreach prog : nauty_progs ) endif if nauty_check.found() - nauty_bins_prefix = nauty_check.full_path().replace(prog, '') + nauty_bins_prefix = nauty_check.full_path().replace(prog + '.EXE', '').replace( + prog, + '', + ).strip( + '\\', + ) endif endforeach conf_data.set('SAGE_NAUTY_BINS_PREFIX', nauty_bins_prefix) diff --git a/src/sage/plot/plot3d/shapes.pyx b/src/sage/plot/plot3d/shapes.pyx index 4acab1a14eb..66806533e7e 100644 --- a/src/sage/plot/plot3d/shapes.pyx +++ b/src/sage/plot/plot3d/shapes.pyx @@ -51,14 +51,14 @@ EXAMPLES:: # https://www.gnu.org/licenses/ # **************************************************************************** -from libc.math cimport sqrt, sin, cos, acos, M_PI +from libc.math cimport sqrt, sin, cos, acos from sage.rings.real_double import RDF from sage.modules.free_module_element import vector from sage.misc.decorators import rename_keyword from sage.plot.plot3d.base import Graphics3dGroup from sage.plot.plot3d.index_face_set cimport IndexFaceSet, PrimitiveObject from sage.plot.plot3d.transform cimport point_c - +from sage.arith.constants cimport M_PI # Helper function to check that Box input is right def validate_frame_size(size): diff --git a/src/sage/rings/meson.build b/src/sage/rings/meson.build index 743c223dcc7..e628f5ee342 100644 --- a/src/sage/rings/meson.build +++ b/src/sage/rings/meson.build @@ -191,7 +191,7 @@ foreach name, pyx : extension_data elif name == 'real_double_element_gsl' deps += [gsl] elif name == 'real_mpfi' - deps += [mpfi] + deps += [mpfi, mpfr] elif name == 'real_mpfr' deps += [gmpy2, mpfr] elif name == 'tate_algebra_element' diff --git a/src/sage/stats/hmm/chmm.pyx b/src/sage/stats/hmm/chmm.pyx index e1ed80da585..6337376a1f1 100644 --- a/src/sage/stats/hmm/chmm.pyx +++ b/src/sage/stats/hmm/chmm.pyx @@ -18,7 +18,8 @@ AUTHOR: # *************************************************************************** from cpython.object cimport PyObject_RichCompare -from libc.math cimport log, sqrt, exp, isnormal, isfinite, M_PI +from libc.math cimport log, sqrt, exp, isnormal, isfinite +from sage.arith.constants cimport M_PI cdef double sqrt2pi = sqrt(2*M_PI) from cysignals.signals cimport sig_on, sig_off diff --git a/subprojects/packagefiles/singular/factory/meson.build b/subprojects/packagefiles/singular/factory/meson.build index a2b1961253e..3a28d85a05e 100644 --- a/subprojects/packagefiles/singular/factory/meson.build +++ b/subprojects/packagefiles/singular/factory/meson.build @@ -12,6 +12,7 @@ has_iostream_h = cpp.has_header('iostream.h') conf.set('NOSTREAMIO', not has_iostream_h)#, 'strstream.h', 'fstream.h', 'iostream', 'string', 'fstream', 'ctype.h')) conf.set('HAVE_IOSTREAM_H', has_iostream_h) conf.set('FACTORYVERSION', meson.project_version()) +conf.set('SIZEOF_LONG', cpp.sizeof('long')) factory_configuration = '@0@ in @1@'.format(meson.project_version(), meson.project_source_root()) conf.set_quoted('FACTORYCONFIGURATION', factory_configuration)