Skip to content
Draft
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
31 changes: 31 additions & 0 deletions lib/package.gd
Original file line number Diff line number Diff line change
Expand Up @@ -1365,3 +1365,34 @@ DeclareGlobalFunction( "Cite" );
DeclareGlobalFunction( "ShowPackageVariables" );

DeclareGlobalFunction( "PackageVariablesInfo" );


# a utility function
DeclareGlobalName( "PrescribedPackageVersions" );


#############################################################################
##
#F PackagesLoaded()
##
## <ManSection>
## <Func Name="PackagesLoaded" Arg=''/>
##
## <Returns>
## a string that describes the names of all currently loaded &GAP; packages
## and their version numbers.
## </Returns>
##
## <Description>
## The result consists of <C>\n</C> separated lines of the form
## <C>name = "version"</C>,
## where <C>name</C> is the name of a loaded &GAP; package
## and <C>version</C> is its version.
## <P/>
## One can print this string to a file and set the user preference
## <C>"PrescribedPackageVersions"</C> to the name of this file,
## Then starting &GAP; anew will load exactly the same packages.
## </Description>
## </ManSection>
##
DeclareGlobalName( "PackagesLoaded" );
96 changes: 95 additions & 1 deletion lib/package.gi
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@ end );
## In earlier versions, this function had an argument; now we ignore it.
##
InstallGlobalFunction( InitializePackagesInfoRecords, function( arg )
local pkgdirs, pkgdir, pkgdirstrs, ignore, name, file, files, record, r;
local pkgdirs, pkgdir, pkgdirstrs, ignore, name, file, files, record, r,
exact;

if IsBound( GAPInfo.PackagesInfoInitialized ) and
GAPInfo.PackagesInfoInitialized = true then
Expand Down Expand Up @@ -372,6 +373,17 @@ InstallGlobalFunction( InitializePackagesInfoRecords, function( arg )
fi;
od;

# If exact version numbers are prescribed then ignore all package
# versions that do not fit to the list.
exact:= PrescribedPackageVersions(
UserPreference( "PrescribedPackageVersions" ) );
if exact <> fail then
GAPInfo.PackagesInfo:= Filtered( GAPInfo.PackagesInfo,
r -> [ LowercaseString( r.PackageName ), r.Version ] in exact );
GAPInfo.PrescribedPackageVersions:= List( exact,
pair -> [ pair[1], Concatenation( "=", pair[2] ) ] );
fi;

# Sort the available info records by their version numbers.
# (Sort stably in order to make sure that an instance from the first
# possible root path gets chosen if the same version of a package
Expand Down Expand Up @@ -1993,6 +2005,46 @@ The level can be changed in a running session using \
multi:= false,
) );

# And a preference for prescribing explicit package versions.
BindGlobal( "PrescribedPackageVersions", function( filename )
local exact;

if IsString( filename ) and filename <> "" then
filename:= UserHomeExpand( filename );
if IsReadableFile( filename ) then
exact:= List( SplitString( StringFile( filename ), "\n" ),
line -> Filtered( SplitString( line, "=", " " ), x -> x <> "" ) );
if ForAll( exact, x -> Length( x ) = 2 and
Length( x[2] ) > 2 and
x[2][1] = '\"' and Last( x[2] ) = '\"' ) then
return List( exact, pair -> [ LowercaseString( pair[1] ),
ReplacedString( pair[2], "\"", "" ) ] );
fi;
fi;
fi;
return fail;
end );

DeclareUserPreference( rec(
name:= "PrescribedPackageVersions",
description:= [
"If the value is a nonempty string then it is assumed to be the name \
of a file that consists of lines of the form <C>name = \"version\"</C>, \
where <C>name</C> is the (case insensitive) name of a package \
and <C>version</C> is the exact version of the package <C>name</C> \
that shall be loaded. \
In this case, &GAP;'s automatic package loading mechanism will try to load \
exactly these package versions, all other available packages (in particular \
suggested packages that are not listed in the file) will be ignored, \
and an error will occur if not all of the given packages can be loaded \
in the prescribed versions. \
This preference overrides the preferences <C>\"PackagesToLoad\"</C>, \
<C>\"ExcludeFromAutoload\"</C>, <C>\"PackagesToIgnore\"</C>."
],
default:= "",
check:= filename -> PrescribedPackageVersions( filename ) <> fail,
) );

InstallGlobalFunction( AutoloadPackages, function()
local msg, pair, excludedpackages, name, record, neededPackages;

Expand All @@ -2014,6 +2066,9 @@ InstallGlobalFunction( AutoloadPackages, function()
# If --bare is specified, load no packages
if GAPInfo.CommandLineOptions.bare then
neededPackages := [];
elif IsBound( GAPInfo.PrescribedPackageVersions ) then
neededPackages := GAPInfo.PrescribedPackageVersions;
PushOptions( rec( OnlyNeeded:= true ) );
else
neededPackages := GAPInfo.Dependencies.NeededOtherPackages;
fi;
Expand Down Expand Up @@ -2090,6 +2145,9 @@ InstallGlobalFunction( AutoloadPackages, function()
LogPackageLoadingMessage( PACKAGE_DEBUG,
"suggested packages loaded", "GAP" );
fi;
if IsBound( GAPInfo.PrescribedPackageVersions ) then
PopOptions();
fi;

# Load the documentation for not yet loaded packages.
LogPackageLoadingMessage( PACKAGE_DEBUG,
Expand Down Expand Up @@ -3550,3 +3608,39 @@ InstallGlobalFunction( ShowPackageVariables, function( arg )
Print( result );
fi;
end );


#############################################################################
##
#F PackagesLoaded()
##
## <ManSection>
## <Func Name="PackagesLoaded" Arg=''/>
##
## <Returns>
## a string that describes the names of all currently loaded &GAP; packages
## and their version numbers.
## </Returns>
##
## <Description>
## The result consists of <C>\n</C> separated lines of the form
## <C>name = "version"</C>,
## where <C>name</C> is the name of a loaded &GAP; package
## and <C>version</C> is its version.
## <P/>
## One can print this string to a file and set the user preference
## <C>"PrescribedPackageVersions"</C> to the name of this file,
## Then starting &GAP; anew will try to load exactly the same packages,
## and signal an error if this isnot possible.
## </Description>
## </ManSection>
##
BindGlobal( "PackagesLoaded", function()
local l;

l:= List( RecNames( GAPInfo.PackagesLoaded ),
x -> GAPInfo.PackagesLoaded.( x ) );
return JoinStringsWithSeparator(
Set( l, x -> Concatenation( x[3], " = \"", x[2], "\"" ) ),
"\n" );
end );
Loading