unit pkgrepos; {$mode objfpc}{$H+} interface uses SysUtils,Classes, fprepos,pkgoptions; function GetRemoteRepositoryURL(const AFileName:string):string; procedure LoadLocalAvailableMirrors; procedure LoadLocalAvailableRepository; procedure LoadUnitConfigFromFile(APackage:TFPPackage;const AFileName: String); function LoadManifestFromFile(const AManifestFN:string):TFPPackage; procedure FindInstalledPackages(ACompilerOptions:TCompilerOptions;showdups:boolean=true); function PackageIsBroken(APackage:TFPPackage):boolean; function FindBrokenPackages(SL:TStrings):Boolean; procedure CheckFPMakeDependencies; function PackageInstalledVersionStr(const AName:String;const ShowUsed: boolean = false;const Local: boolean = false):string; function PackageInstalledStateStr(const AName:String):string; function PackageAvailableVersionStr(const AName:String):string; procedure ListAvailablePackages; procedure ListPackages(const ShowGlobalAndLocal: boolean); procedure ListRemoteRepository; procedure RebuildRemoteRepository; procedure SaveRemoteRepository; var AvailableMirrors : TFPMirrors; AvailableRepository, InstalledRepository : TFPRepository; implementation uses zipper, fpxmlrep, pkgglobals, pkgmessages; {***************************************************************************** Mirror Selection *****************************************************************************} var CurrentRemoteRepositoryURL : String; procedure LoadLocalAvailableMirrors; var S : String; X : TFPXMLMirrorHandler; begin if assigned(AvailableMirrors) then AvailableMirrors.Free; AvailableMirrors:=TFPMirrors.Create(TFPMirror); // Repository S:=GlobalOptions.LocalMirrorsFile; Log(vlDebug,SLogLoadingMirrorsFile,[S]); if not FileExists(S) then exit; try X:=TFPXMLMirrorHandler.Create; With X do try LoadFromXml(AvailableMirrors,S); finally Free; end; except on E : Exception do begin Log(vlError,E.Message); Error(SErrCorruptMirrorsFile,[S]); end; end; end; function SelectRemoteMirror:string; var i,j : Integer; Bucket, BucketCnt : Integer; M : TFPMirror; begin Result:=''; M:=nil; if assigned(AvailableMirrors) then begin // Create array for selection BucketCnt:=0; for i:=0 to AvailableMirrors.Count-1 do inc(BucketCnt,AvailableMirrors[i].Weight); // Select random entry Bucket:=Random(BucketCnt); M:=nil; for i:=0 to AvailableMirrors.Count-1 do begin for j:=0 to AvailableMirrors[i].Weight-1 do begin if Bucket=0 then begin M:=AvailableMirrors[i]; break; end; Dec(Bucket); end; if assigned(M) then break; end; end; if assigned(M) then begin Log(vlInfo,SLogSelectedMirror,[M.Name]); Result:=M.URL; end else Error(SErrFailedToSelectMirror); end; function GetRemoteRepositoryURL(const AFileName:string):string; begin if CurrentRemoteRepositoryURL='' then begin if GlobalOptions.RemoteRepository='auto' then CurrentRemoteRepositoryURL:=SelectRemoteMirror else CurrentRemoteRepositoryURL:=GlobalOptions.RemoteRepository; end; Result:=CurrentRemoteRepositoryURL+AFileName; end; {***************************************************************************** Local Repository *****************************************************************************} procedure ReadIniFile(Const AFileName: String;L:TStrings); Var F : TFileStream; Line : String; I,P,PC : Integer; begin F:=TFileStream.Create(AFileName,fmOpenRead); Try L.LoadFromStream(F); // Fix lines. For I:=L.Count-1 downto 0 do begin Line:=L[I]; P:=Pos('=',Line); PC:=Pos(';',Line); // Comment line. If (P=0) or ((PC<>0) and (PC
'' then
APackage.Checksum:=StrToInt(V)
else
APackage.Checksum:=$ffffffff;
// Load dependencies
V:=L.Values['depends'];
DepSL:=TStringList.Create;
DepSL.CommaText:=V;
for i:=0 to DepSL.Count-1 do
begin
DepName:=DepSL[i];
k:=Pos('|',DepName);
if k>0 then
begin
DepChecksum:=StrToInt(Copy(DepName,k+1,Length(DepName)-k));
DepName:=Copy(DepName,1,k-1);
end
else
DepChecksum:=$ffffffff;
D:=nil;
for j:=0 to APackage.Dependencies.Count-1 do
begin
D:=APackage.Dependencies[j];
if D.PackageName=DepName then
break;
D:=nil;
end;
if not assigned(D) then
D:=APackage.AddDependency(DepName,'');
D.RequireChecksum:=DepChecksum;
end;
DepSL.Free;
Finally
L.Free;
end;
end;
procedure FindInstalledPackages(ACompilerOptions:TCompilerOptions;showdups:boolean=true);
function AddInstalledPackage(const AName,AFileName: String; const Local: boolean):TFPPackage;
begin
result:=InstalledRepository.FindPackage(AName);
if not assigned(result) then
result:=InstalledRepository.AddPackage(AName)
else
begin
result.UnusedVersion:=result.Version;
// Log packages found in multiple locations (local and global) ?
if showdups then
Log(vlDebug,SDbgPackageMultipleLocations,[result.Name,ExtractFilePath(AFileName)]);
end;
result.InstalledLocally:=Local;
end;
procedure LoadPackagefpcFromFile(APackage:TFPPackage;const AFileName: String);
Var
L : TStrings;
V : String;
begin
L:=TStringList.Create;
Try
ReadIniFile(AFileName,L);
V:=L.Values['version'];
APackage.Version.AsString:=V;
Finally
L.Free;
end;
end;
function CheckUnitDir(const AUnitDir:string; const Local: boolean):boolean;
var
SR : TSearchRec;
P : TFPPackage;
UD,UF : String;
begin
Result:=false;
if FindFirst(IncludeTrailingPathDelimiter(AUnitDir)+AllFiles,faDirectory,SR)=0 then
begin
Log(vlDebug,SLogFindInstalledPackages,[AUnitDir]);
repeat
if ((SR.Attr and faDirectory)=faDirectory) and (SR.Name<>'.') and (SR.Name<>'..') then
begin
UD:=IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(AUnitDir)+SR.Name);
// Try new fpunits.conf
UF:=UD+UnitConfigFileName;
if FileExistsLog(UF) then
begin
P:=AddInstalledPackage(SR.Name,UF,Local);
LoadUnitConfigFromFile(P,UF)
end
else
begin
// Try Old style Package.fpc
UF:=UD+'Package.fpc';
if FileExistsLog(UF) then
begin
P:=AddInstalledPackage(SR.Name,UF,Local);
LoadPackagefpcFromFile(P,UF);
end;
end;
end;
until FindNext(SR)<>0;
end;
end;
begin
if assigned(InstalledRepository) then
InstalledRepository.Free;
InstalledRepository:=TFPRepository.Create(nil);
// First scan the global directory
// The local directory will overwrite the versions
if ACompilerOptions.GlobalUnitDir<>'' then
CheckUnitDir(ACompilerOptions.GlobalUnitDir, False);
if ACompilerOptions.LocalUnitDir<>'' then
CheckUnitDir(ACompilerOptions.LocalUnitDir, True);
end;
function PackageIsBroken(APackage:TFPPackage):boolean;
var
j : integer;
D : TFPDependency;
DepPackage : TFPPackage;
begin
result:=false;
for j:=0 to APackage.Dependencies.Count-1 do
begin
D:=APackage.Dependencies[j];
if (CompilerOptions.CompilerOS in D.OSes) and
(CompilerOptions.CompilerCPU in D.CPUs) then
begin
DepPackage:=InstalledRepository.FindPackage(D.PackageName);
// Don't stop on missing dependencies
if assigned(DepPackage) then
begin
if (DepPackage.Checksum<>D.RequireChecksum) then
begin
Log(vlInfo,SLogPackageChecksumChanged,[APackage.Name,D.PackageName]);
result:=true;
exit;
end;
end
else
Log(vlDebug,SDbgObsoleteDependency,[D.PackageName]);
end;
end;
end;
function FindBrokenPackages(SL:TStrings):Boolean;
var
i : integer;
P : TFPPackage;
begin
SL.Clear;
for i:=0 to InstalledRepository.PackageCount-1 do
begin
P:=InstalledRepository.Packages[i];
if PackageIsBroken(P) then
SL.Add(P.Name);
end;
Result:=(SL.Count>0);
end;
procedure CheckFPMakeDependencies;
var
i : Integer;
P,AvailP : TFPPackage;
AvailVerStr : string;
ReqVer : TFPVersion;
begin
// Reset availability
for i:=1 to FPMKUnitDepCount do
FPMKUnitDepAvailable[i]:=false;
// Not version check needed in Recovery mode, we always need to use
// the internal bootstrap procedure
if GlobalOptions.RecoveryMode then
exit;
// Check for fpmkunit dependencies
for i:=1 to FPMKUnitDepCount do
begin
P:=InstalledRepository.FindPackage(FPMKUnitDeps[i].package);
if P<>nil then
begin
AvailP:=AvailableRepository.FindPackage(FPMKUnitDeps[i].package);
if AvailP<>nil then
AvailVerStr:=AvailP.Version.AsString
else
AvailVerStr:='