From 42789e61c61440f24663e08731b5351ef1ae2b6b Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 4 Jun 2018 23:40:32 +0100 Subject: [PATCH] hpcgap: sync lib/package.gi --- hpcgap/lib/package.gi | 3357 ----------------------------------------- lib/package.gi | 41 +- 2 files changed, 25 insertions(+), 3373 deletions(-) delete mode 100644 hpcgap/lib/package.gi diff --git a/hpcgap/lib/package.gi b/hpcgap/lib/package.gi deleted file mode 100644 index b207dfd2ff..0000000000 --- a/hpcgap/lib/package.gi +++ /dev/null @@ -1,3357 +0,0 @@ -############################################################################# -## -#W package.gi GAP Library Frank Celler -#W Alexander Hulpke -## -## -#Y Copyright (C) 1996, Lehrstuhl D für Mathematik, RWTH Aachen, Germany -#Y (C) 1998 School Math and Comp. Sci., University of St Andrews, Scotland -#Y Copyright (C) 2002 The GAP Group -## -## This file contains support for &GAP; packages. -## - - -# recode string to GAPInfo.TermEncoding, assuming input is UTF-8 or latin1 -# (if useful this may become documented for general use) -BindGlobal( "RecodeForCurrentTerminal", function( str ) - local fun, u; - if IsBoundGlobal( "Unicode" ) and IsBoundGlobal( "Encode" ) then - # The GAPDoc package is completely loaded. - fun:= ValueGlobal( "Unicode" ); - u:= fun( str, "UTF-8" ); - if u = fail then - u:= fun( str, "ISO-8859-1"); - fi; - if GAPInfo.TermEncoding <> "UTF-8" then - fun:= ValueGlobal( "SimplifiedUnicodeString" ); - u:= fun( u, GAPInfo.TermEncoding ); - fi; - fun:= ValueGlobal( "Encode" ); - u:= fun( u, GAPInfo.TermEncoding ); - return u; - else - # GAPDoc is not yet available, do nothing in this case. - return str; - fi; - end ); - -############################################################################# -## -#F CompareVersionNumbers( , [, "equal"] ) -## -InstallGlobalFunction( CompareVersionNumbers, function( arg ) - local s, r, inequal, i, j, a, b; - - s:= arg[1]; - r:= arg[2]; - inequal:= not ( Length( arg ) = 3 and arg[3] = "equal" ); - - # Deal with the case of a `dev' version. - if 2 < Length( s ) - and s{ [ Length( s ) - 2 .. Length( s ) ] } = "dev" then - return inequal or ( Length(r)>2 and r{[Length(r)-2..Length(r)]}="dev" ); - elif 2 < Length( r ) - and r{ [ Length( r ) - 2 .. Length( r ) ] } = "dev" then - return false; - fi; - - while 0 < Length( s ) or 0 < Length( r ) do - - # Remove leading non-digit characters. - i:= 1; - while i <= Length( s ) and not IsDigitChar( s[i] ) do - i:= i+1; - od; - s:= s{ [ i .. Length( s ) ] }; - j:= 1; - while j <= Length( r ) and not IsDigitChar( r[j] ) do - j:= j+1; - od; - r:= r{ [ j .. Length( r ) ] }; - - # If one of the two strings is empty then we are done. - if Length( s ) = 0 then - return Length( r ) = 0; - elif Length( r ) = 0 then - return inequal; - fi; - - # Compare the next portion of digit characters. - i:= 1; - while i <= Length( s ) and IsDigitChar( s[i] ) do - i:= i+1; - od; - a:= Int( s{ [ 1 .. i-1 ] } ); - j:= 1; - while j <= Length( r ) and IsDigitChar( r[j] ) do - j:= j+1; - od; - b:= Int( r{ [ 1 .. j-1 ] } ); - if a < b then - return false; - elif b < a then - return inequal; - fi; - s:= s{ [ i .. Length( s ) ] }; - r:= r{ [ j .. Length( r ) ] }; - - od; - - # The two remaining strings are empty. - return true; -end ); - - -############################################################################# -## -#F PackageInfo( ) -## -InstallGlobalFunction( PackageInfo, function( pkgname ) - pkgname:= LowercaseString( pkgname ); - if not IsBound( GAPInfo.PackagesInfo.( pkgname ) ) then - return []; - else - return GAPInfo.PackagesInfo.( pkgname ); - fi; - end ); - - -############################################################################# -## -#F RECORDS_FILE( ) -## -InstallGlobalFunction( RECORDS_FILE, function( name ) - local str, rows, recs, pos, r; - - str:= StringFile( name ); - if str = fail then - return []; - fi; - rows:= SplitString( str, "", "\n" ); - recs:= []; - for r in rows do - # remove comments starting with `#' - pos:= Position( r, '#' ); - if pos <> fail then - r:= r{ [ 1 .. pos-1 ] }; - fi; - Append( recs, SplitString( r, "", " \n\t\r" ) ); - od; - return List( recs, LowercaseString ); - end ); - - -############################################################################# -## -#F SetPackageInfo( ) -## -InstallGlobalFunction( SetPackageInfo, function( record ) - local rnam, info; - info := rec(); - for rnam in REC_NAMES(record) do - info.(rnam) := Immutable(record.(rnam)); - od; - GAPInfo.PackageInfoCurrent:= info; - end ); - - -############################################################################# -## -#F InitializePackagesInfoRecords() -## -## In earlier versions, this function had an argument; now we ignore it. -## -InstallGlobalFunction( InitializePackagesInfoRecords, function( arg ) - local dirs, pkgdirs, pkgdir, ignore, names, noauto, name, pkgpath, - file, files, subdir, str, record, r, pkgname, version; - - if IsBound( GAPInfo.PackagesInfoInitialized ) and - GAPInfo.PackagesInfoInitialized = true then - # This function has already been executed in this sesion. - return; - fi; - - GAPInfo.LoadPackageLevel:= 0; - GAPInfo.PackagesInfo:= []; - GAPInfo.PackagesInfoRefuseLoad:= []; - - LogPackageLoadingMessage( PACKAGE_DEBUG, - "entering InitializePackagesInfoRecords", "GAP" ); - dirs:= []; - pkgdirs:= DirectoriesLibrary( "pkg" ); - if pkgdirs = fail then - LogPackageLoadingMessage( PACKAGE_DEBUG, - "exit InitializePackagesInfoRecords (no pkg directories found)", - "GAP" ); - GAPInfo.PackagesInfo:= AtomicRecord(); - return; - fi; - - if IsBound( GAPInfo.ExcludeFromAutoload ) then - # The function was called from `AutoloadPackages'. - # Collect the `NOAUTO' information in a global list, - # which will be used in `AutoloadPackages' for both packages to be - # loaded according to the user preference "PackagesToLoad" - # and suggested packages of needed packages. - # The component `GAPInfo.ExcludeFromAutoload' will be unbound after the - # call of `AutoloadPackages'. - GAPInfo.ExcludeFromAutoload:= Set( List( - UserPreference( "ExcludeFromAutoload" ), LowercaseString ) ); - fi; - - # Do not store information about packages in "PackagesToIgnore". - ignore:= List( UserPreference( "PackagesToIgnore" ), LowercaseString ); - - # 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 - - 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 - pkgpath:= Filename( [ pkgdir ], name ); - # This can be 'fail' if 'name' is a void link. - if pkgpath <> fail and IsDirectoryPath( pkgpath ) - and not name in [ ".", ".." ] then - file:= Filename( [ pkgdir ], - Concatenation( name, "/PackageInfo.g" ) ); - if file = fail then - # Perhaps some subdirectories contain `PackageInfo.g' files. - files:= []; - for subdir in Set( DirectoryContents( pkgpath ) ) do - if not subdir in [ ".", ".." ] then - pkgpath:= Filename( [ pkgdir ], - Concatenation( name, "/", subdir ) ); - if pkgpath <> fail and IsDirectoryPath( pkgpath ) - and not subdir in [ ".", ".." ] then - file:= Filename( [ pkgdir ], - Concatenation( name, "/", subdir, "/PackageInfo.g" ) ); - if file <> fail then - Add( files, - [ file, Concatenation( name, "/", subdir ) ] ); - fi; - fi; - fi; - od; - else - files:= [ [ file, name ] ]; - fi; - - for file in files do - - # Read the `PackageInfo.g' file. - Unbind( GAPInfo.PackageInfoCurrent ); - Read( file[1] ); - if IsBound( GAPInfo.PackageInfoCurrent ) then - record:= GAPInfo.PackageInfoCurrent; - Unbind( GAPInfo.PackageInfoCurrent ); - pkgname:= LowercaseString( record.PackageName ); - NormalizeWhitespace( pkgname ); - version:= record.Version; - - # If we have this version already then leave it out. - if ForAll( GAPInfo.PackagesInfo, - r -> r.PackageName <> record.PackageName - or r.Version <> version ) then - - # Check whether GAP wants to reset loadability. - if IsBound( GAPInfo.PackagesRestrictions.( pkgname ) ) - and GAPInfo.PackagesRestrictions.( pkgname ).OnInitialization( - record ) = false then - Add( GAPInfo.PackagesInfoRefuseLoad, record ); - elif LowercaseString( pkgname ) in ignore then - LogPackageLoadingMessage( PACKAGE_DEBUG, - Concatenation( "ignore package ", record.PackageName, - " (user preference PackagesToIgnore)" ), "GAP" ); - else - record.InstallationPath:= Filename( [ pkgdir ], file[2] ); - if not IsBound( record.PackageDoc ) then - record.PackageDoc:= []; - elif IsRecord( record.PackageDoc ) then - record.PackageDoc:= [ record.PackageDoc ]; - fi; - Add( GAPInfo.PackagesInfo, MakeImmutable(record) ); - fi; - fi; - fi; - od; - fi; - od; - od; - - # Sort the available info records by their version numbers. - SortParallel( List( GAPInfo.PackagesInfo, r -> r.Version ), - GAPInfo.PackagesInfo, - CompareVersionNumbers ); - - # Turn the lists into records. - record:= rec(); - for r in GAPInfo.PackagesInfo do - name:= MakeImmutable( LowercaseString( r.PackageName ) ); - if IsBound( record.( name ) ) then - record.( name ) := MakeImmutable( Concatenation( record.( name ), [ r ] ) ); - else - record.( name ):= MakeImmutable( [ r ] ); - fi; - od; - GAPInfo.PackagesInfo:= AtomicRecord(record); - - GAPInfo.PackagesInfoInitialized:= true; - LogPackageLoadingMessage( PACKAGE_DEBUG, - "return from InitializePackagesInfoRecords", "GAP" ); - end ); - - -############################################################################# -## -#F LinearOrderByPartialWeakOrder( , ) -## -## The algorithm works with a directed graph -## whose vertices are subsets of the c_i -## and whose edges represent the given partial order. -## We start with one vertex for each x_i and each y_i -## from the input list, and draw an edge from x_i to y_i. -## Furthermore, -## we need a queue Q of the smallest vertices found up to now, -## and a stack S of the largest vertices found up to now; -## both Q and S are empty at the beginning. -## Now we add the vertices without predecessors to Q and remove the -## edges from these vertices until no more such vertex is found. -## Then we put the vertices without successors on S and remove the -## edges to these vertices until no more such vertex is found. -## If edges are left then each of them leads eventually into a cycle in the -## graph; we find a cycle and amalgamate it into a new vertex. -## Now we repeat the process until all edges have disappeared. -## Finally, the concatenation of Q and S gives us the sets -## c_i. -## -InstallGlobalFunction( LinearOrderByPartialWeakOrder, - function( pairs, weights ) - local Q, S, Qw, Sw, F, pair, vx, vy, v, pos, candidates, minwght, - smallest, s, maxwght, largest, p, cycle, next, new; - - # Initialize the queue and the stack. - Q:= []; - S:= []; - Qw:= []; - Sw:= []; - - # Create a list of vertices according to the input. - F:= []; - for pair in Set( pairs ) do - if pair[1] <> pair[2] then - vx:= First( F, r -> r.keys[1] = pair[1] ); - if vx = fail then - vx:= rec( keys:= [ pair[1] ], succ:= [], pred:= [] ); - Add( F, vx ); - fi; - vy:= First( F, r -> r.keys[1] = pair[2] ); - if vy = fail then - vy:= rec( keys:= [ pair[2] ], succ:= [], pred:= [] ); - Add( F, vy ); - fi; - Add( vx.succ, vy ); - Add( vy.pred, vx ); - fi; - od; - - # Assign the weights. - weights:= SortedList( weights ); - for v in F do - pos:= PositionSorted( weights, v.keys ); - if pos <= Length( weights ) and weights[ pos ][1] = v.keys[1] then - v.wght:= weights[ pos ][2]; - else - v.wght:= 0; - fi; - od; - - # While F contains a vertex, ... - while not IsEmpty( F ) do - - # ... find the vertices in F without predecessors and add them to Q, - # remove the edges from these vertices, - # and remove these vertices from F. - candidates:= Filtered( F, v -> IsEmpty( v.pred ) ); - if not IsEmpty( candidates ) then - minwght:= infinity; # larger than all admissible weights - for v in candidates do - if v.wght < minwght then - minwght:= v.wght; - smallest:= [ v ]; - elif v.wght = minwght then - Add( smallest, v ); - fi; - od; - for v in smallest do - Add( Q, v.keys ); - Add( Qw, v.wght ); - for s in v.succ do - s.pred:= Filtered( s.pred, x -> not IsIdenticalObj( v, x ) ); - if IsEmpty( s.pred ) - and ForAll( smallest, x -> not IsIdenticalObj( s, x ) ) then - Add( smallest, s ); - fi; - od; - pos:= PositionProperty( F, x -> IsIdenticalObj( v, x ) ); - Unbind( F[ pos ] ); - F:= Compacted( F ); - od; - fi; - - # Then find the vertices in F without successors and put them on S, - # remove the edges to these vertices, - # and remove these vertices from F. - candidates:= Filtered( F, v -> IsEmpty( v.succ ) ); - if not IsEmpty( candidates ) then - maxwght:= -1; # smaller than all admissible weights - for v in candidates do - if v.wght > maxwght then - maxwght:= v.wght; - largest:= [ v ]; - elif v.wght = maxwght then - Add( largest, v ); - fi; - od; - for v in largest do - Add( S, v.keys ); - Add( Sw, v.wght ); - for p in v.pred do - p.succ:= Filtered( p.succ, x -> not IsIdenticalObj( v, x ) ); - if IsEmpty( p.succ ) - and ForAll( largest, x -> not IsIdenticalObj( p, x ) ) then - Add( largest, p ); - fi; - od; - pos:= PositionProperty( F, x -> IsIdenticalObj( v, x ) ); - Unbind( F[ pos ] ); - F:= Compacted( F ); - od; - fi; - - if not IsEmpty( F ) then - # Find a cycle in F. - # (Note that now any vertex has a successor, - # so we may start anywhere, and eventually get into a cycle.) - cycle:= []; - next:= F[1]; - repeat - Add( cycle, next ); - next:= next.succ[1]; - pos:= PositionProperty( cycle, x -> IsIdenticalObj( x, next ) ); - until pos <> fail; - cycle:= cycle{ [ pos .. Length( cycle ) ] }; - - # Replace the set of vertices in the cycle by a new vertex, - # replace all edges from/to a vertex outside the cycle - # to/from a vertex in the cycle by edges to/from the new vertex. - new:= rec( keys:= [], succ:= [], pred:= [], - wght:= Maximum( List( cycle, v -> v.wght ) ) ); - for v in cycle do - UniteSet( new.keys, v.keys ); - for s in v.succ do - if ForAll( cycle, w -> not IsIdenticalObj( s, w ) ) then - if ForAll( new.succ, w -> not IsIdenticalObj( s, w ) ) then - Add( new.succ, s ); - fi; - pos:= PositionProperty( s.pred, w -> IsIdenticalObj( v, w ) ); - if ForAll( s.pred, w -> not IsIdenticalObj( new, w ) ) then - s.pred[ pos ]:= new; - else - Unbind( s.pred[ pos ] ); - s.pred:= Compacted( s.pred ); - fi; - fi; - od; - for p in v.pred do - if ForAll( cycle, w -> not IsIdenticalObj( p, w ) ) then - if ForAll( new.pred, w -> not IsIdenticalObj( p, w ) ) then - Add( new.pred, p ); - fi; - pos:= PositionProperty( p.succ, w -> IsIdenticalObj( v, w ) ); - if ForAll( p.succ, w -> not IsIdenticalObj( new, w ) ) then - p.succ[ pos ]:= new; - else - Unbind( p.succ[ pos ] ); - p.succ:= Compacted( p.succ ); - fi; - fi; - od; - pos:= PositionProperty( F, x -> IsIdenticalObj( v, x ) ); - Unbind( F[ pos ] ); - F:= Compacted( F ); - od; - Add( F, new ); - fi; - - od; - - # Now the whole input is distributed to Q and S. - return rec( cycles:= Concatenation( Q, Reversed( S ) ), - weights:= Concatenation( Qw, Reversed( Sw ) ) ); - end ); - - -############################################################################# -## -#I InfoPackageLoading -## -## (We cannot do this in `package.gd'.) -## -DeclareInfoClass( "InfoPackageLoading" ); - - -############################################################################# -## -#F LogPackageLoadingMessage( , [, ] ) -## -if not IsBound( TextAttr ) then - TextAttr:= "dummy"; -fi; -#T needed? (decl. of GAPDoc is loaded before) - -InstallGlobalFunction( LogPackageLoadingMessage, function( arg ) - local severity, message, currpkg, i; - - severity:= arg[1]; - message:= arg[2]; - if Length( arg ) = 3 then - currpkg:= arg[3]; - elif IsBound( GAPInfo.PackageCurrent ) then - # This happens inside availability tests. - currpkg:= GAPInfo.PackageCurrent.PackageName; - else - currpkg:= "(unknown package)"; - fi; - if IsString( message ) then - message:= [ message ]; - fi; - if severity <= PACKAGE_WARNING - and UserPreference("UseColorsInTerminal") = true - and IsBound( TextAttr ) - and IsRecord( TextAttr ) then - if severity = PACKAGE_ERROR then - message:= List( message, - msg -> Concatenation( TextAttr.1, msg, TextAttr.reset ) ); - else - message:= List( message, - msg -> Concatenation( TextAttr.4, msg, TextAttr.reset ) ); - fi; - fi; - Add( GAPInfo.PackageLoadingMessages, [ currpkg, severity, message ] ); - Info( InfoPackageLoading, severity, currpkg, ": ", message[1] ); - for i in [ 2 .. Length( message ) ] do - Info( InfoPackageLoading, severity, List( currpkg, x -> ' ' ), - " ", message[i] ); - od; - end ); - -if not IsReadOnlyGlobal( "TextAttr" ) then - Unbind( TextAttr ); -fi; - - -############################################################################# -## -#F DisplayPackageLoadingLog( [] ) -## -InstallGlobalFunction( DisplayPackageLoadingLog, function( arg ) - local severity, entry, message, i; - - if Length( arg ) = 0 then - severity:= PACKAGE_WARNING; - else - severity:= arg[1]; - fi; - - for entry in GAPInfo.PackageLoadingMessages do - if severity >= entry[2] then - message:= entry[3]; - Info( InfoPackageLoading, 1, entry[1], ": ", message[1] ); - for i in [ 2 .. Length( message ) ] do - Info( InfoPackageLoading, 1, List( entry[1], x -> ' ' ), - " ", message[i] ); - od; - fi; - od; - end ); - - -############################################################################# -## -#F PackageAvailabilityInfo( , , , , -#F ) -## -InstallGlobalFunction( PackageAvailabilityInfo, - function( name, version, record, suggested, checkall ) - local InvalidStrongDependencies, Name, equal, comp, pair, currversion, - inforec, skip, msg, dep, record_local, wght, pos, needed, test, - name2, testpair; - - InvalidStrongDependencies:= function( dependencies, weights, - strong_dependencies ) - local result, order, pair, cycle; - - result:= false; - if not IsEmpty( strong_dependencies ) then - order:= LinearOrderByPartialWeakOrder( dependencies, weights ).cycles; - for pair in strong_dependencies do - for cycle in order do - if IsSubset( cycle, pair ) then - # This condition was imposed by some - # `OtherPackagesLoadedInAdvance' component. - LogPackageLoadingMessage( PACKAGE_INFO, - [ Concatenation( "PackageAvailabilityInfo: package '", - pair[1], "'" ), - Concatenation( "shall be loaded before package '", name, - "' but must be" ), - "in the same load cycle, due to other dependencies" ], - Name ); - result:= true; - if not checkall then - return result; - fi; - fi; - od; - od; - fi; - return result; - end; - - Name:= name; - name:= LowercaseString( name ); - equal:= ""; - if 0 < Length( version ) and version[1] = '=' then - equal:= "equal"; - fi; - - if name = "gap" then - # This case occurs if a package requires a particular GAP version. - return CompareVersionNumbers( GAPInfo.Version, version, equal ); - fi; - - # 1. If the package `name' is already loaded then compare the version - # number of the loaded package with the required one. - # (Note that at most one version of a package can be available.) - if IsBound( GAPInfo.PackagesLoaded.( name ) ) then - return CompareVersionNumbers( GAPInfo.PackagesLoaded.( name )[2], - version, equal ); - fi; - - # 2. If the function was called from `AutoloadPackages' - # and if the package is listed among the packages to be excluded - # from autoload then exit. - if IsBound( GAPInfo.ExcludeFromAutoload ) - and name in GAPInfo.ExcludeFromAutoload then - LogPackageLoadingMessage( PACKAGE_DEBUG, - "package to be excluded from autoload, return 'false'", Name ); - return false; - fi; - - # 3. Initialize the dependency info. - for comp in [ "AlreadyHandled", "Dependencies", "StrongDependencies", - "InstallationPaths", "Weights" ] do - if not IsBound( record.( comp ) ) then - record.( comp ):= []; - fi; - od; - - # 4. Deal with the case that `name' is among the packages - # from whose tests the current check for `name' arose. - for pair in record.AlreadyHandled do - if name = pair[1] then - if CompareVersionNumbers( pair[2], version, equal ) then - # The availability of the package will be decided on an outer level. - return fail; - else - # The version assumed on an outer level does not fit. - return false; - fi; - fi; - od; - - # 5. In recursive calls, regard the current package as handled, - # of course in the version in question. - currversion:= [ name ]; - Add( record.AlreadyHandled, currversion ); - - # 6. Get the info records for the package `name', - # and take the first record that satisfies the conditions. - # (Note that they are ordered w.r.t. descending version numbers.) - for inforec in PackageInfo( name ) do - - Name:= inforec.PackageName; - skip:= false; - currversion[2]:= inforec.Version; - - if IsBound( inforec.Dependencies ) then - dep:= inforec.Dependencies; - else - dep:= rec(); - fi; - - # Test whether this package version fits. - msg:= [ Concatenation( "PackageAvailabilityInfo for version ", - inforec.Version ) ]; - if version <> "" then - if not CompareVersionNumbers( inforec.Version, version, equal ) then - # The severity of the log message must be less than `PACKAGE_INFO', - # since we do not want to see the message when looking for reasons - # why the current package cannot be loaded. - LogPackageLoadingMessage( PACKAGE_DEBUG, - [ Concatenation( "PackageAvailabilityInfo: version ", - inforec.Version, " does not fit" ), - Concatenation( "(required: ", version, ")" ) ], - inforec.PackageName ); - if not checkall then - continue; - fi; - skip:= true; - else - Add( msg, Concatenation( "(required: ", version, ")" ) ); - LogPackageLoadingMessage( PACKAGE_INFO, msg, - inforec.PackageName ); - fi; - else - LogPackageLoadingMessage( PACKAGE_INFO, msg, - inforec.PackageName ); - fi; - - # Test whether the required GAP version fits. - if IsBound( dep.GAP ) - and not CompareVersionNumbers( GAPInfo.Version, dep.GAP ) then - LogPackageLoadingMessage( PACKAGE_INFO, - Concatenation( "PackageAvailabilityInfo: required GAP version (", - dep.GAP, ") does not fit", - inforec.PackageName ) ); - if not checkall then - continue; - fi; - skip:= true; - fi; - - # Test whether the availability test function fits. - GAPInfo.PackageCurrent:= inforec; - test:= inforec.AvailabilityTest(); - Unbind( GAPInfo.PackageCurrent ); - if test = true then - LogPackageLoadingMessage( PACKAGE_DEBUG, - Concatenation( "PackageAvailabilityInfo: the AvailabilityTest", - " function returned 'true'" ), - inforec.PackageName ); - else - LogPackageLoadingMessage( PACKAGE_INFO, - Concatenation( "PackageAvailabilityInfo: the AvailabilityTest", - " function returned ", String( test ) ), - inforec.PackageName ); - if not checkall then - continue; - fi; - skip:= true; - fi; - - # Locate the `init.g' file of the package. - if Filename( [ Directory( inforec.InstallationPath ) ], "init.g" ) - = fail then - LogPackageLoadingMessage( PACKAGE_WARNING, - Concatenation( "PackageAvailabilityInfo: cannot locate `", - inforec.InstallationPath, - "/init.g', please check the installation" ), - inforec.PackageName ); - if not checkall then - continue; - fi; - skip:= true; - fi; - - record_local:= StructuralCopy( record ); - - # If the GAP library is not yet loaded then assign - # weight 0 to all packages that may be loaded before the GAP library, - # and weight 1 to those that need the GAP library to be loaded - # in advance. - # The latter means that either another package or the GAP library - # itself is forced to be loaded in advance, - # for example because the current package has no `read.g' file. - if Filename( [ Directory( inforec.InstallationPath ) ], "read.g" ) - = fail or - ( not IsBound( GAPInfo.LibraryLoaded ) and - IsBound( dep.OtherPackagesLoadedInAdvance ) and - not IsEmpty( dep.OtherPackagesLoadedInAdvance ) ) then - wght:= 1; - else - wght:= 0; - fi; - pos:= PositionProperty( record_local.Weights, pair -> pair[1] = name ); - if pos = fail then - Add( record_local.Weights, [ name, wght ] ); - else - record_local.Weights[ pos ][2]:= wght; - fi; - - # Check the dependencies of this package version. - needed:= []; - if IsBound( dep.OtherPackagesLoadedInAdvance ) then - Append( record_local.StrongDependencies, - List( dep.OtherPackagesLoadedInAdvance, - x -> [ LowercaseString( x[1] ), name ] ) ); - Append( needed, dep.OtherPackagesLoadedInAdvance ); - fi; - if IsBound( dep.NeededOtherPackages ) then - Append( needed, dep.NeededOtherPackages ); - fi; - test:= true; - if IsEmpty( needed ) then - LogPackageLoadingMessage( PACKAGE_DEBUG, - "PackageAvailabilityInfo: no needed packages", - inforec.PackageName ); - else - LogPackageLoadingMessage( PACKAGE_DEBUG, Concatenation( - [ "PackageAvailabilityInfo: check needed packages" ], - List( needed, - pair -> Concatenation( pair[1], " (", pair[2], ")" ) ) ), - inforec.PackageName ); - for pair in needed do - name2:= LowercaseString( pair[1] ); - testpair:= PackageAvailabilityInfo( name2, pair[2], record_local, - suggested, checkall ); - if testpair = false then - # This dependency is not satisfied. - test:= false; - LogPackageLoadingMessage( PACKAGE_INFO, - Concatenation( "PackageAvailabilityInfo: dependency '", - name2, "' is not satisfied" ), inforec.PackageName ); - if not checkall then - # Skip the check of other dependencies. - break; - fi; - elif testpair <> true then - # The package `name2' is available but not yet loaded. - Add( record_local.Dependencies, [ name2, name ] ); - fi; - od; - LogPackageLoadingMessage( PACKAGE_DEBUG, - "PackageAvailabilityInfo: check of needed packages done", - inforec.PackageName ); - fi; - if test = false then - # At least one package needed by this version is not available, - if not checkall then - continue; - fi; - skip:= true; - fi; - - if InvalidStrongDependencies( record_local.Dependencies, - record_local.Weights, record_local.StrongDependencies ) then - # This package version cannot be loaded due to conditions - # imposed by `OtherPackagesLoadedInAdvance' components. - # (Log messages are added inside the function.) - if not checkall then - continue; - fi; - skip:= true; - fi; - - # All checks for this version have been performed. - # Go to the next installed version if some check failed. - if skip then - continue; - fi; - - # The version given by `inforec' will be taken. - # Copy the information back to the argument record. - record.InstallationPaths:= record_local.InstallationPaths; - Add( record.InstallationPaths, - [ name, [ inforec.InstallationPath, inforec.Version, - inforec.PackageName ] ] ); - record.Dependencies:= record_local.Dependencies; - record.StrongDependencies:= record_local.StrongDependencies; - record.AlreadyHandled:= record_local.AlreadyHandled; - record.Weights:= record_local.Weights; - - if suggested and IsBound( dep.SuggestedOtherPackages ) then - # Collect info about suggested packages and their dependencies. - LogPackageLoadingMessage( PACKAGE_DEBUG, Concatenation( - [ "PackageAvailabilityInfo: check suggested packages" ], - List( dep.SuggestedOtherPackages, - pair -> Concatenation( pair[1], " (", pair[2], ")" ) ) ), - inforec.PackageName ); - for pair in dep.SuggestedOtherPackages do - name2:= LowercaseString( pair[1] ); - # Do not change the information collected up to now - # until we are sure that we will really use the suggested package. - record_local:= StructuralCopy( record ); - test:= PackageAvailabilityInfo( name2, pair[2], record_local, - suggested, checkall ); - if test <> true then - Add( record_local.Dependencies, [ name2, name ] ); - if IsString( test ) then - if InvalidStrongDependencies( record_local.Dependencies, - record_local.Weights, - record_local.StrongDependencies ) then - test:= false; - fi; - fi; - if test <> false then - record.InstallationPaths:= record_local.InstallationPaths; - record.Dependencies:= record_local.Dependencies; - record.StrongDependencies:= record_local.StrongDependencies; - record.AlreadyHandled:= record_local.AlreadyHandled; - record.Weights:= record_local.Weights; - fi; - fi; - od; - LogPackageLoadingMessage( PACKAGE_DEBUG, - "PackageAvailabilityInfo: check of suggested packages done", - inforec.PackageName ); - fi; - - # Print a warning if the package should better be upgraded. - if IsBound( GAPInfo.PackagesRestrictions.( name ) ) then - GAPInfo.PackagesRestrictions.( name ).OnLoad( inforec ); - fi; -#T component name OnLoad: -#T shouldn't this be done only if the package is actually loaded? - - LogPackageLoadingMessage( PACKAGE_INFO, - Concatenation( "PackageAvailabilityInfo: version ", - inforec.Version, " is available" ), - inforec.PackageName ); - - return inforec.InstallationPath; - - od; - - # No info record satisfies the requirements. - if not IsBound( GAPInfo.PackagesInfo.( name ) ) then - inforec:= First( GAPInfo.PackagesInfoRefuseLoad, - r -> LowercaseString( r.PackageName ) = name ); - if inforec <> fail then - # Some versions are installed but all were refused. - GAPInfo.PackagesRestrictions.( name ).OnLoad( inforec ); - fi; - fi; - - LogPackageLoadingMessage( PACKAGE_INFO, - Concatenation( "PackageAvailabilityInfo: ", - "no installed version fits" ), Name ); - - return false; -end ); - - -############################################################################# -## -#F TestPackageAvailability( [, ][, ] ) -## -InstallGlobalFunction( TestPackageAvailability, function( arg ) - local name, version, checkall, result; - - # Get the arguments. - name:= LowercaseString( arg[1] ); - version:= ""; - checkall:= false; - if Length( arg ) = 2 then - if IsString( arg[2] ) then - version:= arg[2]; - elif arg[2] = true then - checkall:= true; - fi; - elif Length( arg ) = 3 then - if IsString( arg[2] ) then - version:= arg[2]; - fi; - if arg[3] = true then - checkall:= true; - fi; - fi; - - # Ignore suggested packages. - result:= PackageAvailabilityInfo( name, version, rec(), false, - checkall ); - - if result = false then - return fail; - else - return result; - fi; - end ); - - -############################################################################# -## -#F IsPackageMarkedForLoading( , ) -## -InstallGlobalFunction( IsPackageMarkedForLoading, function( name, version ) - local equal; - - equal:= ""; - if 0 < Length( version ) and version[1] = '=' then - equal:= "equal"; - fi; - name:= LowercaseString( name ); - return IsBound( GAPInfo.PackagesLoaded.( name ) ) - and CompareVersionNumbers( GAPInfo.PackagesLoaded.( name )[2], - version, equal ); - end ); - - -############################################################################# -## -#F DefaultPackageBannerString( ) -## -InstallGlobalFunction( DefaultPackageBannerString, function( inforec ) - local len, sep, i, str, authors, role, fill, person; - - # Start with a row of `-' signs. - len:= SizeScreen()[1] - 3; - if GAPInfo.TermEncoding = "UTF-8" then - # The unicode character we use takes up 3 bytes in UTF-8 encoding, - # hence we must adjust the length accordingly. - sep:= "─"; - i:= 1; - while 2 * i <= len do - Append( sep, sep ); - i:= 2 * i; - od; - Append( sep, sep{ [ 1 .. 3 * ( len - i ) ] } ); - else - sep:= ListWithIdenticalEntries( len, '-' ); - fi; - Add( sep, '\n' ); - - str:= ""; - - # Add package name and version number. - if IsBound( inforec.PackageName ) and IsBound( inforec.Version ) then - Append( str, Concatenation( - "Loading ", inforec.PackageName, " ", inforec.Version ) ); - fi; - - # Add the long title. - if IsBound( inforec.PackageDoc ) and IsBound( inforec.PackageDoc[1] ) and - IsBound( inforec.PackageDoc[1].LongTitle ) and - not IsEmpty( inforec.PackageDoc[1].LongTitle ) then - Append( str, Concatenation( - " (", inforec.PackageDoc[1].LongTitle, ")" ) ); - fi; - Add( str, '\n' ); - - # Add info about the authors if there are authors; - # otherwise add maintainers. - if IsBound( inforec.Persons ) then - authors:= Filtered( inforec.Persons, x -> x.IsAuthor ); - role:= "by "; - if IsEmpty( authors ) then - authors:= Filtered( inforec.Persons, x -> x.IsMaintainer ); - role:= "maintained by "; - fi; - fill:= ListWithIdenticalEntries( Length(role), ' ' ); - Append( str, role ); - for i in [ 1 .. Length( authors ) ] do - person:= authors[i]; - Append( str, person.FirstNames ); - Append( str, " " ); - Append( str, person.LastName ); - if IsBound( person.WWWHome ) then - Append( str, Concatenation( " (", person.WWWHome, ")" ) ); - elif IsBound( person.Email ) then - Append( str, Concatenation( " (", person.Email, ")" ) ); - fi; - if i = Length( authors ) then - Append( str, ".\n" ); - elif i = Length( authors )-1 then - if i = 1 then - Append( str, " and\n" ); - else - Append( str, ", and\n" ); - fi; - Append( str, fill ); - else - Append( str, ",\n" ); - Append( str, fill ); - fi; - od; - fi; - - # Add info about the home page of the package. - if IsBound( inforec.PackageWWWHome ) then - Append( str, "Homepage: " ); - Append( str, inforec.PackageWWWHome ); - Append( str, "\n" ); - fi; - - # temporary hack, in some package names with umlauts are in HTML encoding - str := Concatenation(sep, RecodeForCurrentTerminal(str), sep); - str:= ReplacedString( str, "ä", RecodeForCurrentTerminal("ä") ); - str:= ReplacedString( str, "ö", RecodeForCurrentTerminal("ö") ); - str:= ReplacedString( str, "ü", RecodeForCurrentTerminal("ü") ); - - return str; - end ); - - -############################################################################# -## -#F DirectoriesPackagePrograms( ) -## -InstallGlobalFunction( DirectoriesPackagePrograms, function( name ) - local arch, dirs, info, version, r, path; - - arch := GAPInfo.Architecture; - dirs := []; - # We are not allowed to call - # `InstalledPackageVersion', `TestPackageAvailability' etc. - info:= PackageInfo( name ); - if IsBound( GAPInfo.PackagesLoaded.( name ) ) then - # The package is already loaded. - version:= GAPInfo.PackagesLoaded.( name )[2]; - elif IsBound( GAPInfo.PackageCurrent ) then - # The package is currently going to be loaded. - version:= GAPInfo.PackageCurrent.Version; - elif 0 < Length( info ) then - # Take the installed package with the highest version. - version:= info[1].Version; - fi; - for r in info do - if r.Version = version then - path:= Concatenation( r.InstallationPath, "/bin/", arch, "/" ); - Add( dirs, Directory( path ) ); - fi; - od; - return dirs; -end ); - - -############################################################################# -## -#F DirectoriesPackageLibrary( [, ] ) -## -InstallGlobalFunction( DirectoriesPackageLibrary, function( arg ) - local name, path, dirs, info, version, r, tmp; - - if IsEmpty(arg) or 2 < Length(arg) then - Error( "usage: DirectoriesPackageLibrary( [, ] )\n" ); - elif not ForAll(arg, IsString) then - Error( "string argument(s) expected\n" ); - fi; - - name:= LowercaseString( arg[1] ); - if '\\' in name or ':' in name then - Error( " must not contain '\\' or ':'" ); - elif 1 = Length(arg) then - path := "lib"; - else - path := arg[2]; - fi; - - dirs := []; - # We are not allowed to call - # `InstalledPackageVersion', `TestPackageAvailability' etc. - info:= PackageInfo( name ); - if IsBound( GAPInfo.PackagesLoaded.( name ) ) then - # The package is already loaded. - version:= GAPInfo.PackagesLoaded.( name )[2]; - elif IsBound( GAPInfo.PackageCurrent ) then - # The package is currently going to be loaded. - version:= GAPInfo.PackageCurrent.Version; - elif 0 < Length( info ) then - # Take the installed package with the highest version. - version:= info[1].Version; - fi; - for r in info do - if r.Version = version then - tmp:= Concatenation( r.InstallationPath, "/", path ); - if IsDirectoryPath( tmp ) = true then - Add( dirs, Directory( tmp ) ); - fi; - fi; - od; - return dirs; -end ); - - -############################################################################# -## -#F ReadPackage( [, ] ) -#F RereadPackage( [, ] ) -## -InstallGlobalFunction( ReadPackage, function( arg ) - local pos, relpath, pkgname, namespace, filename, rflc, rfc; - - # Note that we cannot use `ReadAndCheckFunc' because this calls - # `READ_GAP_ROOT', but here we have to read the file in one of those - # directories where the package version resides that has been loaded - # or (at least currently) would be loaded. - if Length( arg ) = 1 then - # Guess the package name. - pos:= Position( arg[1], '/' ); - if pos = fail then - ErrorNoReturn(arg[1], " is not a filename in the form 'package/filepath'"); - fi; - relpath:= arg[1]{ [ pos+1 .. Length( arg[1] ) ] }; - pkgname:= LowercaseString( arg[1]{ [ 1 .. pos-1 ] } ); - namespace := GAPInfo.PackagesInfo.(pkgname)[1].PackageName; - elif Length( arg ) = 2 then - pkgname:= LowercaseString( arg[1] ); - namespace := GAPInfo.PackagesInfo.(pkgname)[1].PackageName; - relpath:= arg[2]; - else - Error( "expected 1 or 2 arguments" ); - fi; - - # Note that `DirectoriesPackageLibrary' finds the file relative to the - # installation path of the info record chosen in `LoadPackage'. - filename:= Filename( DirectoriesPackageLibrary( pkgname, "" ), relpath ); - if filename <> fail and IsReadableFile( filename ) then - ENTER_NAMESPACE(namespace); - Read( filename ); - LEAVE_NAMESPACE(); - return true; - else - return false; - fi; - end ); - -InstallGlobalFunction( RereadPackage, function( arg ) - local res; - - MakeReadWriteGlobal( "REREADING" ); - REREADING:= true; - MakeReadOnlyGlobal( "REREADING" ); - res:= CallFuncList( ReadPackage, arg ); - MakeReadWriteGlobal( "REREADING" ); - REREADING:= false; - MakeReadOnlyGlobal( "REREADING" ); - return res; - end ); - - -############################################################################# -## -#F LoadPackageDocumentation( ) -## -## In versions before 4.5, a second argument was required. -## For the sake of backwards compatibility, we do not forbid a second -## argument, but we ignore it. -## (In later versions, we may forbid the second argument.) -## -InstallGlobalFunction( LoadPackageDocumentation, function( arg ) - local info, short, pkgdoc, long, sixfile; - - info:= arg[1]; - - # Load all books for the package. - for pkgdoc in info.PackageDoc do - # Fetch the names. - if IsBound( pkgdoc.LongTitle ) then - long:= pkgdoc.LongTitle; - else - long:= Concatenation( "GAP Package `", info.PackageName, "'" ); - fi; - short:= pkgdoc.BookName; - if not IsBound( GAPInfo.PackagesLoaded.( LowercaseString( - info.PackageName ) ) ) then - short:= Concatenation( short, " (not loaded)" ); - fi; - - # Check that the `manual.six' file is available. - sixfile:= Filename( [ Directory( info.InstallationPath ) ], - pkgdoc.SixFile ); - if sixfile = fail then - LogPackageLoadingMessage( PACKAGE_INFO, - Concatenation( [ "book `", pkgdoc.BookName, - "': no manual index file `", - pkgdoc.SixFile, "', ignored" ] ), - info.PackageName ); - else - # Finally notify the book via its directory. -#T Here we assume that this is the directory that contains also `manual.six'! - HELP_ADD_BOOK( short, long, - Directory( sixfile{ [ 1 .. Length( sixfile )-10 ] } ) ); - fi; - od; - end ); - -############################################################################# -## -#F LoadPackage_ReadImplementationParts( , ) -## -BindGlobal( "LoadPackage_ReadImplementationParts", - function( secondrun, banner ) - local pair, info, bannerstring, fun, u, pkgname, namespace; - - for pair in secondrun do - if pair[2] <> fail then - GAPInfo.PackageCurrent:= pair[1]; - namespace := pair[1].PackageName; - pkgname := LowercaseString( namespace ); - LogPackageLoadingMessage( PACKAGE_DEBUG, - "start reading file 'read.g'", - namespace ); - ENTER_NAMESPACE(namespace); - Read( pair[2] ); - LEAVE_NAMESPACE(); - Unbind( GAPInfo.PackageCurrent ); - LogPackageLoadingMessage( PACKAGE_DEBUG, - "finish reading file 'read.g'", - namespace ); - fi; - od; - - # Show the banners. - if banner then - for pair in secondrun do - info:= pair[1]; - - # If the component `BannerString' is bound in `info' then we print - # this string, otherwise we print the default banner string. - if IsBound( info.BannerString ) then - bannerstring:= RecodeForCurrentTerminal(info.BannerString); - else - bannerstring:= DefaultPackageBannerString( info ); - fi; - - # Be aware of umlauts, accents etc. in the banner. - if IsBoundGlobal( "Unicode" ) and IsBoundGlobal( "Encode" ) then - # The GAPDoc package is completely loaded. - fun:= ValueGlobal( "PrintFormattedString" ); - fun( bannerstring ); - else - # GAPDoc is not available, simply print the banner string as is. - Print( bannerstring ); - fi; - od; - fi; - end ); - - -############################################################################# -## -#F GetPackageNameForPrefix( ) . . . . . . . . show list of matches -#F or single match directly -## -## Compute all names of installed packages that match the prefix . -## In case of a unique match return this match, -## otherwise print an info message about the matches and return . -## -## This function is called by `LoadPackage'. -## -BindGlobal( "GetPackageNameForPrefix", function( prefix ) - local len, lowernames, name, allnames, indent, pos, sep; - - len:= Length( prefix ); - lowernames:= []; - for name in Set( RecNames( GAPInfo.PackagesInfo ) ) do - if Length( prefix ) <= Length( name ) and - name{ [ 1 .. len ] } = prefix then - Add( lowernames, name ); - fi; - od; - if IsEmpty( lowernames ) then - # No package name matches. - return prefix; - fi; - allnames:= List( lowernames, - nam -> GAPInfo.PackagesInfo.( nam )[1].PackageName ); - if Length( allnames ) = 1 then - # There is one exact match. - LogPackageLoadingMessage( PACKAGE_DEBUG, Concatenation( - [ "replace prefix '", prefix, "' by the unique completion '", - allnames[1], "'" ] ), allnames[1] ); - return lowernames[1]; - fi; - - # Several package names match. - if 0 < InfoLevel( InfoPackageLoading ) then - Print( "#I Call 'LoadPackage' with one of the following strings:\n" ); - len:= SizeScreen()[1] - 6; - indent:= "#I "; - Print( indent ); - pos:= Length( indent ); - sep:= ""; - for name in allnames do - Print( sep ); - pos:= pos + Length( sep ); - if len < pos + Length( name ) then - Print( "\n", indent ); - pos:= Length( indent ); - fi; - Print( "\"", name, "\"" ); - pos:= pos + Length( name ) + 2; - sep:= ", "; - od; - Print( ".\n" ); - fi; - return prefix; - end ); - - -############################################################################# -## -#F LoadPackage( [, ][, ] ) -## -InstallGlobalFunction( LoadPackage, function( arg ) - local name, Name, version, banner, loadsuggested, msg, depinfo, path, - pair, i, order, paths, cycle, secondrun, pkgname, pos, info, - filename, read; - - # Get the arguments. - if Length( arg ) = 0 then - name:= ""; - else - name:= arg[1]; - if not IsString( name ) then - Error( " must be a string" ); - fi; - name:= LowercaseString( name ); - fi; - if not IsBound( GAPInfo.PackagesInfo.( name ) ) then - name:= GetPackageNameForPrefix( name ); - fi; - - # Return 'fail' if this package is not installed. - if not IsBound( GAPInfo.PackagesInfo.( name ) ) then - LogPackageLoadingMessage( PACKAGE_DEBUG, - "no package with this name is installed, return 'fail'", name ); - if InfoLevel(InfoPackageLoading) < 4 then - Info(InfoWarning,1, name, " package is not available. Check that the name is correct"); - Info(InfoWarning,1, "and it is present in one of the GAP root directories (see '??RootPaths')"); - fi; - return fail; - fi; - - # The package is available, fetch the name for messages. - Name:= GAPInfo.PackagesInfo.( name )[1].PackageName; - version:= ""; - banner:= not GAPInfo.CommandLineOptions.q and - not GAPInfo.CommandLineOptions.b; - if 1 < Length( arg ) then - if IsString( arg[2] ) then - version:= arg[2]; - if 2 < Length( arg ) then - banner:= banner and not ( arg[3] = false ); - fi; - else - banner:= banner and not ( arg[2] = false ); - fi; - fi; - loadsuggested:= ( ValueOption( "OnlyNeeded" ) <> true ); - - # Print a warning if `LoadPackage' is called inside a - # `LoadPackage' call. - if not IsBound( GAPInfo.LoadPackageLevel ) then - GAPInfo.LoadPackageLevel:= 0; - fi; - GAPInfo.LoadPackageLevel:= GAPInfo.LoadPackageLevel + 1; - if GAPInfo.LoadPackageLevel <> 1 then - if IsBound( GAPInfo.PackageCurrent ) then - msg:= GAPInfo.PackageCurrent.PackageName; - else - msg:= "?"; - fi; - LogPackageLoadingMessage( PACKAGE_WARNING, - [ Concatenation( "Do not call `LoadPackage( \"", name, - "\", ... )' in the package file" ), - Concatenation( INPUT_FILENAME(), "," ), - "use `IsPackageMarkedForLoading' instead" ], msg ); - fi; - - # Start logging. - msg:= "entering LoadPackage "; - if not loadsuggested then - Append( msg, " (omitting suggested packages)" ); - fi; - LogPackageLoadingMessage( PACKAGE_DEBUG, msg, Name ); - - # Test whether the package is available, - # and compute the dependency information. - depinfo:= rec(); - path:= PackageAvailabilityInfo( name, version, depinfo, loadsuggested, - false ); - if not IsString( path ) then - if path = false then - path:= fail; - fi; - # The result is either `true' (the package is already loaded) - # or `fail' (the package cannot be loaded). - if path = true then - LogPackageLoadingMessage( PACKAGE_DEBUG, - "return from LoadPackage, package was already loaded", Name ); - else - LogPackageLoadingMessage( PACKAGE_DEBUG, - "return from LoadPackage, package is not available", Name ); - if banner then - if InfoLevel(InfoPackageLoading) < 4 then - Info(InfoWarning,1, Name, " package is not available. To see further details, enter"); - Info(InfoWarning,1, "SetInfoLevel(InfoPackageLoading,4); and try to load the package again."); - fi; - fi; - fi; - GAPInfo.LoadPackageLevel:= GAPInfo.LoadPackageLevel - 1; - return path; - fi; - - # Compute the order in which the packages are loaded. - # For each set of packages with cyclic dependencies, - # we will first read all `init.g' files - # and afterwards all `read.g' files. - if IsEmpty( depinfo.Dependencies ) then - order:= rec( cycles:= [ [ name ] ], - weights:= [ depinfo.Weights[1][2] ] ); - else - order:= LinearOrderByPartialWeakOrder( depinfo.Dependencies, - depinfo.Weights ); - fi; - # paths:= TransposedMatMutable( depinfo.InstallationPaths ); - # (TransposedMatMutable is not yet available here ...) - paths:= [ [], [] ]; - for pair in depinfo.InstallationPaths do - Add( paths[1], pair[1] ); - Add( paths[2], pair[2] ); - od; - SortParallel( paths[1], paths[2] ); - - secondrun:= []; - for i in [ 1 .. Length( order.cycles ) ] do - cycle:= order.cycles[i]; - - # First mark all packages in the current cycle as loaded, - # in order to avoid that an occasional call of `LoadPackage' - # inside the package code causes the files to be read more than once. - for pkgname in cycle do - pos:= PositionSorted( paths[1], pkgname ); - GAPInfo.PackagesLoaded.( pkgname ):= MakeImmutable(paths[2][ pos ]); -#T Remove the following as soon as the obsolete variable has been removed! -if IsBoundGlobal( "PACKAGES_VERSIONS" ) then - ValueGlobal( "PACKAGES_VERSIONS" ).( pkgname ):= paths[2][ pos ][2]; -fi; - od; - - # If the weight is 1 and the GAP library is not yet loaded - # then load the GAP library now. - if order.weights[i] = 1 and not IsBound( GAPInfo.LibraryLoaded ) then - LogPackageLoadingMessage( PACKAGE_DEBUG, - [ "read the impl. part of the GAP library" ], Name ); - ReadGapRoot( "lib/read.g" ); - GAPInfo.LibraryLoaded:= true; - LoadPackage_ReadImplementationParts( Concatenation( - GAPInfo.delayedImplementationParts, secondrun ), false ); - GAPInfo.delayedImplementationParts:= []; - secondrun:= []; - fi; - - if loadsuggested then - msg:= "start loading needed/suggested/self packages"; - else - msg:= "start loading needed/self packages"; - fi; - LogPackageLoadingMessage( PACKAGE_DEBUG, - Concatenation( [ msg ], cycle ), - Name ); - - for pkgname in cycle do - pos:= PositionSorted( paths[1], pkgname ); - info:= First( PackageInfo( pkgname ), - r -> r.InstallationPath = paths[2][ pos ][1] ); - - # This is the first attempt to read stuff for this package. - # So we handle the case of a `PreloadFile' entry. - if IsBound( info.PreloadFile ) then - filename:= UserHomeExpand( info.PreloadFile ); - if filename[1] = '/' then - read:= READ( filename ); - else - read:= ReadPackage( name, filename ); - fi; - if not read then - LogPackageLoadingMessage( PACKAGE_WARNING, - Concatenation( "file `", filename, "' cannot be read" ), - info.PackageName ); - fi; - fi; - - # Notify the documentation (for the available version). - LoadPackageDocumentation( info ); - - # Read the `init.g' files. - LogPackageLoadingMessage( PACKAGE_DEBUG, - "start reading file 'init.g'", - info.PackageName ); - GAPInfo.PackageCurrent:= info; - ReadPackage( pkgname, "init.g" ); - Unbind( GAPInfo.PackageCurrent ); - LogPackageLoadingMessage( PACKAGE_DEBUG, - "finish reading file 'init.g'", - info.PackageName ); - - filename:= Filename( [ Directory( info.InstallationPath ) ], - "read.g" ); - Add( secondrun, [ info, filename ] ); - od; - - if IsBound( GAPInfo.LibraryLoaded ) - and GAPInfo.LibraryLoaded = true then - # Read the `read.g' files collected up to now. - # Afterwards show the banners. - # (We have delayed this until now because it uses functionality - # from the package GAPDoc.) - # Note that no banners are printed during autoloading. - LoadPackage_ReadImplementationParts( secondrun, banner ); - secondrun:= []; - fi; - - od; - - if not IsBound( GAPInfo.LibraryLoaded ) then - Append( GAPInfo.delayedImplementationParts, secondrun ); - fi; - - LogPackageLoadingMessage( PACKAGE_DEBUG, "return from LoadPackage", - Name ); - GAPInfo.LoadPackageLevel:= GAPInfo.LoadPackageLevel - 1; - return true; - end ); - - -############################################################################# -## -#F LoadAllPackages() -## -InstallGlobalFunction( LoadAllPackages, function() - if ValueOption( "reversed" ) = true then - List( Reversed( RecNames( GAPInfo.PackagesInfo ) ), LoadPackage ); - else - List( RecNames( GAPInfo.PackagesInfo ), LoadPackage ); - fi; - end ); - - -############################################################################# -## -#F SetPackagePath( , ) -## -InstallGlobalFunction( SetPackagePath, function( pkgname, pkgpath ) - local pkgdir, file, record, version; - - InitializePackagesInfoRecords(); - pkgname:= LowercaseString( pkgname ); - NormalizeWhitespace( pkgname ); - if IsBound( GAPInfo.PackagesLoaded.( pkgname ) ) then - if GAPInfo.PackagesLoaded.( pkgname )[1] = pkgpath then - return; - fi; - Error( "another version of package ", pkgname, " is already loaded" ); - fi; - - pkgdir:= Directory( pkgpath ); - file:= Filename( [ pkgdir ], "PackageInfo.g" ); - if file = fail then - file:= Filename( [ pkgdir ], "PkgInfo.g" ); - fi; - if file = fail then - return; - fi; - Unbind( GAPInfo.PackageInfoCurrent ); - Read( file ); - record:= GAPInfo.PackageInfoCurrent; - Unbind( GAPInfo.PackageInfoCurrent ); - if IsBound( record.PkgName ) then - record.PackageName:= record.PkgName; - fi; - if pkgname <> NormalizedWhitespace( LowercaseString( - record.PackageName ) ) then - Error( "found package ", record.PackageName, " not ", pkgname, - " in ", pkgpath ); - fi; - version:= record.Version; - if IsBound( GAPInfo.PackagesRestrictions.( pkgname ) ) - and GAPInfo.PackagesRestrictions.( pkgname ).OnInitialization( - record ) = false then - Add( GAPInfo.PackagesInfoRefuseLoad, record ); - else - record.InstallationPath:= Filename( [ pkgdir ], "" ); - if not IsBound( record.PackageDoc ) then - record.PackageDoc:= []; - elif IsRecord( record.PackageDoc ) then - record.PackageDoc:= [ record.PackageDoc ]; - fi; - fi; - GAPInfo.PackagesInfo.( pkgname ):= [ record ]; - end ); - - -############################################################################# -## -#F ExtendRootDirectories( ) -## -InstallGlobalFunction( ExtendRootDirectories, function( rootpaths ) - rootpaths:= Filtered( rootpaths, path -> not path in GAPInfo.RootPaths ); - if not IsEmpty( rootpaths ) then - # Append the new root paths. - GAPInfo.RootPaths:= Immutable( Concatenation( GAPInfo.RootPaths, - rootpaths ) ); - # Clear the cache. - GAPInfo.DirectoriesLibrary:= AtomicRecord( rec() ); - # Deal with an obsolete variable. - if IsBoundGlobal( "GAP_ROOT_PATHS" ) then - MakeReadWriteGlobal( "GAP_ROOT_PATHS" ); - UnbindGlobal( "GAP_ROOT_PATHS" ); - BindGlobal( "GAP_ROOT_PATHS", GAPInfo.RootPaths ); - fi; - # Reread the package information. - if IsBound( GAPInfo.PackagesInfoInitialized ) and - GAPInfo.PackagesInfoInitialized = true then - GAPInfo.PackagesInfoInitialized:= false; - InitializePackagesInfoRecords(); - fi; - fi; - end ); - - -############################################################################# -## -#F InstalledPackageVersion( ) -## -InstallGlobalFunction( InstalledPackageVersion, function( name ) - local avail, info; - - avail:= TestPackageAvailability( name, "" ); - if avail = fail then - return fail; - elif avail = true then - return GAPInfo.PackagesLoaded.( LowercaseString( name ) )[2]; - fi; - info:= First( PackageInfo( name ), r -> r.InstallationPath = avail ); - return info.Version; - end ); - - -############################################################################# -## -#F AutoloadPackages() -## - -# The packages to load during startup can be specified via a user preference. -DeclareUserPreference( rec( - name:= "PackagesToLoad", - description:= [ - "A list of names of packages which should be loaded during startup. \ -For backwards compatibility, the default lists most of packages \ -that were autoloaded in GAP 4.4 (add or remove packages as you like)." - ], - default:= [ "autpgrp", "alnuth", "crisp", "ctbllib", "factint", "fga", - "irredsol", "laguna", "polenta", "polycyclic", "resclasses", - "sophus", "tomlib" ], - values:= function() return RecNames( GAPInfo.PackagesInfo ); end, - multi:= true, - ) ); - -# And a preference for avoiding some packages. -DeclareUserPreference( rec( - name:= "ExcludeFromAutoload", - description:= [ - "These packages are not loaded at GAP startup. This doesn't work for \ -packages which are needed by the GAP library, or which are already loaded \ -in a workspace." - ], - default:= [], - values:= function() return RecNames( GAPInfo.PackagesInfo ); end, - multi:= true, - ) ); - -# And a preference for ignoring some packages completely during the session. -DeclareUserPreference( rec( - name:= "PackagesToIgnore", - description:= [ - "These packages are not regarded as available. This doesn't work for \ -packages which are needed by the GAP library, or which are already loaded \ -in a workspace." - ], - default:= [], - values:= function() return RecNames( GAPInfo.PackagesInfo ); end, - multi:= true, - ) ); - -# And a preference for setting the info level of package loading. -DeclareUserPreference( rec( - name:= "InfoPackageLoadingLevel", - description:= [ - "Info messages concerning package loading up to this level are printed. \ -The level can be changed in a running session using 'SetInfoLevel'." - ], - default:= PACKAGE_ERROR, - values:= [ PACKAGE_ERROR, PACKAGE_WARNING, PACKAGE_INFO, PACKAGE_DEBUG ], - multi:= false, - ) ); - -InstallGlobalFunction( AutoloadPackages, function() - local banner, msg, pair, excludedpackages, name, record; - -#T remove this as soon as `BANNER' is not used anymore in packages -if IsBoundGlobal( "BANNER" ) then - banner:= ValueGlobal( "BANNER" ); - MakeReadWriteGlobal( "BANNER" ); - UnbindGlobal( "BANNER" ); -fi; -BindGlobal( "BANNER", false ); - - if GAPInfo.CommandLineOptions.L = "" then - msg:= "entering AutoloadPackages (no workspace)"; - else - msg:= Concatenation( "entering AutoloadPackages (workspace ", - GAPInfo.CommandLineOptions.L, ")" ) ; - fi; - LogPackageLoadingMessage( PACKAGE_DEBUG, msg, "GAP" ); - - SetInfoLevel( InfoPackageLoading, - UserPreference( "InfoPackageLoadingLevel" ) ); - - GAPInfo.ExcludeFromAutoload:= []; - GAPInfo.PackagesInfoInitialized:= false; - InitializePackagesInfoRecords(); - - GAPInfo.delayedImplementationParts:= []; - - # Load the needed other packages (suppressing banners) - # that are not yet loaded. - if ForAny( GAPInfo.Dependencies.NeededOtherPackages, - p -> not IsBound( GAPInfo.PackagesLoaded.( p[1] ) ) ) then - LogPackageLoadingMessage( PACKAGE_DEBUG, - Concatenation( [ "trying to load needed packages" ], - List( GAPInfo.Dependencies.NeededOtherPackages, - pair -> Concatenation( pair[1], " (", pair[2], ")" ) ) ), - "GAP" ); - if GAPInfo.CommandLineOptions.A then - PushOptions( rec( OnlyNeeded:= true ) ); - fi; - for pair in GAPInfo.Dependencies.NeededOtherPackages do - if LoadPackage( pair[1], pair[2], false ) <> true then - LogPackageLoadingMessage( PACKAGE_ERROR, Concatenation( - "needed package ", pair[1], " cannot be loaded" ), "GAP" ); - Error( "failed to load needed package `", pair[1], - "' (version ", pair[2], ")" ); - fi; - od; - LogPackageLoadingMessage( PACKAGE_DEBUG, "needed packages loaded", - "GAP" ); - if GAPInfo.CommandLineOptions.A then - PopOptions(); - fi; - fi; - - # If necessary then load the implementation part of the GAP library, - # and the implementation parts of the packages loaded up to now. - if not IsBound( GAPInfo.LibraryLoaded ) then - LogPackageLoadingMessage( PACKAGE_DEBUG, - [ "read the impl. part of the GAP library" ], "GAP" ); - ReadGapRoot( "lib/read.g" ); - GAPInfo.LibraryLoaded:= true; - GAPInfo.LoadPackageLevel:= GAPInfo.LoadPackageLevel + 1; - LoadPackage_ReadImplementationParts( - GAPInfo.delayedImplementationParts, false ); - GAPInfo.LoadPackageLevel:= GAPInfo.LoadPackageLevel - 1; - fi; - Unbind( GAPInfo.delayedImplementationParts ); -#T remove this as soon as `BANNER' is not used anymore in packages -MakeReadWriteGlobal( "BANNER" ); -UnbindGlobal( "BANNER" ); -if IsBound( banner ) then - BindGlobal( "BANNER", banner ); -fi; - - # Load suggested packages of GAP (suppressing banners). - if GAPInfo.CommandLineOptions.A then - LogPackageLoadingMessage( PACKAGE_DEBUG, - "omitting packages suggested via \"PackagesToLoad\" (-A option)", - "GAP" ); - elif ValueOption( "OnlyNeeded" ) = true then - LogPackageLoadingMessage( PACKAGE_DEBUG, - [ "omitting packages suggested via \"PackagesToLoad\"", - " ('OnlyNeeded' option)" ], - "GAP" ); - elif ForAny( List( UserPreference( "PackagesToLoad" ), LowercaseString ), - p -> not IsBound( GAPInfo.PackagesLoaded.( p ) ) ) then - - # Try to load the suggested other packages (suppressing banners), - # issue a warning for each such package where this is not possible. - excludedpackages:= List( UserPreference( "ExcludeFromAutoload" ), - LowercaseString ); - LogPackageLoadingMessage( PACKAGE_DEBUG, - Concatenation( [ "trying to load suggested packages" ], - UserPreference( "PackagesToLoad" ) ), - "GAP" ); - for name in UserPreference( "PackagesToLoad" ) do -#T admit pair [ name, version ] in user preferences! - if LowercaseString( name ) in excludedpackages then - LogPackageLoadingMessage( PACKAGE_DEBUG, - Concatenation( "excluded from autoloading: ", name ), - "GAP" ); - elif not IsBound( GAPInfo.PackagesLoaded.( LowercaseString( name ) ) ) then - LogPackageLoadingMessage( PACKAGE_DEBUG, - Concatenation( "considering for autoloading: ", name ), - "GAP" ); - if LoadPackage( name, false ) <> true then - LogPackageLoadingMessage( PACKAGE_DEBUG, - Concatenation( "suggested package ", name, - " cannot be loaded" ), "GAP" ); - else - LogPackageLoadingMessage( PACKAGE_DEBUG, - Concatenation( name, " loaded" ), "GAP" ); -#T possible to get the right case of the name? - fi; - fi; - od; - LogPackageLoadingMessage( PACKAGE_DEBUG, - "suggested packages loaded", "GAP" ); - fi; - - # Load the documentation for not yet loaded packages. - LogPackageLoadingMessage( PACKAGE_DEBUG, - "call LoadPackageDocumentation for not loaded packages", - "GAP" ); - for name in RecNames( GAPInfo.PackagesInfo ) do - if not IsBound( GAPInfo.PackagesLoaded.( name ) ) then - # Note that the info records for each package are sorted - # w.r.t. decreasing version number. - record:= First( GAPInfo.PackagesInfo.( name ), IsRecord ); - if record <> fail then - LoadPackageDocumentation( record ); - fi; - fi; - od; - LogPackageLoadingMessage( PACKAGE_DEBUG, - "LoadPackageDocumentation for not loaded packages done", - "GAP" ); - - Unbind( GAPInfo.ExcludeFromAutoload ); - - LogPackageLoadingMessage( PACKAGE_DEBUG, - "return from AutoloadPackages", - "GAP" ); - end ); - - -############################################################################# -## -#F GAPDocManualLab() . create manual.lab for package w/ GAPDoc docs -## -# avoid warning (will be def. in GAPDoc) -if not IsBound(StripEscapeSequences) then - StripEscapeSequences := 0; -fi; -InstallGlobalFunction( GAPDocManualLabFromSixFile, - function( bookname, sixfilepath ) - local stream, entries, SecNumber, esctex, file; - - stream:= InputTextFile( sixfilepath ); - - atomic readonly HELP_REGION do - entries:= HELP_BOOK_HANDLER.GapDocGAP.ReadSix( stream ).entries; - od; - - SecNumber:= function( list ) - if IsEmpty( list ) or list[1] = 0 then - return ""; - fi; - while list[ Length( list ) ] = 0 do - Unbind( list[ Length( list ) ] ); - od; - return JoinStringsWithSeparator( List( list, String ), "." ); - end; - - # throw away TeX critical characters here - esctex:= function( str ) - return Filtered( StripEscapeSequences( str ), c -> not c in "%#$&^_~" ); - end; - - bookname:= LowercaseString( bookname ); - entries:= List( entries, - entry -> Concatenation( "\\makelabel{", bookname, ":", - esctex(entry[1]), "}{", - SecNumber( entry[3] ), "}{", - entry[7], "}\n" ) ); - # forget entries that contain a character from "\\*+/=" in label, - # these were never allowed, so no old manual will refer to them - entries := Filtered(entries, entry -> - not ForAny("\\*+/=", c-> c in entry{[9..Length(entry)]})); - file:= Concatenation( sixfilepath{ [ 1 .. Length( sixfilepath ) - 3 ] }, - "lab" ); - # add marker line - entries := Concatenation ( - [Concatenation ("\\GAPDocLabFile{", bookname,"}\n")], - entries); - FileString( file, Concatenation( entries ) ); - Info( InfoWarning, 1, "File: ", file, " written." ); -end ); - -InstallGlobalFunction( GAPDocManualLab, function(pkgname) - local pinf, book, file; - - if not IsString(pkgname) then - Error("argument should be a string\n"); - fi; - pkgname := LowercaseString(pkgname); - LoadPackage(pkgname); - if not IsBound(GAPInfo.PackagesInfo.(pkgname)) then - Error("Could not load package ", pkgname, ".\n"); - fi; - if LoadPackage("GAPDoc") <> true then - Error("package `GAPDoc' not installed. Please install `GAPDoc'\n" ); - fi; - - pinf := GAPInfo.PackagesInfo.(pkgname)[1]; - for book in pinf.PackageDoc do - file := Filename([Directory(pinf.InstallationPath)], book.SixFile); - if file = fail or not IsReadableFile(file) then - Error("could not open `manual.six' file of package `", pkgname, "'.\n", - "Please compile its documentation\n"); - fi; - GAPDocManualLabFromSixFile( book.BookName, file ); - od; -end ); -if StripEscapeSequences = 0 then - Unbind(StripEscapeSequences); -fi; - - -############################################################################# -## -#F DeclareAutoreadableVariables( , , ) -## -InstallGlobalFunction( DeclareAutoreadableVariables, - function( pkgname, filename, varlist ) - CallFuncList( AUTO, Concatenation( [ - function( x ) - # Avoid nested calls to `RereadPackage', - # which could cause that `REREADING' is set to `false' too early. - if REREADING then - ReadPackage( pkgname, filename ); - else - RereadPackage( pkgname, filename ); - fi; - end, filename ], varlist ) ); - end ); - - -############################################################################# -## -## Tests whether loading a package works and does not obviously break -## anything. -## (This is very preliminary.) -## - - -############################################################################# -## -#F ValidatePackageInfo( ) -## -InstallGlobalFunction( ValidatePackageInfo, function( info ) - local record, pkgdir, i, IsStringList, IsRecordList, IsProperBool, IsURL, - IsFilename, IsFilenameList, result, TestOption, TestMandat, subrec, - list; - - if IsString( info ) then - if IsReadableFile( info ) then - Unbind( GAPInfo.PackageInfoCurrent ); - Read( info ); - if IsBound( GAPInfo.PackageInfoCurrent ) then - record:= GAPInfo.PackageInfoCurrent; - Unbind( GAPInfo.PackageInfoCurrent ); - else - Error( "the file is not a `PackageInfo.g' file" ); - fi; - pkgdir:= "./"; - for i in Reversed( [ 1 .. Length( info ) ] ) do - if info[i] = '/' then - pkgdir:= info{ [ 1 .. i ] }; - break; - fi; - od; - else - Error( " is not the name of a readable file" ); - fi; - elif IsRecord( info ) then - pkgdir:= fail; - record:= info; - else - Error( " must be either a record or a filename" ); - fi; - - IsStringList:= x -> IsList( x ) and ForAll( x, IsString ); - IsRecordList:= x -> IsList( x ) and ForAll( x, IsRecord ); - IsProperBool:= x -> x = true or x = false; - IsFilename:= x -> IsString( x ) and Length( x ) > 0 and - ( pkgdir = fail or - ( x[1] <> '/' and IsReadableFile( Concatenation( pkgdir, x ) ) ) ); - IsFilenameList:= x -> IsList( x ) and ForAll( x, IsFilename ); - IsURL := x -> ForAny(["http://","https://","ftp://"], s -> StartsWith(x,s)); - - result:= true; - - TestOption:= function( record, name, type, typename ) - if IsBound( record.( name ) ) and not type( record.( name ) ) then - Print( "#E component `", name, "', if present, must be bound to ", - typename, "\n" ); - result:= false; - return false; - fi; - return true; - end; - - TestMandat:= function( record, name, type, typename ) - if not IsBound( record.( name ) ) or not type( record.( name ) ) then - Print( "#E component `", name, "' must be bound to ", - typename, "\n" ); - result:= false; - return false; - fi; - return true; - end; - - TestMandat( record, "PackageName", - x -> IsString(x) and 0 < Length(x), - "a nonempty string" ); - TestMandat( record, "Subtitle", IsString, "a string" ); - TestMandat( record, "Version", - x -> IsString(x) and 0 < Length(x) and x[1] <> '=', - "a nonempty string that does not start with `='" ); - TestMandat( record, "Date", - x -> IsString(x) and Length(x) = 10 and x{ [3,6] } = "//" - and ForAll( x{ [1,2,4,5,7,8,9,10] }, IsDigitChar ), - "a string of the form `dd/mm/yyyy'" ); - TestMandat( record, "ArchiveURL", IsURL, "a string started with http://, https:// or ftp://" ); - TestMandat( record, "ArchiveFormats", IsString, "a string" ); - TestOption( record, "TextFiles", IsStringList, "a list of strings" ); - TestOption( record, "BinaryFiles", IsStringList, "a list of strings" ); - TestOption( record, "TextBinaryFilesPatterns", - x -> IsStringList(x) and - ForAll( x, i -> Length(i) > 0 ) and - ForAll( x, i -> i[1] in ['T','B'] ), - "a list of strings, each started with 'T' or 'B'" ); - if Number( [ IsBound(record.TextFiles), - IsBound(record.BinaryFiles), - IsBound(record.TextBinaryFilesPatterns) ], - a -> a=true ) > 1 then - Print("#W only one of TextFiles, BinaryFiles or TextBinaryFilesPatterns\n"); - Print("#W components must be bound\n"); - fi; - if TestOption( record, "Persons", IsRecordList, "a list of records" ) - and IsBound( record.Persons ) then - for subrec in record.Persons do - TestMandat( subrec, "LastName", IsString, "a string" ); - TestMandat( subrec, "FirstNames", IsString, "a string" ); - if not ( IsBound( subrec.IsAuthor ) - or IsBound( subrec.IsMaintainer ) ) then - Print( "#E one of the components `IsAuthor', `IsMaintainer' ", - "must be bound\n" ); - result:= false; - fi; - TestOption( subrec, "IsAuthor", IsProperBool, "`true' or `false'" ); - TestOption( subrec, "IsMaintainer", IsProperBool, - "`true' or `false'" ); - if IsBound( subrec.IsMaintainer ) then - if subrec.IsMaintainer = true and - not ( IsBound( subrec.Email ) or - IsBound( subrec.WWWHome ) or - IsBound( subrec.PostalAddress ) ) then - Print( "#E one of the components `Email', `WWWHome', `PostalAddress'\n", - "#E must be bound for each package maintainer \n" ); - result:= false; - fi; - fi; - TestOption( subrec, "Email", IsString, "a string" ); - TestOption( subrec, "WWWHome", IsURL, "a string started with http://, https:// or ftp://" ); - TestOption( subrec, "PostalAddress", IsString, "a string" ); - TestOption( subrec, "Place", IsString, "a string" ); - TestOption( subrec, "Institution", IsString, "a string" ); - od; - fi; - - if TestMandat( record, "Status", - x -> x in [ "accepted", "submitted", "deposited", "dev", "other" ], - "one of \"accepted\", \"deposited\", \"dev\", \"other\"" ) - and record.Status = "accepted" then - TestMandat( record, "CommunicatedBy", - x -> IsString(x) and PositionSublist( x, " (" ) <> fail - and x[ Length(x) ] = ')', - "a string of the form ` ()'" ); - TestMandat( record, "AcceptDate", - x -> IsString( x ) and Length( x ) = 7 and x[3] = '/' - and ForAll( x{ [1,2,4,5,6,7] }, IsDigitChar ), - "a string of the form `mm/yyyy'" ); - fi; - TestMandat( record, "README_URL", IsURL, "a string started with http://, https:// or ftp://" ); - TestMandat( record, "PackageInfoURL", IsURL, "a string started with http://, https:// or ftp://" ); - - if TestOption( record, "SourceRepository", IsRecord, "a record" ) then - if IsBound( record.SourceRepository ) then - TestMandat( record.SourceRepository, "Type", IsString, "a string" ); - TestMandat( record.SourceRepository, "URL", IsString, "a string" ); - fi; - fi; - TestOption( record, "IssueTrackerURL", IsURL, "a string started with http://, https:// or ftp://" ); - TestOption( record, "SupportEmail", IsString, "a string" ); - TestMandat( record, "AbstractHTML", IsString, "a string" ); - TestMandat( record, "PackageWWWHome", IsURL, "a string started with http://, https:// or ftp://" ); - if TestMandat( record, "PackageDoc", - x -> IsRecord( x ) or IsRecordList( x ), - "a record or a list of records" ) then - if IsRecord( record.PackageDoc ) then - list:= [ record.PackageDoc ]; - else - list:= record.PackageDoc; - fi; - for subrec in list do - TestMandat( subrec, "BookName", IsString, "a string" ); - if IsBound(subrec.Archive) then - Print("#W PackageDoc.Archive is withdrawn, use PackageDoc.ArchiveURLSubset instead\n"); - fi; - TestMandat( subrec, "ArchiveURLSubset", IsFilenameList, - "a list of strings denoting relative paths to readable files or directories" ); - TestMandat( subrec, "HTMLStart", IsFilename, - "a string denoting a relative path to a readable file" ); - TestMandat( subrec, "PDFFile", IsFilename, - "a string denoting a relative path to a readable file" ); - TestMandat( subrec, "SixFile", IsFilename, - "a string denoting a relative path to a readable file" ); - TestMandat( subrec, "LongTitle", IsString, "a string" ); - od; - fi; - if TestOption( record, "Dependencies", IsRecord, "a record" ) - and IsBound( record.Dependencies ) then - TestOption( record.Dependencies, "GAP", IsString, "a string" ); - TestOption( record.Dependencies, "NeededOtherPackages", - comp -> IsList( comp ) and ForAll( comp, - l -> IsList( l ) and Length( l ) = 2 - and ForAll( l, IsString ) ), - "a list of pairs `[ , ]' of strings" ); - TestOption( record.Dependencies, "SuggestedOtherPackages", - comp -> IsList( comp ) and ForAll( comp, - l -> IsList( l ) and Length( l ) = 2 - and ForAll( l, IsString ) ), - "a list of pairs `[ , ]' of strings" ); - TestOption( record.Dependencies, "ExternalConditions", - comp -> IsList( comp ) and ForAll( comp, - l -> IsString( l ) or ( IsList( l ) and Length( l ) = 2 - and ForAll( l, IsString ) ) ), - "a list of strings or of pairs `[ , ]' of strings" ); - - # If the package is a needed package of GAP then all its needed - # packages must also occur in the list of needed packages of GAP. - list:= List( GAPInfo.Dependencies.NeededOtherPackages, - x -> LowercaseString( x[1] ) ); - if IsBound( record.PackageName ) - and IsString( record.PackageName ) - and LowercaseString( record.PackageName ) in list - and IsBound( record.Dependencies.NeededOtherPackages ) - and IsList( record.Dependencies.NeededOtherPackages ) then - list:= Filtered( record.Dependencies.NeededOtherPackages, - x -> IsList( x ) and IsBound( x[1] ) - and IsString( x[1] ) - and not LowercaseString( x[1] ) in list ); - if not IsEmpty( list ) then - Print( "#E the needed packages in '", - List( list, x -> x[1] ), "'\n", - "#E are currently not needed packages of GAP\n" ); - result:= false; - fi; - fi; - fi; - TestMandat( record, "AvailabilityTest", IsFunction, "a function" ); - TestOption( record, "BannerString", IsString, "a string" ); - TestOption( record, "TestFile", IsFilename, - "a string denoting a relative path to a readable file" ); - TestOption( record, "PreloadFile", IsFilename, - "a string denoting a relative path to a readable file" ); - TestOption( record, "Keywords", IsStringList, "a list of strings" ); - - return result; - end ); - - -############################################################################# -## -#V GAPInfo.PackagesRestrictions -## -## -## -## -## -## This is a mutable record, each component being the name of a package -## pkg (in lowercase letters) that is required/recommended to be -## updated to a certain version, -## the value being a record with the following components. -##

-## -## OnInitialization -## -## a function that takes one argument, the record stored in the -## PackageInfo.g file of the package, -## and returns true if the package can be loaded, -## and returns false if not. -## The function is allowed to change components of the argument record. -## It should not print any message, -## this should be left to the OnLoad component, -## -## OnLoad -## -## a function that takes one argument, the record stored in the -## PackageInfo.g file of the package, and can print a message -## when the availability of the package is checked for the first time; -## this message is intended to explain why the package cannot loaded due -## to the false result of the OnInitialization component, -## or as a warning about known problems (when the package is in fact -## loaded), and it might give hints for upgrading the package. -## -## -## -## -## -GAPInfo.PackagesRestrictions := AtomicRecord(rec( - anupq := MakeImmutable(rec( - OnInitialization := function( pkginfo ) - if CompareVersionNumbers( pkginfo.Version, "1.3" ) = false then - return false; - fi; - return true; - end, - OnLoad := function( pkginfo ) - if CompareVersionNumbers( pkginfo.Version, "1.3" ) = false then - Print( " The package `anupq'", - " should better be upgraded at least to version 1.3,\n", - " the given version (", pkginfo.Version, - ") is known to be incompatible\n", - " with the current version of GAP.\n", - " It is strongly recommended to update to the ", - "most recent version, see URL\n", - " http://www.math.rwth-aachen.de/~Greg.Gamble/ANUPQ\n" ); - fi; - end )), - - autpgrp := MakeImmutable(rec( - OnInitialization := function( pkginfo ) - return true; - end, - OnLoad := function( pkginfo ) - if CompareVersionNumbers( pkginfo.Version, "1.1" ) = false then - Print( " The package `autpgrp'", - " should better be upgraded at least to version 1.1,\n", - " the given version (", pkginfo.Version, - ") is known to be incompatible\n", - " with the current version of GAP.\n", - " It is strongly recommended to update to the ", - "most recent version, see URL\n", - " https://www.gap-system.org/Packages/autpgrp.html\n" ); - fi; - end )) )); - - -############################################################################# -## -#F SuggestUpgrades( versions ) . . compare installed with distributed versions -## -InstallGlobalFunction( SuggestUpgrades, function( suggestedversions ) - local ok, outstr, out, entry, inform, info; - - suggestedversions := Set( List( suggestedversions, ShallowCopy ) ); - ok:= true; - # We collect the output in a string, because availability test may - # cause some intermediate printing. This way the output of the present - # function comes after such texts. - outstr := ""; - out := OutputTextString(outstr, true); - PrintTo(out, "#I ======================================================", - "================ #\n", - "#I Result of 'SuggestUpgrades':\n#I\n" - ); - # Deal with the kernel and library versions. - entry:= First( suggestedversions, x -> x[1] = "GAPLibrary" ); - if entry = fail then - PrintTo(out, "#E no info about suggested GAP library version ...\n" ); - ok:= false; - elif not CompareVersionNumbers( GAPInfo.Version, entry[2] ) then - PrintTo(out, "#E You are using version ", GAPInfo.Version, - " of the GAP library.\n", - "#E Please upgrade to version ", entry[2], ".\n\n" ); - ok:= false; - elif not CompareVersionNumbers( entry[2], GAPInfo.Version ) then - PrintTo(out, "#E You are using version ", GAPInfo.Version, - " of the GAP library.\n", - "#E This is newer than the distributed version ", - entry[2], ".\n\n" ); - ok:= false; - fi; - RemoveSet( suggestedversions, entry ); - - entry:= First( suggestedversions, x -> x[1] = "GAPKernel" ); - if entry = fail then - PrintTo(out, "#E no info about suggested GAP kernel version ...\n" ); - ok:= false; - elif not CompareVersionNumbers( GAPInfo.KernelVersion, entry[2] ) then - PrintTo(out, "#E You are using version ", GAPInfo.KernelVersion, - " of the GAP kernel.\n", - "#E Please upgrade to version ", entry[2], ".\n\n" ); - ok:= false; - elif not CompareVersionNumbers( entry[2], GAPInfo.KernelVersion ) then - PrintTo(out, "#E You are using version ", GAPInfo.KernelVersion, - " of the GAP kernel.\n", - "#E This is newer than the distributed version ", - entry[2], ".\n\n" ); - ok:= false; - fi; - RemoveSet( suggestedversions, entry ); - - # Deal with present packages which are not distributed. - inform := Difference(NamesOfComponents(GAPInfo.PackagesInfo), - List(suggestedversions, x-> LowercaseString(x[1]))); - if not IsEmpty( inform ) then - PrintTo(out, "#I The following GAP packages are present but not ", - "officially distributed.\n" ); - for entry in inform do - info := GAPInfo.PackagesInfo.(entry)[1]; - PrintTo(out, "#I ", info.PackageName, " ", info.Version, "\n" ); - od; - PrintTo(out, "\n" ); - ok:= false; - fi; - - - # Deal with packages that are not installed. - inform := Filtered( suggestedversions, entry -> not IsBound( - GAPInfo.PackagesInfo.( LowercaseString( entry[1] ) ) ) - and ForAll( GAPInfo.PackagesInfoRefuseLoad, - r -> LowercaseString( entry[1] ) - <> LowercaseString( r.PackageName ) ) ); - if not IsEmpty( inform ) then - PrintTo(out, "#I The following distributed GAP packages are ", - "not installed.\n" ); - for entry in inform do - PrintTo(out, "#I ", entry[1], " ", entry[2], "\n" ); - od; - PrintTo(out, "\n" ); - ok:= false; - fi; - SubtractSet( suggestedversions, inform ); - - # Deal with packages whose installed versions are not available - # (without saying anything about the reason). -#T Here it would be desirable to omit those packages that cannot be loaded -#T on the current platform; e.g., Windoofs users need not be informed about -#T packages for which no Windoofs version is available. - # These packages can be up to date or outdated. - for entry in suggestedversions do - Add( entry, InstalledPackageVersion( entry[1] ) ); -#T Here we may get print statements from the availability testers; -#T how to avoid this? - od; - inform:= Filtered( suggestedversions, entry -> entry[3] = fail ); - if not IsEmpty( inform ) then - PrintTo(out, "#I The following GAP packages are present ", - "but cannot be used.\n" ); - for entry in inform do - PrintTo(out, "#I ", entry[1], " ", - GAPInfo.PackagesInfo.( LowercaseString( entry[1] ) )[1].Version, - "\n" ); - if not ForAny( GAPInfo.PackagesInfo.( LowercaseString( entry[1] ) ), - r -> CompareVersionNumbers( r.Version, entry[2] ) ) then - PrintTo(out, "#I (distributed version is newer: ", - entry[2], ")\n" ); - fi; - od; - PrintTo(out, "\n" ); - ok:= false; - fi; - SubtractSet( suggestedversions, inform ); - - # Deal with packages in *newer* (say, dev-) versions than the - # distributed ones. - inform:= Filtered( suggestedversions, entry -> not CompareVersionNumbers( - entry[2], entry[3] ) ); - if not IsEmpty( inform ) then - PrintTo(out, - "#I Your following GAP packages are *newer* than the ", - "distributed version.\n" ); - for entry in inform do - PrintTo(out, "#I ", entry[1], " ", entry[3], - " (distributed is ", entry[2], ")\n" ); - od; - PrintTo(out, "\n" ); - ok:= false; - fi; - # Deal with packages whose installed versions are not up to date. - inform:= Filtered( suggestedversions, entry -> not CompareVersionNumbers( - entry[3], entry[2] ) ); - if not IsEmpty( inform ) then - PrintTo(out, - "#I The following GAP packages are available but outdated.\n" ); - for entry in inform do - PrintTo(out, "#I ", entry[1], " ", entry[3], - " (please upgrade to ", entry[2], ")\n" ); - od; - PrintTo(out, "\n" ); - ok:= false; - fi; - - if ok then - PrintTo(out, "#I Your GAP installation is up to date with the ", - "official distribution.\n\n" ); - fi; - CloseStream(out); - Print( outstr ); - end ); - - -############################################################################# -## -#F BibEntry( "GAP"[, ] ) -#F BibEntry( [, ] ) -#F BibEntry( [, ] ) -## -Unicode:= "dummy"; -Encode:= "dummy"; - -InstallGlobalFunction( BibEntry, function( arg ) - local key, pkgname, pkginfo, GAP, ps, months, val, entry, author; - - key:= false; - if Length( arg ) = 1 and IsString( arg[1] ) then - pkgname:= arg[1]; - elif Length( arg ) = 2 and IsString( arg[1] ) and IsString( arg[2] ) then - pkgname:= arg[1]; - key:= arg[2]; - elif Length( arg ) = 1 and IsRecord( arg[1] ) then - pkginfo:= arg[1]; - elif Length( arg ) = 2 and IsRecord( arg[1] ) and IsString( arg[2] ) then - pkginfo:= arg[1]; - key:= arg[2]; - else - Error( "usage: BibEntry( \"GAP\"[, ] ), ", - "BibEntry( [, ] ), ", - "BibEntry( [, ] )" ); - fi; - - GAP:= false; - if IsBound( pkgname ) then - if pkgname = "GAP" then - GAP:= true; - else - pkginfo:= InstalledPackageVersion( pkgname ); - pkginfo:= First( PackageInfo( pkgname ), r -> r.Version = pkginfo ); - if pkginfo = fail then - return ""; - fi; - fi; - fi; - - if key = false then - if GAP then - key:= Concatenation( "GAP", GAPInfo.Version ); - elif IsBound( pkginfo.Version ) then - key:= Concatenation( pkginfo.PackageName, pkginfo.Version ); - else - key:= pkginfo.PackageName; - fi; - fi; - - ps:= function( str ) - local uni; - - uni:= Unicode( str, "UTF-8" ); - if uni = fail then - uni:= Unicode( str, "ISO-8859-1" ); - fi; - return Encode( uni, GAPInfo.TermEncoding ); - end; - - # According to , - # the supported fields of a Bib&TeX; entry of @misc type are - # the following. - #

