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

Add command line option --packagedirs and GAP function ExtendPackageDirectories() to make it easier to use custom packages #5873

Merged
merged 17 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from 14 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
4 changes: 1 addition & 3 deletions dev/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,9 @@ testmockpkg () {
cd "$mockpkg_dir"
echo_and_run ./configure "$gaproot"
echo_and_run $MAKE V=1
# trick to make it easy to load the package in GAP
rm -f pkg && ln -sf . pkg
# try to load the kernel extension
cd "$gaproot"
echo_and_run $gap -A -l "$mockpkg_dir;" "$mockpkg_dir/tst/testall.g"
echo_and_run $gap -A --packagedirs "$mockpkg_dir" "$mockpkg_dir/tst/testall.g"
}


Expand Down
31 changes: 28 additions & 3 deletions doc/ref/files.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ directories. For example when ⪆ wants to read its library file
<C>GAPInfo.RootPaths</C> until it finds the path of an existing file.
The first file found this way is read.
<P/>
Furthermore, &GAP; looks for available packages by examining the
subdirectories <F>pkg/</F> in each of the directories in
<C>GAPInfo.RootPaths</C>.
Subdirectories <F>pkg/</F> in each of the directories in <C>GAPInfo.RootPaths</C>
appear in <C>GAPInfo.PackageDirectories</C> (see <Ref Sect="GAP Package Directories"/>),
fingolfin marked this conversation as resolved.
Show resolved Hide resolved
which controls where &GAP; looks for available packages.
<P/>
lgoettgens marked this conversation as resolved.
Show resolved Hide resolved
The root directories are specified via one or several of the
<C>-l paths</C> command line options, see <Ref Sect="Command Line Options"/>.
Expand All @@ -95,11 +95,36 @@ This directory can be used to tell &GAP; about personal preferences,
to always load some additional code, to install additional packages,
or to overwrite some &GAP; files. See <Ref Sect="sect:gap.ini"/>
for more information how to do this.
After &GAP; has been started, one can add additional root directories
via the function <Ref Func="ExtendRootDirectories"/>.
<P/>

</Section>


<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->
<Section Label="GAP Package Directories">
<Heading>GAP Package Directories</Heading>
<Index Key="GAPInfo.PackageDirectories"><C>GAPInfo.PackageDirectories</C></Index>

When &GAP; is started it determines a list of directories potentially
containing packages called the <E>&GAP; package directories</E>.
fingolfin marked this conversation as resolved.
Show resolved Hide resolved
In a running &GAP; session this list can be found in <C>GAPInfo.PackageDirectories</C>.
<P/>
Every subdirectory <F>pkg/</F> in a &GAP; root directory is automatically
fingolfin marked this conversation as resolved.
Show resolved Hide resolved
added to this list. Further package directories can be specified via one or several
<C>--packagedirs paths</C> command line options, see <Ref Sect="Command Line Options"/>,
or after &GAP; has been started via the function <Ref Func="ExtendPackageDirectories"/>.
The order of the directories in <C>GAPInfo.PackageDirectories</C> is as follows:
first the package directories specified via the command line option <C>--packagedirs </C>,
fingolfin marked this conversation as resolved.
Show resolved Hide resolved
then the subdirectories of the &GAP; root directories that were known at startup in the
fingolfin marked this conversation as resolved.
Show resolved Hide resolved
same order, and finally the directories added after &GAP; has been started.
<P/>
&GAP; looks for available packages by examining each of the directories in
<C>GAPInfo.PackageDirectories</C>.
</Section>


<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->
<Section Label="Directories">
<Heading>Directories</Heading>
Expand Down
1 change: 1 addition & 0 deletions doc/ref/gappkg.xml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ that is they will be loaded automatically when &GAP; starts

<#Include Label="SetPackagePath">
<#Include Label="ExtendRootDirectories">
<#Include Label="ExtendPackageDirectories">
<#Include Label="DisplayPackageLoadingLog">

</Section>
Expand Down
21 changes: 21 additions & 0 deletions doc/ref/run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,27 @@ It is not possible to use &GAP; without the library files, so you must
not ignore this warning. You should leave &GAP; and start it again,
specifying the correct root path using the <C>-l</C> option.
</Item>
<Mark><Index Key="--packagedirs"><C>--packagedirs</C></Index>
<C>--packagedirs </C><A>path_list</A></Mark>
<Item>
can be used to add paths to &GAP;'s list of package directories
(see <Ref Sect="GAP Package Directories"/>).
The list always contains all subdirectories <F>pkg/</F> in a &GAP; root directory.
fingolfin marked this conversation as resolved.
Show resolved Hide resolved
<P/>
<A>path_list</A> should be a list of directories separated by semicolons.
No whitespace is permitted before or after a semicolon, and the first and
last character of <A>path_list</A> may not be a semicolon.
After &GAP; has completed its startup procedure and
displays the prompt, the list of package directories can be seen in the
variable <C>GAPInfo.PackageDirectories</C>,
see <Ref Var="GAPInfo"/>.
<P/>
Usually this option is used inside a startup script to specify
where additional &GAP; packages are located on the system.
The <C>--packagedirs</C> option can also be used by individual users to tell &GAP;
about additional &GAP; packages, without the need to set up a complete root
directory structure.
</Item>
<Mark><Index Key="-M"><C>-M</C></Index>
<C>-M</C></Mark>
<Item>
Expand Down
26 changes: 25 additions & 1 deletion lib/package.gd
Original file line number Diff line number Diff line change
Expand Up @@ -859,7 +859,8 @@ DeclareGlobalFunction( "LoadPackage" );
## <P/>
## See <Ref Func="SetPackagePath"/> for a way to force the loading of a
## prescribed package version.
## See also <Ref Func="ExtendRootDirectories"/> for a method of adding
## See also <Ref Func="ExtendRootDirectories"/> and
## <Ref Func="ExtendPackageDirectories"/> for methods of adding
## directories containing packages <E>after</E> &GAP; has been started.
## </Subsection>
## <#/GAPDoc>
Expand Down Expand Up @@ -947,6 +948,29 @@ DeclareGlobalFunction( "SetPackagePath" );
##
DeclareGlobalFunction( "ExtendRootDirectories" );


#############################################################################
##
#F ExtendPackageDirectories( <paths> )
##
## <#GAPDoc Label="ExtendPackageDirectories">
## <ManSection>
## <Func Name="ExtendPackageDirectories" Arg='paths'/>
##
## <Description>
## Let <A>paths</A> be a list of strings that denote paths to intended
## &GAP; package directories (see <Ref Sect="GAP Package Directories"/>).
## The function <Ref Func="ExtendPackageDirectories"/> adds these paths to
## the global list <C>GAPInfo.PackageDirectories</C> and calls the initialization of
## available &GAP; packages,
## such that later calls to <Ref Func="LoadPackage"/> will find the &GAP;
## packages that are contained in the directories given by <A>paths</A>.
## </Description>
## </ManSection>
## <#/GAPDoc>
##
DeclareGlobalFunction( "ExtendPackageDirectories" );

#############################################################################
##
#F InstalledPackageVersion( <name> )
Expand Down
73 changes: 64 additions & 9 deletions lib/package.gi
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@
## In earlier versions, this function had an argument; now we ignore it.
##
InstallGlobalFunction( InitializePackagesInfoRecords, function( arg )
local pkgdirs, pkgdir, ignore, name, files, record, r;
local pkgdirs, pkgdir, pkgdirstrs, ignore, name, file, files, record, r;

if IsBound( GAPInfo.PackagesInfoInitialized ) and
GAPInfo.PackagesInfoInitialized = true then
Expand All @@ -300,8 +300,28 @@

LogPackageLoadingMessage( PACKAGE_DEBUG,
"entering InitializePackagesInfoRecords", "GAP" );

# the first time this is called, add the cmd line args to the list
if IsEmpty(GAPInfo.PackageDirectories) then
for pkgdirstrs in GAPInfo.CommandLineOptions.packagedirs do
pkgdirs:= List( SplitString( pkgdirstrs, ";" ), Directory );
for pkgdir in pkgdirs do
if not pkgdir in GAPInfo.PackageDirectories then
Add( GAPInfo.PackageDirectories, pkgdir );
fi;
od;
od;
fi;
# add any new pkg directories to the list
pkgdirs:= DirectoriesLibrary( "pkg" );
if pkgdirs = fail then
if pkgdirs <> fail then
pkgdirs:= Filtered( pkgdirs, dir -> not dir in GAPInfo.PackageDirectories );
lgoettgens marked this conversation as resolved.
Show resolved Hide resolved
if not IsEmpty(pkgdirs) then
APPEND_LIST_INTR( GAPInfo.PackageDirectories, pkgdirs );
fingolfin marked this conversation as resolved.
Show resolved Hide resolved
fi;
fi;

if IsEmpty(GAPInfo.PackageDirectories) then
LogPackageLoadingMessage( PACKAGE_DEBUG,
"exit InitializePackagesInfoRecords (no pkg directories found)",
"GAP" );
Expand All @@ -327,23 +347,29 @@
# Loop over the package directories,
# remove the packages listed in `NOAUTO' files from GAP's suggested
# packages, and unite the information for the directories.
for pkgdir in pkgdirs do
for pkgdir in GAPInfo.PackageDirectories do

if IsBound( GAPInfo.ExcludeFromAutoload ) then
UniteSet( GAPInfo.ExcludeFromAutoload,
List( RECORDS_FILE( Filename( pkgdir, "NOAUTO" ) ),
LowercaseString ) );
fi;

# Loop over subdirectories of this package directory.
for name in Set( DirectoryContents( Filename( pkgdir, "" ) ) ) do
# pkgdir may be a package instead of a package directory
file:= Filename( [ pkgdir ], "PackageInfo.g" );
fingolfin marked this conversation as resolved.
Show resolved Hide resolved
if file <> fail then
AddPackageInfos( [ [ file, "" ] ], pkgdir, ignore );
else
# Loop over subdirectories of this package directory.
for name in Set( DirectoryContents( Filename( pkgdir, "" ) ) ) do
fingolfin marked this conversation as resolved.
Show resolved Hide resolved

## Get all package dirs
files := FindPackageInfosInSubdirectories( pkgdir, name );
## Get all package dirs
files := FindPackageInfosInSubdirectories( pkgdir, name );

AddPackageInfos( files, pkgdir, ignore );
AddPackageInfos( files, pkgdir, ignore );

od;
od;
fi;
od;

# Sort the available info records by their version numbers.
Expand Down Expand Up @@ -1958,6 +1984,35 @@
end );


#############################################################################
##
#F ExtendPackageDirectories( <paths_or_dirs> )
##
InstallGlobalFunction( ExtendPackageDirectories, function( paths_or_dirs )
fingolfin marked this conversation as resolved.
Show resolved Hide resolved
local p, changed;
changed:= false;
for p in paths_or_dirs do
if IsString( p ) then
p:= Directory( p );

Check warning on line 1996 in lib/package.gi

View check run for this annotation

Codecov / codecov/patch

lib/package.gi#L1996

Added line #L1996 was not covered by tests
elif not IsDirectory( p ) then
Error("input must be a list of path strings or directory objects");

Check warning on line 1998 in lib/package.gi

View check run for this annotation

Codecov / codecov/patch

lib/package.gi#L1998

Added line #L1998 was not covered by tests
fi;
if not p in GAPInfo.PackageDirectories then
Add( GAPInfo.PackageDirectories, p );
changed:= true;
fi;
od;
if changed then
# Reread the package information.
if IsBound( GAPInfo.PackagesInfoInitialized ) and
GAPInfo.PackagesInfoInitialized = true then
GAPInfo.PackagesInfoInitialized:= false;
InitializePackagesInfoRecords();
fi;
fi;
end );


