Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

spkg-configure.m4 for gap #29644

Closed
thierry-FreeBSD opened this issue May 4, 2020 · 25 comments
Closed

spkg-configure.m4 for gap #29644

thierry-FreeBSD opened this issue May 4, 2020 · 25 comments

Comments

@thierry-FreeBSD
Copy link

This ticket adds an spkg-configure.m4 for gap, in order to use it from a system package if possible (see #27330).

With this, the installed gap is well detected, but for sagelib and gap packages to built, several paths must be modified:

  • GAP_ROOT_DIR in src/sage/env.py

  • GAP_ROOT in build/pkgs/gap_packages/spkg-install.in

  • gap directory in src/sage/libs/gap/util.pyx

Moreover, the dependency on SAGE_LOCAL needs to be removed. Without it, one currently gets the following error

      File "/__w/sagetrac-mirror/sagetrac-mirror/src/sage/interfaces/expect.py", line 520, in _start
        raise RuntimeError("unable to start %s: %s" % (self.name(), msg))
    RuntimeError: unable to start gap: End Of File (EOF). Exception style platform.
    Gap finished running /__w/sagetrac-mirror/sagetrac-mirror/.tox/local-sudo-standard/local/bin/gap -r -b -p -T -E -o 401m -s 401m -m 64m /__w/sagetrac-mirror/sagetrac-mirror/src/sage/ext_data/gap/sage.g
    command: /__w/sagetrac-mirror/sagetrac-mirror/.tox/local-sudo-standard/local/bin/gap
    args: ['/__w/sagetrac-mirror/sagetrac-mirror/.tox/local-sudo-standard/local/bin/gap', '-r', '-b', '-p', '-T', '-E', '-o', '401m', '-s', '401m', '-m', '64m', '/__w/sagetrac-mirror/sagetrac-mirror/src/sage/ext_data/gap/sage.g']
    buffer (last 100 chars): b''
    before (last 100 chars): b'Set the environment variable SAGE_LOCAL.\r\n'
    after: <class 'pexpect.exceptions.EOF'>
    match: None
    match_index: None
    exitstatus: 1
    flag_eof: True
    pid: 3124
    child_fd: 26
    closed: False
    timeout: None
    delimiter: <class 'pexpect.exceptions.EOF'>
    logfile: None
    logfile_read: None
    logfile_send: None
    maxread: 4194304
    ignorecase: False
    searchwindowsize: None
    delaybeforesend: None
    delayafterclose: 0.1
    delayafterterminate: 0.1
    searcher: searcher_re:
        0: re.compile(b'gap> ')

CC: @dimpase @orlitzky @slel @isuruf @antonio-rojas

Component: build: configure

Keywords: gap, system packages

Author: Thierry Thomas, Samuel Lelièvre

Branch/Commit: public/29644 @ 2a41f49

Issue created by migration from https://trac.sagemath.org/ticket/29644

@thierry-FreeBSD
Copy link
Author

Attachment: spkg-configure.m4.gz

spkg-configure.m4 to be copied under build/pkgs/gap/

@mkoeppe
Copy link
Contributor

mkoeppe commented Aug 11, 2020

comment:1

system package information also needs to be added

@mkoeppe
Copy link
Contributor

mkoeppe commented Aug 11, 2020

Changed author from gh-thierry-FreeBSD to Thierry Thomas

@mkoeppe mkoeppe modified the milestones: sage-9.2, sage-9.3 Oct 24, 2020
@mkoeppe
Copy link
Contributor

mkoeppe commented Nov 10, 2020

comment:3

Which systems have a usable gap?

@antonio-rojas
Copy link
Contributor

comment:4

I'm using it in the distro sage package with no issues, with the patch from #29314. The only potential problem I'm aware of is that our gap-packages contains all packages shipped in the upstream tarball - this can cause high memory usage if it's installed.

@dimpase
Copy link
Member

dimpase commented Nov 11, 2020

comment:5

Replying to @antonio-rojas:

I'm using it in the distro sage package with no issues, with the patch from #29314. The only potential problem I'm aware of is that our gap-packages contains all packages shipped in the upstream tarball - this can cause high memory usage if it's installed.

I don't think having all the GAP packages used causes any memory problems - the majority are not loaded by GAP by default (and even if they are, their memory footprint is very small).

@tobiasdiez

This comment has been minimized.

@slel
Copy link
Member

slel commented Mar 22, 2021

comment:7

Thierry, can you push a branch here?
Or would you prefer someone else to commit for you?

The file spkg-configure.m4 you attached can be
committed with you as the author using:

$ git commit --author="Your Name <[email protected]>"

with name and email found from your previous contributions using:

$ git log | grep "Author: Thierry Thomas"

@slel
Copy link
Member

slel commented Mar 22, 2021

Changed author from Thierry Thomas to Thierry Thomas, Samuel Lelièvre

@slel
Copy link
Member

slel commented Mar 22, 2021

Commit: 2a41f49

@slel
Copy link
Member

slel commented Mar 22, 2021

comment:8

Not sure how to address any of the tasks in the ticket description.

But here is Thierry's spkg-configure.m4 and some distro info.


New commits:

2c9813529644: Add spkg-configure.m4 for gap
2a41f4929644: Add distro information for gap

@slel
Copy link
Member

slel commented Mar 22, 2021

Branch: public/29644

@mkoeppe mkoeppe modified the milestones: sage-9.3, sage-9.4 Mar 24, 2021
@antonio-rojas
Copy link
Contributor

comment:10

Replying to @dimpase:

I don't think having all the GAP packages used causes any memory problems - the majority are not loaded by GAP by default (and even if they are, their memory footprint is very small).

They are not loaded by GAP, but they are by Sage and it does cause problems - see #31761

@mkoeppe mkoeppe modified the milestones: sage-9.4, sage-9.5 Jul 19, 2021
@mkoeppe mkoeppe modified the milestones: sage-9.5, sage-9.6 Dec 18, 2021
@tornaria
Copy link
Contributor

comment:13

We could get the gap root from

$ gap -q --nointeract --bare -r -c 'Display(KERNEL_INFO().GAP_ROOT_PATHS[1]);'
/usr/share/gap/

The code in sage.libs.gap.util.gap_root() tries to read the gap shell script to figure out the gap root but that doesn't work with the gap shell script shipped by gap. Moreover, it's only used when sage.env.GAP_ROOT_DIR is undefined, but this is hardcoded in src/sage/env.py to be SAGE_SHARE/gap.

Maybe:

a. build/pkgs/gap/spkg-configure.m4: set GAP_ROOT using my suggestion above (while we are at it, check that running gap works, which is required by sage and I'm not sure is checked right now).
b. maybe check that the gap root is ok e.g. by looking for the file ${GAP_ROOT}/sysinfo.gap.
c. if gap will not be used from system, unset GAP_ROOT
d. AC_SUBST(GAP_ROOT)
e. in pkgs/sage-conf/sage_conf.py.in add a line like

GAP_ROOT_DIR = "@GAP_ROOT@" or None

I'm not completely sure how to do (a) and (b) but it's probably easy for someone who understands autoconf.

Note that when not using system gap, GAP_ROOT_DIR should be set to None so that the fallback defined in src/sage/env.py takes precedence ("" is not good enough). Alternatively, when gap will not be used from system, set GAP_ROOT=\$SAGE_LOCAL/share/gap and then have sage_conf.py replace $SAGE_LOCAL as is done for other variables (and please remove the fallback from src/sage/env.py, there's too many different places where variables are set and each package seems to have a different way to do it).

@dimpase
Copy link
Member

dimpase commented Jan 12, 2022

comment:14

Replying to @tornaria:

We could get the gap root from

$ gap -q --nointeract --bare -r -c 'Display(KERNEL_INFO().GAP_ROOT_PATHS[1]);'
/usr/share/gap/

Doesn't work for me on Debian 11:

dimpase@penguin:~/tmp$ gap -q --nointeract --bare -r -c 'Display(KERNEL_INFO().GAP_ROOT_PATHS[1]);'
/home/dimpase/gap/
dimpase@penguin:~/tmp$ ls /home/dimpase/gap
ls: cannot access '/home/dimpase/gap': No such file or directory
dimpase@penguin:~/tmp$ which gap
/usr/bin/gap
dimpase@penguin:~/tmp$ gap
 ┌───────┐   GAP 4.11.0 of 29-Feb-2020
 │  GAP  │   https://www.gap-system.org
 └───────┘   Architecture: x86_64-pc-linux-gnu-default64-kv7
 Configuration:  gmp 6.2.1, GASMAN, readline
 Loading the library and packages ...
 Packages:   Alnuth 3.1.2, AtlasRep 2.1.0, AutPGrp 1.10.2, CTblLib 1.3.1, 
             FactInt 1.6.3, GAPDoc 1.6.3, IO 4.7.0, Polycyclic 2.15.1, PrimGrp 3.4.0, 
             SmallGrp 1.4.1, TomLib 1.2.9, TransGrp 2.0.6
 Try '??help' for help. See also '?copyright', '?cite' and '?authors'
gap> 

@dimpase
Copy link
Member

dimpase commented Jan 12, 2022

comment:15

In case, here is my GAP_ROOT_PATHS:

  GAP_ROOT_PATHS := [ "/home/dimpase/.gap/", "/home/dimpase/gap/", 
      "/usr/local/lib/gap/", "/usr/local/share/gap/", "/usr/lib/gap/", 
      "/usr/share/gap/" ]

@tornaria
Copy link
Contributor

comment:16

What are GAPInfo.UserGapRoot and KERNEL_INFO().GAP_ROOT_PATHS with and without -r?

Here I get:

$ gap --norepl --bare -b -c 'Display(GAPInfo.UserGapRoot); Display(KERNEL_INFO().GAP_ROOT_PATHS);'
/home/tornaria/.gap
[ "/home/tornaria/.gap/", "/usr/share/gap/" ]
$ gap -r --norepl --bare -b -c 'Display(GAPInfo.UserGapRoot); Display(KERNEL_INFO().GAP_ROOT_PATHS);'
/home/tornaria/.gap
[ "/usr/share/gap/" ]

Using -r prevents UserGapRoot to be prepended to GAP_ROOT_PATHS. It seems your GAP_ROOT_PATHS already has a user directory and other locations.

Which one is the "real" gap root? I think it should be either the one containing sysinfo.gap or else the one containing lib/init.g.

Alternatively, use the whole list of GAP_ROOT_PATHS, so that sage passes all of them to gap when initializing the library. This would need to rework sage.libs.gap.util.gap_root() so it accepts multiple directories (and it's not necessary for all directories to exist, it suffices that at least one contains lib/init.g).

To get the complete list for GAP_ROOT_PATHS we can use this:

$ gap -q --nointeract --bare -r -c 'Display(JoinStringsWithSeparator(KERNEL_INFO().GAP_ROOT_PATHS,";"));'
/usr/share/gap/

For me it gives the same since I only have one path in the system gap root, but for you it should give all of them.

@tornaria
Copy link
Contributor

comment:17

Something like that seems to work as a fallback in case GAP_ROOT_DIR is not set:

--- sage-9.5.rc0/src/sage/libs/gap/util.pyx.orig	2022-01-09 07:49:26.000000000 -0300
+++ sage-9.5.rc0/src/sage/libs/gap/util.pyx	2022-01-13 01:57:11.072897453 -0300
@@ -178,6 +178,11 @@
     if os.path.exists(sage.env.GAP_ROOT_DIR):
         return sage.env.GAP_ROOT_DIR
 
+    from sage.interfaces.gap import gap
+    for gapdir in gap("KERNEL_INFO().GAP_ROOT_PATHS").sage():
+        if os.path.exists(os.path.join(gapdir, 'sysinfo.gap')):
+            return gapdir
+
     # Attempt to figure out the appropriate GAP_ROOT by reading the
     # local/bin/gap shell script; this is an ugly hack that exists for
     # historical reasons; the best approach to setting where Sage looks for

The drawback is that this will launch and keep running the gap interpreter. Maybe better to do a one-time run of gap just for this purpose (or run it at configure time as I proposed above).

@tornaria
Copy link
Contributor

comment:18

Maybe this is better:

--- a/src/sage/libs/gap/util.pyx
+++ b/src/sage/libs/gap/util.pyx
@@ -23,6 +23,7 @@ from cysignals.signals cimport sig_on, sig_off
 import os
 import warnings
 import sage.env
+import subprocess
 
 from .gap_includes cimport *
 from .element cimport *
@@ -178,6 +179,16 @@ def gap_root():
     if os.path.exists(sage.env.GAP_ROOT_DIR):
         return sage.env.GAP_ROOT_DIR
 
+    gap_expr = 'JoinStringsWithSeparator(KERNEL_INFO().GAP_ROOT_PATHS,";")'
+    gap_root_paths = subprocess.getoutput(
+        f"gap -r -q --bare --nointeract -c 'Display({gap_expr});'"
+        ).strip().split(';')
+
+    for gapdir in gap_root_paths:
+        if os.path.exists(os.path.join(gapdir, 'sysinfo.gap')):
+            sage.env.GAP_ROOT_DIR = gapdir
+            return gapdir
+
     # Attempt to figure out the appropriate GAP_ROOT by reading the
     # local/bin/gap shell script; this is an ugly hack that exists for
     # historical reasons; the best approach to setting where Sage looks for

Also, sage.env._get_shared_lib_path() is broken in the sense that it looks for libgap.so but it should really look for libgap.so*. Indeed the soname for the library is libgap.so.0 and the symlink libgap.so -> libgap.so.0 is only provided by gap-devel package (which is installed when I build sage, but not necessarily when I run sage).

Simple fix:

--- a/src/sage/env.py
+++ b/src/sage/env.py
@@ -293,7 +293,7 @@ def _get_shared_lib_path(*libnames: str) -> Optional[str]:
             if sys.platform == 'darwin':
                 ext = 'dylib'
             else:
-                ext = 'so'
+                ext = 'so*'
 
             if SAGE_LOCAL:
                 search_directories.append(Path(SAGE_LOCAL) / 'lib')

OTOH, there's sage.misc.compat.find_library, maybe that could be used instead.

Finally, there's a test in src/sage_setup/optional_extension.py that fails when gap is installed from system so

--- a/src/sage_setup/optional_extension.py
+++ b/src/sage_setup/optional_extension.py
@@ -80,7 +80,7 @@ def OptionalExtension(*args, **kwds):
         sage: print(ext.__class__.__name__)
         CythonizeExtension
         sage: ext = OptionalExtension("foo", ["foo.c"], package="gap")
-        sage: print(ext.__class__.__name__)
+        sage: print(ext.__class__.__name__)     # not tested - fails with system gap
         Extension
     """
     try:

I also changed GAP_ROOT_DIR for gap_root() in
src/sage/libs/gap/saved_workspace.py:

--- a/src/sage/libs/gap/saved_workspace.py
+++ b/src/sage/libs/gap/saved_workspace.py
@@ -8,7 +8,7 @@ workspaces.

 import os
 import glob
-from sage.env import GAP_ROOT_DIR
+from sage.libs.gap.util import gap_root
 from sage.interfaces.gap_workspace import gap_workspace_file


@@ -31,7 +31,7 @@ def timestamp():
     """
     libgap_dir = os.path.dirname(__file__)
     libgap_files = glob.glob(os.path.join(libgap_dir, '*'))
-    gap_packages = glob.glob(os.path.join(GAP_ROOT_DIR, 'pkg', '*'))
+    gap_packages = glob.glob(os.path.join(gap_root(), 'pkg', '*'))
     files = libgap_files + gap_packages
     if len(files) == 0:
         print('Unable to find LibGAP files.')

@dimpase
Copy link
Member

dimpase commented Jan 14, 2022

comment:19

How about asking GAP people (or doing a PR) to provide an appropriate feature? Which one does really matter? The one with sysinfo.gap ?

Did you look at how this is done in https://github.com/embray/gappy ?

@tornaria
Copy link
Contributor

comment:20

Replying to @dimpase:

How about asking GAP people (or doing a PR) to provide an appropriate feature? Which one does really matter? The one with sysinfo.gap ?

Did you look at how this is done in https://github.com/embray/gappy ?

TL;DR: if the shared library for gap is in /PATH/TO/lib/, then try GAP_ROOT=/PATH/TO/share/gap. Decide the path is correct if a file $GAP_ROOT/lib/init.g exists.

This seems ok to me, in principle the path to the gap library is in GAP_SO.

@tornaria
Copy link
Contributor

tornaria commented Mar 4, 2022

comment:21

For GAP_SO see #33446 which intends to make it no longer necessary. The patch to _get_shared_lib_path() would not be used either.

@mkoeppe mkoeppe modified the milestones: sage-9.6, sage-9.7 Mar 5, 2022
@tornaria
Copy link
Contributor

tornaria commented Mar 6, 2022

comment:23

Notes about GAP_ROOT_DIR:

  • my patches in comment:17 and comment:18 are not good, because running gap at sage runtime is too costly (about 0.7s in my box, which would double sage initialization time).
  • sage installs all the gap packages in a single directory $SAGE_LOCAL/share/gap. However, system gap may have packages split in more than one location (e.g. debian uses /usr/share/gap and /usr/lib/gap, the latter to place compiled portions of packages).

Maybe the GAP_ROOT_DIR variable should be replaced by a GAP_ROOT_PATHS variable to be determined at configure time with some magic. Distros could override this variable as usual.

@orlitzky
Copy link
Contributor

Has anything improved in the past year and a half? @tobiasdiez is working on a meson build for sagelib that uses only

gap = cc.find_library('gap')

to find GAP. If it's that easy now, we could finally write the spkg-configure.m4 for it; if it's not then it would be good to know the current list of pitfalls of that approach.

vbraun pushed a commit to vbraun/sage that referenced this issue Dec 17, 2023
    
Resurrect sagemath#29644 and add an
`spkg-configure.m4` for GAP.

Some other changes were made to make this possible / nicer:

1. `GAP_LIB_DIR` and `GAP_SHARE_DIR` are merged into `GAP_ROOT_PATHS`.
This was suggested by @tornaria and is the right approach, especially
now that `GAP_SO` has been removed. Nothing else needs those two
directories and GAP itself doesn't care about them -- it only cares
about the package search path, i.e. `GAP_ROOT_PATHS`. So changing the
variable(s) brings us in line with the way GAP works.
2. We no longer pass the `-r` flag to GAP. Particularly when using the
system GAP, we do want the user to be able to install his own packages
and run his own initialization.
3. We begin to pass `-A` to GAP. This tells GAP not to autoload the big
set of "recommended" packages at start-up, which avoids the inevitable
error messages when those packages are not installed on the system GAP.
We could check for all of them in `spkg-configure.m4`, but it's a long
list, and loading them slows down GAP initialization for no benefit --
Sage itself uses only one such package. Users can autoload them via
gaprc or gap.ini in light of (2).
4. After adding `-A` to the initialization, we try to load the
PolyCyclic package if it's installed. This is the one "recommended"
package that Sage itself uses.
5. The "recommended" packages are all moved from the gap SPKG to
gap_packages because we no longer need them, except (maybe) for
PolyCyclic. Should keep polycyclic installed by default? The tests all
pass without it, but it is used a few places in sagelib.
6. Various stale doctest fixes.
7. The expected output from a few tests has changed. Where possible,
I've made the test more robust. In one case I had to drop the printing
of a matrix, because if you dig into the source code for GAP's
`NormalSubgroups()`, it chooses a `Representative()` inconsistently, and
that eventually affects the entries of the matrix.

All tests are passing afterwards... except one, a heisenbug:

```
sage -t --warn-long 187.7 --random-
seed=96688270013898650573232209016248663009
src/sage/groups/matrix_gps/finitely_generated_gap.py
**********************************************************************
File "src/sage/groups/matrix_gps/finitely_generated_gap.py", line 123,
in sage.groups.matrix_gps.finitely_generated_gap.FinitelyGeneratedMatrix
Group_gap.as_permutation_group
Failed example:
    P == Psmaller
Expected:
    False
Got:
    True
**********************************************************************
```

If anyone knows what's going on there, I'd be grateful. The GAP docs for
`SmallerDegreePermutationRepresentation()` say,

> The methods used might involve the use of random elements and the
permutation representation (or even the degree of the representation) is
not guaranteed to be the same for different calls of
SmallerDegreePermutationRepresentation.

So maybe this isn't really a bug, but I would find it strange if that
randomness could mean that sometimes the smaller degree option just
wouldn't work at all.

Anyway, have at it, and let me know what you think.
    
URL: sagemath#36792
Reported by: Michael Orlitzky
Reviewer(s): Dima Pasechnik, François Bissey, Gonzalo Tornaría, Matthias Köppe, Michael Orlitzky, Tobias Diez
@mkoeppe
Copy link
Contributor

mkoeppe commented Dec 23, 2023

Fixed in #36792

@mkoeppe mkoeppe closed this as completed Dec 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants