diff --git a/lib/package.gd b/lib/package.gd index ac5c6ae8ab..80f9c30ebc 100644 --- a/lib/package.gd +++ b/lib/package.gd @@ -43,9 +43,10 @@ ## is a mutable record, ## its component names are the names of those ⪆ packages that are ## already loaded. -## The component for each package is a list of length three, the entries +## The component for each package is a list of length four, the entries ## being the path to the ⪆ root directory that contains the package, -## the package version, and the package name. +## the package version, the package name, and a boolean indicating whether +## the package finished loading. ## For each package, the value gets bound in the ## call. ##

@@ -353,16 +354,22 @@ DeclareGlobalFunction( "TestPackageAvailability" ); ############################################################################# ## -#F IsPackageLoaded( [, ][, ] ) +#F IsPackageLoaded( [, ] ) ## ## <#GAPDoc Label="IsPackageLoaded"> ## -## +## ## ## -## This function return true if the given package is loaded, and -## false otherwise. For details on the meaning of the arguments, -## see . +## For strings name and version, this function tests +## whether the &GAP; package name is already loaded in a +## version that is at least version, or equal to version +## if the first character of version is = +## (see for further +## details about version numbers). +##

+## The result is true if the package is already loaded, +## false otherwise. ## ## ## <#/GAPDoc> diff --git a/lib/package.gi b/lib/package.gi index 6978b7371e..e169400601 100644 --- a/lib/package.gi +++ b/lib/package.gi @@ -888,7 +888,7 @@ InstallGlobalFunction( PackageAvailabilityInfo, record.InstallationPaths:= record_local.InstallationPaths; Add( record.InstallationPaths, [ name, [ inforec.InstallationPath, inforec.Version, - inforec.PackageName ] ] ); + inforec.PackageName, false ] ] ); record.Dependencies:= record_local.Dependencies; record.StrongDependencies:= record_local.StrongDependencies; record.AlreadyHandled:= record_local.AlreadyHandled; @@ -1005,13 +1005,20 @@ InstallGlobalFunction( TestPackageAvailability, function( arg ) ############################################################################# ## -#F IsPackageLoaded( [, ][, ] ) +#F IsPackageLoaded( [, ] ) ## -InstallGlobalFunction( IsPackageLoaded, function( arg ) +InstallGlobalFunction( IsPackageLoaded, function( name, version... ) local result; - result := CallFuncList( TestPackageAvailability, arg ); - return result = true; + if Length(version) > 0 then + version := version[1]; + fi; + result := IsPackageMarkedForLoading( name, version ); + if result then + # check if the package actually completed loading + result := GAPInfo.PackagesLoaded.( name )[4]; + fi; + return result; end ); @@ -1319,10 +1326,10 @@ BindGlobal( "LoadPackage_ReadImplementationParts", local pair, info, bannerstring, fun, u, pkgname, namespace; for pair in secondrun do + namespace := pair[1].PackageName; + pkgname := LowercaseString( namespace ); 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 ); @@ -1334,6 +1341,9 @@ BindGlobal( "LoadPackage_ReadImplementationParts", "finish reading file 'read.g'", namespace ); fi; + # mark the package as completely loaded + GAPInfo.PackagesLoaded.(pkgname)[4] := true; + MakeImmutable( GAPInfo.PackagesLoaded.(pkgname) ); od; # Show the banners. @@ -1560,7 +1570,8 @@ 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 ):= MakeImmutable(paths[2][ pos ]); + # the following entry is made immutable in LoadPackage_ReadImplementationParts + GAPInfo.PackagesLoaded.( pkgname ):= paths[2][ pos ]; od; # If the weight is 1 and the GAP library is not yet loaded diff --git a/tst/mockpkg/PackageInfo.g b/tst/mockpkg/PackageInfo.g index 07c321c18a..02002fe8b2 100644 --- a/tst/mockpkg/PackageInfo.g +++ b/tst/mockpkg/PackageInfo.g @@ -85,7 +85,10 @@ Dependencies := rec( ExternalConditions := [ ], ), -AvailabilityTest := ReturnTrue, +AvailabilityTest := function() + Print("oops, should not print here\n"); + return true; +end, # use an empty banner string, so that we get identical output regardless # of whether GAP is started with -q or -b, or not. diff --git a/tst/testinstall/package.tst b/tst/testinstall/package.tst index 3305c98126..ce4d92b0c0 100644 --- a/tst/testinstall/package.tst +++ b/tst/testinstall/package.tst @@ -182,6 +182,47 @@ true # # Deal with mock package +# + +# first, force "unload" it (this is a very bad idea in general, +# but for this mock package, it is OK because we control everything) +gap> Unbind(GAPInfo.PackagesInfo.mockpkg); +gap> Unbind(GAPInfo.PackagesLoaded.mockpkg); +gap> for n in [ "mockpkg_GlobalFunction", "mockpkg_Operation", "mockpkg_Attribute", "mockpkg_Property" ] do +> if IsBoundGlobal(n) then +> MakeReadWriteGlobal(n); +> UnbindGlobal(n); +> fi; +> od; + +# +gap> TestPackageAvailability("non-existing-package"); +fail +gap> TestPackageAvailability("mockpkg"); +fail +gap> TestPackageAvailability("mockpkg", "=0.1"); +fail +gap> TestPackageAvailability("mockpkg", ">=0.1"); +fail +gap> TestPackageAvailability("mockpkg", "=2.0"); +fail +gap> TestPackageAvailability("mockpkg", ">=2.0"); +fail + +# +gap> IsPackageLoaded("non-existing-package"); +false +gap> IsPackageLoaded("mockpkg"); +false +gap> IsPackageLoaded("mockpkg", "=0.1"); +false +gap> IsPackageLoaded("mockpkg", ">=0.1"); +false +gap> IsPackageLoaded("mockpkg", "=2.0"); +false +gap> IsPackageLoaded("mockpkg", ">=2.0"); +false + # gap> mockpkgpath := DirectoriesLibrary("tst/mockpkg")[1];; gap> ValidatePackageInfo(Filename(mockpkgpath, "PackageInfo.g")); @@ -196,12 +237,45 @@ gap> GetPackageNameForPrefix("mock"); # point GAP at mockpkg gap> SetPackagePath("mockpkg", mockpkgpath); -# ... now it "knows" it +# ... now GAP "knows" the package gap> GetPackageNameForPrefix("mock"); "mockpkg" +# +gap> TestPackageAvailability("non-existing-package"); +fail +gap> TestPackageAvailability("mockpkg") = Filename(mockpkgpath, ""); +oops, should not print here +true +gap> TestPackageAvailability("mockpkg", "=0.1") = Filename(mockpkgpath, ""); +oops, should not print here +true +gap> TestPackageAvailability("mockpkg", ">=0.1") = Filename(mockpkgpath, ""); +oops, should not print here +true +gap> TestPackageAvailability("mockpkg", "=2.0"); +fail +gap> TestPackageAvailability("mockpkg", ">=2.0"); +fail + +# +gap> IsPackageLoaded("non-existing-package"); +false +gap> IsPackageLoaded("mockpkg"); +false +gap> IsPackageLoaded("mockpkg", "=0.1"); +false +gap> IsPackageLoaded("mockpkg", ">=0.1"); +false +gap> IsPackageLoaded("mockpkg", "=2.0"); +false +gap> IsPackageLoaded("mockpkg", ">=2.0"); +false + # instruct GAP to load the package, and record all its declarations gap> PackageVariablesInfo("mockpkg", "0.1");; +oops, should not print here +oops, should not print here gap> ShowPackageVariables("mockpkg"); new global functions: mockpkg_GlobalFunction( )*