#############################################################################
##
#F InstalledPackageVersion( <name> )
Expand Down
4 changes: 4 additions & 0 deletions lib/system.g
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ BIND_GLOBAL( "GAPInfo", rec(
"directories to the end/start of existing list",
"of root paths" ] ),
rec( short:= "r", default := false, help := ["disable/enable user GAP root dir", "GAPInfo.UserGapRoot"] ),
rec( long := "packagedirs", default := [], arg := "<paths>",
help := [ "add additional GAP directory paths",
"Directories are separated using ';'." ] ),
,
rec( section:= ["Loading:"] ),
rec( short:= "A", default := false, help := ["disable/enable autoloading of suggested", "GAP packages"] ),
Expand Down Expand Up @@ -302,6 +305,7 @@ CallAndInstallPostRestore( function()

# paths
GAPInfo.RootPaths:= GAPInfo.KernelInfo.GAP_ROOT_PATHS;
GAPInfo.PackageDirectories := [];
if IsBound(GAPInfo.SystemEnvironment.HOME) then
GAPInfo.UserHome := GAPInfo.SystemEnvironment.HOME;
else
Expand Down
27 changes: 7 additions & 20 deletions tst/testinstall/package.tst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#@local entry,equ,pair,sml,oldTermEncoding,pkginfo,info,tmp_dir,mockpkgpath,old_warning_level,p,n,filename,IsDateFormatValid,loadinfo,eval_loadinfo
#@local entry,equ,pair,sml,oldTermEncoding,pkginfo,info,mockpkgpath,old_warning_level,p,n,filename,IsDateFormatValid,loadinfo,eval_loadinfo
gap> START_TEST("package.tst");

# CompareVersionNumbers( <supplied>, <required>[, \"equal\"] )
Expand Down Expand Up @@ -380,17 +380,9 @@ false
gap> IsPackageLoaded("mockpkg", ">=2.0");
false

# load mockpkg via a symlink in a directory called `pkg`
# so we can test ExtendRootDirectories below
# first create a temporary directory for all of this
gap> tmp_dir := DirectoryTemporary( );;

# create a subdirectory `<tmp_dir>/pkg`
gap> Exec( Concatenation( "mkdir -p ", Filename( tmp_dir, "/pkg" ) ) );

# make `<tmp_dir>/pkg/mockpkg` a symlink to `tst/mockpkg`
gap> Exec( Concatenation( "ln -sfn ", Filename( DirectoriesLibrary("tst/mockpkg"), "" )," ", Filename( tmp_dir, "pkg/mockpkg" ) ) );
gap> mockpkgpath := Directory( Filename( tmp_dir, "pkg/mockpkg" ) );;
# load mockpkg first via SetPackagePath and later via
# ExtendPackageDirectories
gap> mockpkgpath := DirectoriesLibrary("tst/mockpkg")[1];;
gap> ValidatePackageInfo(Filename(mockpkgpath, "PackageInfo.g"));
true

Expand Down Expand Up @@ -635,12 +627,8 @@ false
gap> IsPackageLoaded("mockpkg", ">=2.0");
false

# now add the temporary directory created above as a new root directory
gap> filename:= ShallowCopy( Filename( tmp_dir, "" ) );;
gap> while EndsWith( filename, "/" ) do Remove( filename ); od;
gap> ExtendRootDirectories( [ filename ] );
gap> ForAll( GAPInfo.RootPaths, x -> EndsWith( x, "/" ) );
true
# now add the directory with mockpkgpath as a new package directory
gap> ExtendPackageDirectories( [ mockpkgpath ] );

# make sure that the newly discovered installation path matches
# the path from which mockpkg was loaded above
Expand All @@ -649,8 +637,7 @@ gap> Last( GAPInfo.PackagesInfo.mockpkg ).InstallationPath =
true

#
gap> SetPackagePath( "mockpkg", Filename( tmp_dir, "pkg/mockpkg" ) );
gap> SetPackagePath( "mockpkg", Filename( tmp_dir, "pkg/mockpkg/" ) );
gap> SetPackagePath( "mockpkg", Filename( mockpkgpath, "" ) );
gap> SetPackagePath( "mockpkg", "/some/other/directory" );
Error, another version of package mockpkg is already loaded

Expand Down
Loading