- # - # author - # - # computed from the Persons component of the package, - # not distinguishing authors and maintainers, - # keeping the ordering of entries, - # - # title - # - # computed from the PackageName and Subtitle components - # of the package, - # - # month and year - # - # computed from the Date component of the package, - # - # note - # - # the string "Refereed \\textsf{GAP} package" or - # "\\textsf{GAP} package", - # - # howpublished - # - # the PackageWWWHome component of the package. - # - # - #

- # Also the edition component seems to be supported; - # it is computed from the Version component of the package. - - # Bib&Tex;'s @manual type seems to be not appropriate, - # since this type does not support a URL component - # in the base bib styles of La&TeX;. - # Instead we can use the @misc type and its howpublished - # component. - # We put the version information into the title component since - # the edition component is not supported in the base styles. - - months:= [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]; - if GAP then - val:= SplitString( GAPInfo.Date, "-" ); - if Length( val ) = 3 then - if Int( val[2] ) in [ 1 .. 12 ] then - val:= Concatenation( " ", months[ Int( val[2] ) ], - "\n ", val[3], "\n" ); - else - val:= Concatenation( " ", val[2], - "\n ", val[3], "\n" ); - fi; - else - val:= ""; - fi; - entry:= Concatenation( - "\n", - " <C>GAP</C> –", - " <C>G</C>roups, <C>A</C>lgorithms,\n", - " and <C>P</C>rogramming,", - " <C>V</C>ersion ", GAPInfo.Version, "\n", - " https://www.gap-system.org\n", - val, - " GAP\n", - " groups; *; gap; manual\n", - " The GAP Group\n", - "" ); - else - entry:= Concatenation( "\n" ); - author:= List( Filtered( pkginfo.Persons, - person -> person.IsAuthor or person.IsMaintainer ), - person -> Concatenation( - " ", person.FirstNames, - "", person.LastName, "\n" ) ); - if not IsEmpty( author ) then - Append( entry, Concatenation( - " \n", - ps( Concatenation( author ) ), - " \n" ) ); - fi; - Append( entry, Concatenation( - " <C>", pkginfo.PackageName, "</C>" ) ); - if IsBound( pkginfo.Subtitle ) then - Append( entry, Concatenation( - ", ", ps( pkginfo.Subtitle ) ) ); - fi; - if IsBound( pkginfo.Version ) then - Append( entry, Concatenation( - ",\n <C>V</C>ersion ", pkginfo.Version ) ); - fi; - Append( entry, "\n" ); - if IsBound( pkginfo.PackageWWWHome ) then - Append( entry, Concatenation( - " ", pkginfo.PackageWWWHome, - "\n" ) ); - fi; - if IsBound( pkginfo.Date ) and IsDenseList( pkginfo.Date ) - and Length( pkginfo.Date ) = 10 then - if Int( pkginfo.Date{ [ 4, 5 ] } ) in [ 1 .. 12 ] then - Append( entry, Concatenation( - " ", months[ Int( pkginfo.Date{ [ 4, 5 ] } ) ], - "\n", - " ", pkginfo.Date{ [ 7 .. 10 ] }, "\n" ) ); - else - Append( entry, Concatenation( - " ", pkginfo.Date{ [ 4, 5 ] }, "\n", - " ", pkginfo.Date{ [ 7 .. 10 ] }, "\n" ) ); - fi; - fi; - if IsBound( pkginfo.Status ) and pkginfo.Status = "accepted" then - Append( entry, " Refereed " ); - else - Append( entry, " " ); - fi; -# Append( entry, "GAP package\n" ); - Append( entry, "GAP package\n" ); - if IsBound( pkginfo.Keywords ) then - Append( entry, Concatenation( - " ", - JoinStringsWithSeparator( pkginfo.Keywords, "; " ), - "\n" ) ); - fi; - Append( entry, "" ); - fi; - - return entry; -end ); - -Unbind( Unicode ); -Unbind( Encode ); - -# dummy assignments to functions to be read lated in the GAPDoc package -ParseBibXMLextString:= "dummy"; -StringBibXMLEntry:= "dummy"; - -InstallGlobalFunction( Cite, function(arg) -local name, bib, key, parse, year; -if Length(arg)=0 then - name:="GAP"; -else - name := NormalizedWhitespace(arg[1]); -fi; -if name="gap" then - name:="GAP"; -fi; -if Length(arg)<=1 then - bib:= BibEntry( name ); -elif Length(arg)>2 then - Error("`Cite' takes no more than two arguments"); -else - key:=arg[2]; - bib:= BibEntry( name, key ); -fi; -if bib="" then - Print("WARNING: No working version of package ", name, " is available!\n"); - return; -fi; -parse:= ParseBibXMLextString( bib ); -Print("Please use one of the following samples\n", - "to cite ", name, " version from this installation\n\n"); - -Print("Text:\n\n"); -Print( StringBibXMLEntry( parse.entries[1], "Text" ) ); - -Print("HTML:\n\n"); -Print( StringBibXMLEntry( parse.entries[1], "HTML" ) ); - -Print("BibXML:\n\n"); -Print( bib, "\n\n" ); - -Print("BibTeX:\n\n"); -Print( StringBibXMLEntry( parse.entries[1], "BibTeX" ), "\n" ); - -if name="GAP" then - year:=SplitString(GAPInfo.Date,"-"); - if Length(year)=3 then - year:=year[3]; - else - year:=year[1]; # to work in GAP.dev - fi; - - Print("If you are not using BibTeX, here is the bibliography entry produced \n", - "by BibTeX (in bibliography style `alpha'):\n\n", - "\\bibitem[GAP]{GAP4}\n", - "\\emph{GAP -- Groups, Algorithms, and Programming}, ", - "Version ", GAPInfo.Version, ",\n", - "The GAP~Group (", year, "), \\verb+https://www.gap-system.org+.\n\n"); - Print( - "If you have (predominantly) used one or more particular GAP packages,\n", - "please cite these packages in addition to GAP itself (either check the\n", - "the package documentation for the suggestions, or use a scheme like:\n\n", - - "[PKG]\n", - ", , , \n", - "Version (), (GAP package),\n", - ".\n\n", - - "You may also produce citation samples for a GAP package by entering\n\n", - " Cite(\"packagename\");\n\n", - "in a GAP installation with the working version of this package available.\n\n"); -fi; -end); - -Unbind( ParseBibXMLextString ); -Unbind( StringBibXMLEntry ); - - -############################################################################# -## -#F PackageVariablesInfo( , ) -## -NamesSystemGVars := "dummy"; # is not yet defined when this file is read -NamesUserGVars := "dummy"; - -InstallGlobalFunction( PackageVariablesInfo, function( pkgname, version ) - local test, cache, cache2, PkgName, realname, new, new_up_to_case, - redeclared, newmethod, pos, key_dependent_operation, rules, - localBindGlobal, rule, loaded, pkg, args, docmark, done, result, - subrule, added, prev, subresult, entry, isrelevant, guesssource, - protected; - - pkgname:= LowercaseString( pkgname ); - test:= TestPackageAvailability( pkgname, version ); - - # If the function has been called for this package then - # return the stored value. - cache:= Concatenation( pkgname, ":", version ); - if not IsBound( GAPInfo.PackageVariablesInfo ) then - GAPInfo.PackageVariablesInfo:= rec(); - elif IsBound( GAPInfo.PackageVariablesInfo.( cache ) ) then - return GAPInfo.PackageVariablesInfo.( cache ); - elif version = "" and test = true then - cache2:= Concatenation( pkgname, ":", - InstalledPackageVersion( pkgname ) ); - if IsBound( GAPInfo.PackageVariablesInfo.( cache2 ) ) then - return GAPInfo.PackageVariablesInfo.( cache2 ); - fi; - fi; - - # Check that the package is available but not yet loaded. - if test = true then - Info( InfoWarning, 1, - "the package `", pkgname, "' is already loaded" ); - return []; - elif test = fail then - if version = "" then - Info( InfoWarning, 1, - "the package `", pkgname, "' cannot be loaded" ); - else - Info( InfoWarning, 1, - "the package `", pkgname, "' cannot be loaded in version `", - version, "'" ); - fi; - return []; - fi; - - PkgName:= GAPInfo.PackagesInfo.( pkgname )[1].PackageName; - - realname:= function( name ) - if name[ Length( name ) ] = '@' then - return Concatenation( name, PkgName ); - else - return name; - fi; - end; - - new:= function( entry ) - local name; - - name:= realname( entry[1][1] ); - if not name in GAPInfo.data.varsThisPackage then - return fail; - elif Length( entry[1] ) = 3 and entry[1][3] = "mutable" - and Length( name ) > 9 and name{ [ 1 .. 8 ] } = "Computed" - and name[ Length( name ) ] = 's' - and IsBoundGlobal( name{ [ 9 .. Length( name ) - 1 ] } ) then - return fail; - elif Length( entry[1] ) = 2 - and Length( name ) > 3 and name{ Length( name ) - [1,0] } = "Op" - and IsBoundGlobal( name{ [ 1 .. Length( name ) - 2 ] } ) - and ForAny( GAPInfo.data.KeyDependentOperation[2], - x -> x[1][1] = name{ [ 1 .. Length( name ) - 2 ] } - and x[2] = entry[2] - and x[3] = entry[3] ) then - # Ignore the declaration of the operation created by - # `KeyDependentOperation'. - # (We compare filename and line number in the file with these values - # for the call of `KeyDependentOperation'.) - return fail; - else - return [ name, ValueGlobal( name ), entry[2], entry[3] ]; - fi; - end; - - new_up_to_case:= function( entry ) - local name; - - name:= realname( entry[1][1] ); - if not name in GAPInfo.data.varsThisPackage then - return fail; - elif LowercaseString( name ) in GAPInfo.data.lowercase_vars then - return [ name, ValueGlobal( name ), entry[2], entry[3] ]; - else - return fail; - fi; - end; - - redeclared:= function( entry ) - local name; - - name:= realname( entry[1][1] ); - if not name in GAPInfo.data.varsThisPackage then - return [ name, ValueGlobal( name ), entry[2], entry[3] ]; - else - return fail; - fi; - end; - - newmethod:= function( entry ) - local name, setter, getter; - - name:= NameFunction( entry[1][1] ); - if IsString( entry[1][2] ) then - if entry[1][2] in [ "system setter", "system mutable setter", - "default method, does nothing" ] then - setter:= entry[1][1]; - if ForAny( ATTRIBUTES, - attr -> IsIdenticalObj( setter, attr[4] ) ) then - return fail; - fi; - elif entry[1][2] in [ "system getter", - "default method requiring categories and checking properties" ] then - getter:= entry[1][1]; - if ForAny( ATTRIBUTES, - attr -> IsIdenticalObj( getter, attr[3] ) ) then - return fail; - fi; - elif entry[1][2] in [ "default method" ] then - # Ignore the default methods (for attribute and operation) - # that are installed in calls to `KeyDependentOperation'. - # (We compare filename and line number in the file - # with these values for the call of `KeyDependentOperation'.) - if 9 < Length( name ) and name{ [ 1 .. 8 ] } = "Computed" - and name[ Length( name ) ] = 's' - and IsBoundGlobal( name{ [ 9 .. Length( name ) - 1 ] } ) - and ForAny( GAPInfo.data.KeyDependentOperation[2], - x -> x[1][1] = name{ [ 9 .. Length( name ) - 1 ] } - and x[2] = entry[2] - and x[3] = entry[3] ) then - return fail; - elif IsBoundGlobal( name ) - and ForAny( GAPInfo.data.KeyDependentOperation[2], - x -> x[1][1] = name - and x[2] = entry[2] - and x[3] = entry[3] ) then - return fail; - fi; - fi; - fi; - - # Omit methods for `FlushCaches'. - if name = "FlushCaches" then - return fail; - fi; - - # Extract a comment if possible. - if IsString( entry[1][2] ) then - # Store also the comment for this method installation. - return [ name, entry[1][ Length( entry[1] ) ], - entry[2], entry[3], entry[1][2] ]; - else - pos:= PositionProperty( entry[1], - x -> IsList( x ) and not IsEmpty( x ) - and ForAll( x, IsString ) ); - if pos <> fail then - # Create a comment from the list of strings that describe filters. - return [ NameFunction( entry[1][1] ), - entry[1][ Length( entry[1] ) ], - entry[2], entry[3], Concatenation( "for ", - JoinStringsWithSeparator( entry[1][ pos ], ", " ) ) ]; - else - # We know no comment. - return [ NameFunction( entry[1][1] ), - entry[1][ Length( entry[1] ) ], - entry[2], entry[3] ]; - fi; - fi; - end; - - key_dependent_operation:= function( entry ) - return entry; - end; - - # List the cases to be dealt with. - rules:= [ - [ "DeclareGlobalFunction", - [ "new global functions", new ], - [ "globals that are new only up to case", new_up_to_case ] ], - [ "DeclareGlobalVariable", - [ "new global variables", new ], - [ "globals that are new only up to case", new_up_to_case ] ], - [ "BindGlobal", - [ "new global variables", new ], - [ "globals that are new only up to case", new_up_to_case ] ], - [ "DeclareOperation", - [ "new operations", new ], - [ "redeclared operations", redeclared ], - [ "globals that are new only up to case", new_up_to_case ] ], - [ "DeclareAttribute", - [ "new attributes", new ], - [ "redeclared attributes", redeclared ], - [ "globals that are new only up to case", new_up_to_case ] ], - [ "DeclareProperty", - [ "new properties", new ], - [ "redeclared properties", redeclared ], - [ "globals that are new only up to case", new_up_to_case ] ], - [ "DeclareCategory", - [ "new categories", new ], - [ "redeclared categories", redeclared ], - [ "globals that are new only up to case", new_up_to_case ] ], - [ "DeclareRepresentation", - [ "new representations", new ], - [ "redeclared representations", redeclared ], - [ "globals that are new only up to case", new_up_to_case ] ], - [ "DeclareFilter", - [ "new plain filters", new ], - [ "redeclared plain filters", redeclared ], - [ "globals that are new only up to case", new_up_to_case ] ], - [ "InstallMethod", - [ "new methods", newmethod ] ], - [ "InstallOtherMethod", - [ "new other methods", newmethod ] ], - [ "DeclareSynonymAttr", - [ "new synonyms of attributes", new ], - [ "globals that are new only up to case", new_up_to_case ] ], - [ "DeclareSynonym", - [ "new synonyms", new ], - [ "globals that are new only up to case", new_up_to_case ] ], - [ "DeclareInfoClass", - [ "new info classes", new ] ], - [ "KeyDependentOperation", - [ "KeyDependentOperation", key_dependent_operation ] ], - ]; - - # Save the relevant global variables, and replace them. - GAPInfo.data:= rec( userGVars:= NamesUserGVars(), - varsThisPackage:= [], - revision_components:= [], - pkgpath:= test, - pkgname:= pkgname ); - - GAPInfo.data.lowercase_vars:= List( Union( NamesSystemGVars(), - GAPInfo.data.userGVars ), LowercaseString ); - - localBindGlobal:= BindGlobal; - - for rule in rules do - GAPInfo.data.( rule[1] ):= [ ValueGlobal( rule[1] ), [] ]; - MakeReadWriteGlobal( rule[1] ); - UnbindGlobal( rule[1] ); - localBindGlobal( rule[1], EvalString( Concatenation( - "function( arg ) ", - "local infile, path; ", - "infile:= INPUT_FILENAME(); ", - "path:= GAPInfo.data.pkgpath; ", - "if Length( path ) <= Length( infile ) and ", - " infile{ [ 1 .. Length( path ) ] } = path then ", - " Add( GAPInfo.data.( \"", rule[1], "\" )[2], ", - " [ StructuralCopy( arg ), infile, INPUT_LINENUMBER() ] ); ", - "fi; ", - "CallFuncList( GAPInfo.data.( \"", rule[1], "\" )[1], arg ); ", - "end" ) ) ); - od; - - # Redirect `ReadPackage'. - GAPInfo.data.ReadPackage:= ReadPackage; - MakeReadWriteGlobal( "ReadPackage" ); - UnbindGlobal( "ReadPackage" ); - localBindGlobal( "ReadPackage", EvalString( Concatenation( - "function( arg ) ", - "local pos, pkgname, before, cbefore, res, after, cafter; ", - "if Length( arg ) = 1 then ", - " pos:= Position( arg[1], '/' ); ", - " pkgname:= LowercaseString( arg[1]{[ 1 .. pos - 1 ]} ); ", - "elif Length( arg ) = 2 then ", - " pkgname:= LowercaseString( arg[1] ); ", - "else ", - " pkgname:= fail; ", - "fi; ", - "if pkgname = GAPInfo.data.pkgname then ", - " before:= NamesUserGVars(); ", - " if IsBoundGlobal( \"Revision\" ) then ", - " cbefore:= RecNames( ValueGlobal( \"Revision\" ) ); ", - " fi; ", - "fi; ", - "res:= CallFuncList( GAPInfo.data.ReadPackage, arg ); ", - "if pkgname = GAPInfo.data.pkgname then ", - " after:= NamesUserGVars(); ", - " UniteSet( GAPInfo.data.varsThisPackage, ", - " Filtered( Difference( after, before ), IsBoundGlobal ) ); ", - " if IsBoundGlobal( \"Revision\" ) then ", - " cafter:= RecNames( ValueGlobal( \"Revision\" ) ); ", - " UniteSet( GAPInfo.data.revision_components, ", - " Difference( cafter, cbefore ) ); ", - " fi; ", - "fi; ", - "return res; ", - "end" ) ) ); - - # Load the package `pkgname'. - loaded:= LoadPackage( pkgname ); - - # Put the original global variables back. - for rule in rules do - MakeReadWriteGlobal( rule[1] ); - UnbindGlobal( rule[1] ); - localBindGlobal( rule[1], GAPInfo.data.( rule[1] )[1] ); - od; - MakeReadWriteGlobal( "ReadPackage" ); - UnbindGlobal( "ReadPackage" ); - localBindGlobal( "ReadPackage", GAPInfo.data.ReadPackage ); - - if not loaded then - Print( "#E the package `", pkgname, "' could not be loaded\n" ); - return []; - fi; - - # Functions are printed together with their argument lists. - args:= function( func ) - local num, nam, str; - - if not IsFunction( func ) then - return ""; - fi; - num:= NumberArgumentsFunction( func ); - nam:= NamesLocalVariablesFunction( func ); - if num = -1 then - str:= "arg"; - elif nam = fail then - str:= "..."; - else - str:= JoinStringsWithSeparator( nam{ [ 1 .. num ] }, ", " ); - fi; - return Concatenation( "( ", str, " )" ); - end; - - # Mark undocumented globals with an asterisk. - docmark:= function( nam ) - if not ( IsBoundGlobal( nam ) and IsDocumentedWord( nam ) ) then - return "*"; - else - return ""; - fi; - end; - - # Prepare the output. - done:= []; - result:= []; - rules:= Filtered( rules, x -> x[1] <> "KeyDependentOperation" ); - for rule in rules do - for subrule in rule{ [ 2 .. Length( rule ) ] } do - added:= []; - for entry in Filtered( List( GAPInfo.data.( rule[1] )[2], - x -> subrule[2]( x ) ), - x -> x <> fail ) do - if Length( entry ) = 5 then - Add( added, [ [ entry[1], args( entry[2] ), - docmark( entry[1] ), entry[5] ], - [ entry[3], entry[4] ] ] ); - else - Add( added, [ [ entry[1], args( entry[2] ), - docmark( entry[1] ) ], - [ entry[3], entry[4] ] ] ); - fi; - od; - if not IsEmpty( added ) then - prev:= First( result, x -> x[1] = subrule[1] ); - if prev = fail then - Add( result, [ subrule[1], added ] ); - else - Append( prev[2], added ); - fi; - UniteSet( done, List( added, x -> x[1][1] ) ); - fi; - od; - od; - for subresult in result do - Sort( subresult[2] ); - od; - - # Mention the remaining new globals. - isrelevant:= function( name ) - local name2, attr; - - # Omit variables that are not bound anymore. - # (We have collected the new variables file by file, and it may happen - # that some of them become unbound in the meantime.) - if not IsBoundGlobal( name ) then - return false; - fi; - - # Omit `Set' and `Has' type variables. - if 3 < Length( name ) and name{ [ 1 .. 3 ] } in [ "Has", "Set" ] then - name2:= name{ [ 4 .. Length( name ) ] }; - if not IsBoundGlobal( name2 ) then - return true; - fi; - attr:= ValueGlobal( name2 ); - if ForAny( ATTRIBUTES, entry -> IsIdenticalObj( attr, entry[3] ) ) then - return false; - fi; - fi; - - # Omit operation and attribute created by `KeyDependentOperation'. - if 9 < Length( name ) and name{ [ 1 .. 8 ] } = "Computed" - and name[ Length( name ) ] = 's' - and IsBoundGlobal( name{ [ 9 .. Length( name ) - 1 ] } ) - and ForAny( GAPInfo.data.KeyDependentOperation[2], - x -> x[1][1] = name{ [ 9 .. Length( name ) - 1 ] } ) then - return false; - fi; - if 3 < Length( name ) and name{ Length( name ) - [1,0] } = "Op" - and IsBoundGlobal( name{ [ 1 .. Length( name ) - 2 ] } ) - and ForAny( GAPInfo.data.KeyDependentOperation[2], - x -> x[1][1] = name{ [ 1 .. Length( name ) - 2 ] } ) then - return false; - fi; - - return true; - end; - - added:= Filtered( Difference( GAPInfo.data.varsThisPackage, done ), - isrelevant ); - - # Distinguish write protected variables from others. - guesssource:= function( nam ) - local val; - - val:= ValueGlobal( nam ); - if IsFunction( val ) then - return [ FilenameFunc( val ), StartlineFunc( val ) ]; - else - return [ fail, fail ]; - fi; - end; - - protected:= Filtered( added, IsReadOnlyGVar ); - if not IsEmpty( protected ) then - Add( result, [ "other new globals (write protected)", - List( SortedList( protected ), - nam -> [ [ nam, args( ValueGlobal( nam ) ), - docmark( nam ) ], - guesssource( nam ) ] ) ] ); - fi; - added:= Difference( added, protected ); - if not IsEmpty( added ) then - Add( result, [ "other new globals (not write protected)", - List( SortedList( added ), - nam -> [ [ nam, args( ValueGlobal( nam ) ), - docmark( nam ) ], - guesssource( nam ) ] ) ] ); - fi; - - # Have new components been added to `Revision'? - if not IsEmpty( GAPInfo.data.revision_components ) then - Add( result, [ "new components of the outdated 'Revision' record", - List( GAPInfo.data.revision_components, - x -> [ [ x, "", "" ], [ fail, fail ] ] ) ] ); - fi; - - # Delete the auxiliary component from `GAPInfo'. - Unbind( GAPInfo.data ); - - # Store the data. - GAPInfo.PackageVariablesInfo.( cache ):= result; - if version = "" then - Append( cache, InstalledPackageVersion( pkgname ) ); - GAPInfo.PackageVariablesInfo.( cache ):= result; - fi; - - return result; - end ); - -Unbind( NamesSystemGVars ); -Unbind( NamesUserGVars ); - - -############################################################################# -## -#F ShowPackageVariables( [, ][, ] ) -## -InstallGlobalFunction( ShowPackageVariables, function( arg ) - local version, arec, pkgname, info, show, documented, undocumented, - private, result, len, format, entry, first, subentry, str; - - # Get and check the arguments. - version:= ""; - arec:= rec(); - if Length( arg ) = 1 and IsString( arg[1] ) then - pkgname:= LowercaseString( arg[1] ); - elif Length( arg ) = 2 and IsString( arg[1] ) and IsString( arg[2] ) then - pkgname:= LowercaseString( arg[1] ); - version:= arg[2]; - elif Length( arg ) = 2 and IsString( arg[1] ) and IsRecord( arg[2] ) then - pkgname:= LowercaseString( arg[1] ); - arec:= arg[2]; - elif Length( arg ) = 3 and IsString( arg[1] ) and IsString( arg[2] ) - and IsRecord( arg[3] ) then - pkgname:= LowercaseString( arg[1] ); - version:= arg[2]; - arec:= arg[3]; - else - Error( "usage: ShowPackageVariables( [, ]", - "[, ] )" ); - fi; - - # Compute the data. - info:= PackageVariablesInfo( pkgname, version ); - - # Evaluate the optional record. - if IsBound( arec.show ) and IsList( arec.show ) then - show:= arec.show; - else - show:= List( info, entry -> entry[1] ); - fi; - documented:= not IsBound( arec.showDocumented ) - or arec.showDocumented <> false; - undocumented:= not IsBound( arec.showUndocumented ) - or arec.showUndocumented <> false; - private:= not IsBound( arec.showPrivate ) - or arec.showPrivate <> false; - - # Render the relevant data. - result:= ""; - len:= SizeScreen()[1] - 2; - if IsBoundGlobal( "FormatParagraph" ) then - format:= ValueGlobal( "FormatParagraph" ); - else - format:= function( arg ) return Concatenation( arg[1], "\n" ); end; - fi; - for entry in info do - if entry[1] in show then - first:= true; - for subentry in entry[2] do - if ( ( documented and subentry[1][3] = "" ) or - ( undocumented and subentry[1][3] = "*" ) ) and - ( private or not '@' in subentry[1][1] ) then - if first then - Append( result, entry[1] ); - Append( result, ":\n" ); - first:= false; - fi; - Append( result, " " ); - for str in subentry[1]{ [ 1 .. 3 ] } do - Append( result, str ); - od; - Append( result, "\n" ); - if Length( subentry[1] ) = 4 and not IsEmpty( subentry[1][4] ) then - Append( result, - format( subentry[1][4], len, "left", [ " ", "" ] ) ); - fi; - fi; - od; - if not first then - Append( result, "\n" ); - fi; - fi; - od; - - # Show the relevant data. - if IsBound( arec.Display ) then - arec.Display( result ); - else - Print( result ); - fi; - end ); - - -############################################################################# -## -#E diff --git a/lib/package.gi b/lib/package.gi index ae5b268e62..71f256045e 100644 --- a/lib/package.gi +++ b/lib/package.gi @@ -147,6 +147,14 @@ InstallGlobalFunction( RECORDS_FILE, function( name ) #F SetPackageInfo( ) ## InstallGlobalFunction( SetPackageInfo, function( record ) + local rnam, info; + if IsHPCGAP then + info := rec(); + for rnam in REC_NAMES(record) do + info.(rnam) := Immutable(record.(rnam)); + od; + record := info; + fi; GAPInfo.PackageInfoCurrent:= record; end ); @@ -179,7 +187,7 @@ InstallGlobalFunction( InitializePackagesInfoRecords, function( arg ) LogPackageLoadingMessage( PACKAGE_DEBUG, "exit InitializePackagesInfoRecords (no pkg directories found)", "GAP" ); - GAPInfo.PackagesInfo:= rec(); + GAPInfo.PackagesInfo:= AtomicRecord(); return; fi; @@ -261,7 +269,7 @@ InstallGlobalFunction( InitializePackagesInfoRecords, function( arg ) and GAPInfo.PackagesRestrictions.( pkgname ).OnInitialization( record ) = false then Add( GAPInfo.PackagesInfoRefuseLoad, record ); - elif LowercaseString( pkgname ) in ignore then + elif pkgname in ignore then LogPackageLoadingMessage( PACKAGE_DEBUG, Concatenation( "ignore package ", record.PackageName, " (user preference PackagesToIgnore)" ), "GAP" ); @@ -272,7 +280,7 @@ InstallGlobalFunction( InitializePackagesInfoRecords, function( arg ) elif IsRecord( record.PackageDoc ) then record.PackageDoc:= [ record.PackageDoc ]; fi; - Add( GAPInfo.PackagesInfo, record ); + Add( GAPInfo.PackagesInfo, MakeImmutable(record) ); fi; fi; fi; @@ -295,8 +303,9 @@ InstallGlobalFunction( InitializePackagesInfoRecords, function( arg ) else record.( name ):= [ r ]; fi; + MakeImmutable( record.( name ) ); od; - GAPInfo.PackagesInfo:= record; + GAPInfo.PackagesInfo:= AtomicRecord(record); GAPInfo.PackagesInfoInitialized:= true; LogPackageLoadingMessage( PACKAGE_DEBUG, @@ -1524,11 +1533,7 @@ InstallGlobalFunction( LoadPackage, function( arg ) # inside the package code causes the files to be read more than once. for pkgname in cycle do pos:= PositionSorted( paths[1], pkgname ); - GAPInfo.PackagesLoaded.( pkgname ):= paths[2][ pos ]; -#T Remove the following as soon as the obsolete variable has been removed! -if IsBoundGlobal( "PACKAGES_VERSIONS" ) then - ValueGlobal( "PACKAGES_VERSIONS" ).( pkgname ):= paths[2][ pos ][2]; -fi; + GAPInfo.PackagesLoaded.( pkgname ):= MakeImmutable(paths[2][ pos ]); od; # If the weight is 1 and the GAP library is not yet loaded @@ -1695,7 +1700,7 @@ InstallGlobalFunction( ExtendRootDirectories, function( rootpaths ) GAPInfo.RootPaths:= Immutable( Concatenation( GAPInfo.RootPaths, rootpaths ) ); # Clear the cache. - GAPInfo.DirectoriesLibrary:= rec(); + GAPInfo.DirectoriesLibrary:= AtomicRecord( rec() ); # Deal with an obsolete variable. if IsBoundGlobal( "GAP_ROOT_PATHS" ) then MakeReadWriteGlobal( "GAP_ROOT_PATHS" ); @@ -1948,7 +1953,11 @@ InstallGlobalFunction( GAPDocManualLabFromSixFile, local stream, entries, SecNumber, esctex, file; stream:= InputTextFile( sixfilepath ); - entries:= HELP_BOOK_HANDLER.GapDocGAP.ReadSix( stream ).entries; + + atomic readonly HELP_REGION do + entries:= HELP_BOOK_HANDLER.GapDocGAP.ReadSix( stream ).entries; + od; + SecNumber:= function( list ) if IsEmpty( list ) or list[1] = 0 then return ""; @@ -2307,8 +2316,8 @@ InstallGlobalFunction( ValidatePackageInfo, function( info ) ## ## ## -GAPInfo.PackagesRestrictions := rec( - anupq := rec( +GAPInfo.PackagesRestrictions := AtomicRecord(rec( + anupq := MakeImmutable(rec( OnInitialization := function( pkginfo ) if CompareVersionNumbers( pkginfo.Version, "1.3" ) = false then return false; @@ -2326,9 +2335,9 @@ GAPInfo.PackagesRestrictions := rec( "most recent version, see URL\n", " http://www.math.rwth-aachen.de/~Greg.Gamble/ANUPQ\n" ); fi; - end ), + end )), - autpgrp := rec( + autpgrp := MakeImmutable(rec( OnInitialization := function( pkginfo ) return true; end, @@ -2343,7 +2352,7 @@ GAPInfo.PackagesRestrictions := rec( "most recent version, see URL\n", " https://www.gap-system.org/Packages/autpgrp.html\n" ); fi; - end ) ); + end )) )); #############################################################################