Browse Source

* Replaced the local- and global-repositories with a list of repositories.
A repository can contain installed or packages available for installation.
The repositories have a helper-class that contains information about
where the packages are installed and such. Things like installing a
package from the current-directory are done using temporary 'fake'
repositories. The TpkgFPpkg-class keeps track of all repositories
and packages.

git-svn-id: trunk@34503 -

joost 9 years ago
parent
commit
79d2d05c58

+ 1 - 0
.gitattributes

@@ -3392,6 +3392,7 @@ packages/fppkg/src/pkghandler.pp svneol=native#text/plain
 packages/fppkg/src/pkgmessages.pp svneol=native#text/plain
 packages/fppkg/src/pkgmkconv.pp svneol=native#text/plain
 packages/fppkg/src/pkgoptions.pp svneol=native#text/plain
+packages/fppkg/src/pkgpackagesstructure.pp svneol=native#text/plain
 packages/fppkg/src/pkgrepos.pp svneol=native#text/plain
 packages/fppkg/src/pkgwget.pp svneol=native#text/plain
 packages/fuse/Makefile svneol=native#text/plain

+ 1 - 0
packages/fppkg/fpmake.pp

@@ -70,6 +70,7 @@ begin
     T:=P.Targets.AddUnit('pkgfpmake.pp');
     T.Dependencies.AddInclude('fpmkunitsrc.inc');
     T:=P.Targets.AddUnit('pkgcommands.pp');
+    T:=P.Targets.AddUnit('pkgpackagesstructure.pp');
 
     T:=P.Targets.AddUnit('pkgwget.pp', TargetsWithWGet);
     T:=P.Targets.AddUnit('pkgfphttp.pp', TargetsWithfpWeb);

+ 52 - 5
packages/fppkg/src/fprepos.pp

@@ -28,6 +28,21 @@ Const
 
 
 type
+  TFPRepositoryType = (fprtUnknown, fprtInstalled, fprtAvailable);
+  TFPRepository = class;
+  TFPPackage = class;
+
+  { TFPCustomPackagesStructure }
+
+  TFPCustomPackagesStructure = Class(TComponent)
+  public
+    function AddPackagesToRepository(ARepository: TFPRepository): Boolean; virtual; abstract;
+    function GetUnitDirectory(APackage: TFPPackage): string; virtual;
+    function GetBuildPathDirectory(APackage: TFPPackage): string; virtual;
+    function GetPrefix: string; virtual;
+    function GetBaseInstallDir: string; virtual;
+    function UnzipBeforeUse: Boolean; virtual;
+  end;
 
   { TFPDependency }
 
@@ -76,7 +91,6 @@ type
     FKeywords: String;
     FRecompileBroken: boolean;
     FSourcePath: string;
-    FInstalledLocally: boolean;
     FIsFPMakeAddIn: boolean;
     FLicense: String;
     FName: String;
@@ -92,6 +106,7 @@ type
     // Installation info
     FChecksum : cardinal;
     FLocalFileName : String;
+    FPackagesStructure: TFPCustomPackagesStructure;
     function GetFileName: String;
     procedure SetName(const AValue: String);
     procedure SetUnusedVersion(const AValue: TFPVersion);
@@ -107,9 +122,6 @@ type
     Procedure Assign(Source : TPersistent); override;
     Function AddDependency(Const APackageName : String; const AMinVersion : String = '') : TFPDependency;
     Property Dependencies : TFPDependencies Read FDependencies;
-    // Only for installed packages: (is false for packages which are installed globally)
-    Property InstalledLocally : boolean read FInstalledLocally write FInstalledLocally;
-    Property UnusedVersion : TFPVersion Read FUnusedVersion Write SetUnusedVersion;
     Property RecompileBroken : boolean read FRecompileBroken write FRecompileBroken;
     Property OSes : TOSes Read FOSes Write FOses;
     Property CPUs : TCPUs Read FCPUs Write FCPUs;
@@ -133,6 +145,7 @@ type
     Property FPMakeOptionsString : string read FFPMakeOptionsString write FFPMakeOptionsString;
     // Manual package from commandline not in official repository
     Property LocalFileName : String Read FLocalFileName Write FLocalFileName;
+    Property PackagesStructure: TFPCustomPackagesStructure read FPackagesStructure write FPackagesStructure;
   end;
 
   { TFPPackages }
@@ -158,9 +171,13 @@ type
 
   TFPRepository = Class(TComponent)
   Private
+    FDefaultPackagesStructure: TFPCustomPackagesStructure;
     FMaxDependencyLevel : Integer;
     FBackUpFiles: Boolean;
     FFileName: String;
+    FDescription: string;
+    FRepositoryName: string;
+    FRepositoryType: TFPRepositoryType;
     function GetPackage(Index : Integer): TFPPackage;
     function GetPackageCount: Integer;
   Protected
@@ -193,6 +210,11 @@ type
     Property BackupFiles : Boolean Read FBackUpFiles Write FBackupFiles;
     Property MaxDependencyLevel : Integer Read FMaxDependencyLevel Write FMaxDependencyLevel;
     Property PackageCollection : TFPPackages Read FPackages;
+
+    Property RepositoryName: string read FRepositoryName write FRepositoryName;
+    Property Description: string read FDescription write FDescription;
+    Property RepositoryType: TFPRepositoryType read FRepositoryType write FRepositoryType;
+    Property DefaultPackagesStructure: TFPCustomPackagesStructure read FDefaultPackagesStructure write FDefaultPackagesStructure;
   end;
   TFPRepositoryClass = class of TFPRepository;
 
@@ -298,6 +320,32 @@ begin
   OS:=StringToOs(Copy(S,P+1,Length(S)-P));
 end;
 
+{ TFPCustomPackagesStructure }
+
+function TFPCustomPackagesStructure.GetUnitDirectory(APackage: TFPPackage): string;
+begin
+  raise Exception.Create('There is no unit-directory available for this package.');
+end;
+
+function TFPCustomPackagesStructure.GetBuildPathDirectory(APackage: TFPPackage): string;
+begin
+  Result := '';
+end;
+
+function TFPCustomPackagesStructure.GetPrefix: string;
+begin
+  raise Exception.Create('There is no prefix for this repository.');
+end;
+
+function TFPCustomPackagesStructure.GetBaseInstallDir: string;
+begin
+  raise Exception.Create('It is not possible to install into this repository.');
+end;
+
+function TFPCustomPackagesStructure.UnzipBeforeUse: Boolean;
+begin
+  Result := False;
+end;
 
 { TFPPackage }
 
@@ -528,7 +576,6 @@ begin
       DownloadURL:=P.DownloadURL;
       SourcePath:=P.SourcePath;
       FPMakeOptionsString:=P.FPMakeOptionsString;
-      InstalledLocally:=P.InstalledLocally;
       OSes:=P.OSes;
       CPUs:=P.CPUs;
       FileName:=P.FileName;

+ 64 - 88
packages/fppkg/src/pkgcommands.pp

@@ -16,6 +16,7 @@ uses
   pkgoptions,
   pkgdownload,
   pkgrepos,
+  pkgFppkg,
   fpxmlrep,
   fprepos;
 
@@ -145,7 +146,7 @@ var
 begin
   if PackageName='' then
     Error(SErrNoPackageSpecified);
-  P:=AvailableRepository.PackageByName(PackageName);
+  P:=GFPpkg.PackageByName(PackageName, pkgpkAvailable);
 
   log(llProgres,SLogPackageInfoName,[P.Name]);
   S := P.Email;
@@ -182,7 +183,6 @@ end;
 procedure TCommandUnInstall.Execute;
 var
   AvailP: TFPPackage;
-  APackage: TFPPackage;
 begin
   if PackageName<>'' then
     begin
@@ -192,22 +192,11 @@ begin
         end
       else if (PackageName<>CurrentDirPackageName) then
         begin
-          AvailP:=AvailableRepository.FindPackage(PackageName);
-          if not assigned(AvailP) then
+          AvailP:=GFPpkg.FindPackage(PackageName, pkgpkAvailable);
+          if Assigned(AvailP) then
             begin
-              APackage := InstalledRepository.FindPackage(PackageName);
-              if assigned(APackage) and (APackage.SourcePath<>'') then
-                begin
-                  AvailP := AvailableRepository.AddPackage(PackageName);
-                  AvailP.Assign(APackage);
-                  // The package won't be recompiled, but should be handled as such.
-                  AvailP.RecompileBroken:=true;
-                end
-              else
-                begin
-                  // The package is not available locally, download and unzip it.
-                  ExecuteAction(PackageName,'unzip');
-                end;
+              if AvailP.PackagesStructure.UnzipBeforeUse then
+                ExecuteAction(PackageName,'unzip');
             end;
         end;
     end;
@@ -252,7 +241,7 @@ begin
   Log(llCommands,SLogDownloading,[PackagesURL,GFPpkg.Options.GlobalSection.LocalPackagesFile]);
   DownloadFile(PackagesURL,GFPpkg.Options.GlobalSection.LocalPackagesFile);
   // Read the repository again
-  LoadLocalAvailableRepository;
+  GFPpkg.ScanAvailablePackages;
   // no need to log errors again
   FindInstalledPackages(GFPpkg.CompilerOptions,False);
 end;
@@ -277,7 +266,7 @@ var
 begin
   if PackageName='' then
     Error(SErrNoPackageSpecified);
-  P:=AvailableRepository.PackageByName(PackageName);
+  P:=GFPpkg.PackageByName(PackageName, pkgpkAvailable);
   if not FileExists(PackageLocalArchive(P)) then
     ExecuteAction(PackageName,'downloadpackage');
 end;
@@ -291,7 +280,7 @@ Var
 begin
   if PackageName='' then
     Error(SErrNoPackageSpecified);
-  P:=AvailableRepository.PackageByName(PackageName);
+  P:=GFPpkg.PackageByName(PackageName, pkgpkAvailable);
   BuildDir:=PackageBuildPath(P);
   ArchiveFile:=PackageLocalArchive(P);
   if not FileExists(ArchiveFile) then
@@ -359,13 +348,13 @@ begin
           end
       else
         begin
+          P:=GFPpkg.FindPackage(PackageName, pkgpkAvailable);
+          if Assigned(P) then
+            begin
+              if P.PackagesStructure.UnzipBeforeUse then
+                ExecuteAction(PackageName,'unzip');
+            end;
           ExecuteAction(PackageName,'installdependencies');
-          // Check if the package is not installed but being recompiled because of changed
-          // dependencies while the original source is still available.
-          P := AvailableRepository.FindPackage(PackageName);
-          if not (assigned(P) and P.RecompileBroken and (P.SourcePath<>'')) then
-            // The package is not available locally, download and unzip it.
-            ExecuteAction(PackageName,'unzip');
         end;
     end;
   ExecuteAction(PackageName,'fpmakebuild');
@@ -377,58 +366,30 @@ procedure TCommandInstall.Execute;
 var
   S : String;
   P   : TFPPackage;
-
-  function GetUnitConfigFilename: string;
-  begin
-    if P.RecompileBroken then
-      begin
-        // If the package is recompiled, the installation-location is dependent on where
-        // the package was installed originally.
-        if P.InstalledLocally then
-          Result:=GFPpkg.CompilerOptions.LocalUnitDir
-        else
-          Result:=GFPpkg.CompilerOptions.GlobalUnitDir;
-        // Setting RecompileBroken to false is in a strict sense not needed. But it is better
-        // to clean this temporary flag, to avoid problems with changes in the future
-        P.RecompileBroken := false;
-        AvailableRepository.FindPackage(P.Name).RecompileBroken:=false;
-      end
-    else
-      begin
-        if (IsSuperUser or GFPpkg.Options.CommandLineSection.InstallGlobal) then
-          Result:=GFPpkg.CompilerOptions.GlobalUnitDir
-        else
-          Result:=GFPpkg.CompilerOptions.LocalUnitDir;
-      end;
-    Result:=IncludeTrailingPathDelimiter(Result)+S+PathDelim+UnitConfigFileName;
-  end;
+  InstallRepo: TFPRepository;
 
   function GetFpmFilename: string;
+  var
+    ConfFile: string;
   begin
-    if P.RecompileBroken then
-      begin
-        // If the package is recompiled, the installation-location is dependent on where
-        // the package was installed originally.
-        if P.InstalledLocally then
-          Result:=GFPpkg.CompilerOptions.LocalInstallDir
-        else
-          Result:=GFPpkg.CompilerOptions.GlobalInstallDir;
-        // Setting RecompileBroken to false is in a strict sense not needed. But it is better
-        // to clean this temporary flag, to avoid problems with changes in the future
-        P.RecompileBroken := false;
-        AvailableRepository.FindPackage(P.Name).RecompileBroken:=false;
-      end
-    else
+    Result := '';
+    if Assigned(InstallRepo.DefaultPackagesStructure) then
       begin
-        if (IsSuperUser or GFPpkg.Options.CommandLineSection.InstallGlobal) then
-          Result:=GFPpkg.CompilerOptions.GlobalInstallDir
+        Result := InstallRepo.DefaultPackagesStructure.GetBaseInstallDir;
+        ConfFile := IncludeTrailingPathDelimiter(Result)+'fpmkinst'+PathDelim+GFPpkg.CompilerOptions.CompilerTarget+PathDelim+s+FpmkExt;
+        if not FileExistsLog(ConfFile) then
+          begin
+            // If there is no fpm-file, search for an (obsolete, pre-2.7.x)
+            // fpunits.cfg-file
+            ConfFile := IncludeTrailingPathDelimiter(Result)+S+PathDelim+UnitConfigFileName;
+            if FileExistsLog(ConfFile) then
+              Result := ConfFile;
+          end
         else
-          Result:=GFPpkg.CompilerOptions.LocalInstallDir;
+          Result := ConfFile;
       end;
-    Result:=IncludeTrailingPathDelimiter(Result)+'fpmkinst'+PathDelim+GFPpkg.CompilerOptions.CompilerTarget+PathDelim+s+FpmkExt;
   end;
 
-
 var
   UFN : String;
 begin
@@ -448,19 +409,24 @@ begin
         end
       else
         S:=PackageName;
-      P:=InstalledRepository.FindPackage(S);
-      if not assigned(P) then
-        P:=InstalledRepository.AddPackage(S);
-
-      UFN:=GetFpmFilename;
-      // If there is no fpm-file, search for an (obsolete, pre-2.7.x)
-      // fpunits.cfg-file
-      if not FileExists(ufn) then
-        UFN:=GetUnitConfigFilename;
-
-      P.LoadUnitConfigFromFile(UFN);
-      if P.IsFPMakeAddIn then
-        AddFPMakeAddIn(P);
+
+      InstallRepo := GFPpkg.RepositoryByName(GFPpkg.Options.CommandLineSection.InstallRepository);
+      if Assigned(InstallRepo) then
+        begin
+          P := InstallRepo.PackageByName(S);
+          if not Assigned(P) then
+            P := InstallRepo.AddPackage(S);
+          if Assigned(P) then
+            begin
+              UFN:=GetFpmFilename;
+              if UFN<>'' then
+                begin
+                  P.LoadUnitConfigFromFile(UFN);
+                  if P.IsFPMakeAddIn then
+                    AddFPMakeAddIn(P);
+                end;
+            end;
+        end;
     end
   else
     ExecuteAction(PackageName,'fpmakeinstall');
@@ -480,6 +446,15 @@ end;
 
 
 procedure TCommandInstallDependencies.Execute;
+
+  function PackageVersionStr(APackage: TFPPackage): string;
+  begin
+    if Assigned(APackage) then
+      Result := APackage.Version.AsString
+    else
+      Result := 'N/A';
+  end;
+
 var
   i : Integer;
   MissingDependency,
@@ -520,7 +495,7 @@ begin
         end;
     end
   else
-    P:=AvailableRepository.PackageByName(PackageName);
+    P:=GFPpkg.PackageByName(PackageName, pkgpkBoth);
 
   MissingDependency:=nil;
   while assigned(P) do
@@ -535,12 +510,13 @@ begin
           // Skip dependencies that are available within the fpmake-file itself
           else if not (assigned(ManifestPackages) and assigned(ManifestPackages.FindPackage(D.PackageName))) then
             begin
-              InstalledP:=InstalledRepository.FindPackage(D.PackageName);
+              AvailP := nil;
+              InstalledP:=GFPpkg.FindPackage(D.PackageName, pkgpkInstalled);
               // Need installation?
               if not assigned(InstalledP) or
                  (InstalledP.Version.CompareVersion(D.MinVersion)<0) then
                 begin
-                  AvailP:=AvailableRepository.FindPackage(D.PackageName);
+                  AvailP:=GFPpkg.FindPackage(D.PackageName, pkgpkAvailable);
                   if not assigned(AvailP) or
                      (AvailP.Version.CompareVersion(D.MinVersion)<0) then
                     begin
@@ -564,8 +540,8 @@ begin
                     status:='OK';
                 end;
               Log(llInfo,SLogPackageDependency,
-                  [D.PackageName,D.MinVersion.AsString,PackageInstalledVersionStr(D.PackageName),
-                   PackageAvailableVersionStr(D.PackageName),status])
+                  [D.PackageName,D.MinVersion.AsString,PackageVersionStr(InstalledP),
+                   PackageVersionStr(AvailP),status])
             end
         end;
       if assigned(ManifestPackages) and (PackNr<ManifestPackages.Count-1)  then

+ 3 - 2
packages/fppkg/src/pkgdownload.pp

@@ -48,7 +48,8 @@ uses
   pkgglobals,
   pkgoptions,
   pkgmessages,
-  pkgrepos;
+  pkgrepos,
+  pkgFppkg;
 
 var
   DownloaderList  : TFPHashList;
@@ -164,7 +165,7 @@ var
   DownloaderClass : TBaseDownloaderClass;
   P : TFPPackage;
 begin
-  P:=AvailableRepository.PackageByName(PackageName);
+  P:=GFPpkg.PackageByName(PackageName, pkgpkAvailable);
   DownloaderClass:=GetDownloader(GFPpkg.Options.GlobalSection.Downloader);
   with DownloaderClass.Create(nil) do
     try

+ 25 - 27
packages/fppkg/src/pkgfpmake.pp

@@ -16,6 +16,7 @@ uses
   pkgglobals,
   pkgmessages,
   pkgrepos,
+  pkgFppkg,
   fpxmlrep;
 
 type
@@ -135,25 +136,17 @@ Procedure TFPMakeCompiler.Execute;
 var
   OOptions : string;
 
-  function CheckUnitDir(const AUnitName:string;Out AUnitDir:string):boolean;
+  function CheckUnitDir(const APackageName:string;Out AUnitDir:string):boolean;
+  var
+    P: TFPPackage;
   begin
     Result:=false;
-    if GFPpkg.FpmakeCompilerOptions.LocalUnitDir<>'' then
-      begin
-        AUnitDir:=IncludeTrailingPathDelimiter(GFPpkg.FPMakeCompilerOptions.LocalUnitDir+AUnitName);
-        if DirectoryExistsLog(AUnitDir) then
-          begin
-            Result:=true;
-            exit;
-          end;
-      end;
-    AUnitDir:=IncludeTrailingPathDelimiter(GFPpkg.FPMakeCompilerOptions.GlobalUnitDir+AUnitName);
-    if DirectoryExistsLog(AUnitDir) then
+    P := GFPpkg.FPMakeRepoFindPackage(APackageName, pkgpkInstalled);
+    if Assigned(P) then
       begin
-        Result:=true;
-        exit;
+        AUnitDir := P.PackagesStructure.GetUnitDirectory(P);
+        Result := DirectoryExistsLog(AUnitDir);
       end;
-    AUnitDir:='';
   end;
 
   procedure AddOption(const s:string);
@@ -173,7 +166,7 @@ Var
   HaveFpmake : boolean;
   P : TFPPackage;
 begin
-  P:=AvailableRepository.PackageByName(PackageName);
+  P:=GFPpkg.PackageByName(PackageName, pkgpkAvailable);
   NeedFPMKUnitSource:=false;
   OOptions:='';
   SetCurrentDir(PackageBuildPath(P));
@@ -258,10 +251,11 @@ end;
 
 Function TFPMakeRunner.RunFPMake(const Command:string) : Integer;
 Var
-  ManifestPackage,
   P : TFPPackage;
   FPMakeBin,
   OOptions : string;
+  InstallRepo: TFPRepository;
+  i: Integer;
 
   procedure AddOption(const s:string);
   begin
@@ -304,7 +298,7 @@ begin
   // Does the current package support this CPU-OS?
   if PackageName<>'' then
     begin
-      P:=AvailableRepository.PackageByName(PackageName);
+      P:=GFPpkg.PackageByName(PackageName, pkgpkAvailable);
       if (PackageName=CurrentDirPackageName) and (FileExists(ManifestFileName)) then
         ObtainSupportedTargetsFromManifest(p);
     end
@@ -347,18 +341,22 @@ begin
   AddOption('--compiler='+GFPpkg.CompilerOptions.Compiler);
   AddOption('--cpu='+CPUToString(GFPpkg.CompilerOptions.CompilerCPU));
   AddOption('--os='+OSToString(GFPpkg.CompilerOptions.CompilerOS));
-  if IsSuperUser or GFPpkg.Options.CommandLineSection.InstallGlobal then
+
+  InstallRepo := GFPpkg.RepositoryByName(GFPpkg.Options.CommandLineSection.InstallRepository);
+
+  if not Assigned(InstallRepo.DefaultPackagesStructure) then
     begin
-      CondAddOption('--prefix',GFPpkg.CompilerOptions.GlobalPrefix);
-      CondAddOption('--baseinstalldir',GFPpkg.CompilerOptions.GlobalInstallDir);
-    end
-  else
+      Error(SErrIllConfRepository,[InstallRepo.RepositoryName]);
+      Exit;
+    end;
+  CondAddOption('--prefix',InstallRepo.DefaultPackagesStructure.GetPrefix);
+  CondAddOption('--baseinstalldir',InstallRepo.DefaultPackagesStructure.GetBaseInstallDir);
+
+  for i := GFPpkg.Options.SectionList.Count -1 downto 0 do
     begin
-      CondAddOption('--prefix',GFPpkg.CompilerOptions.LocalPrefix);
-      CondAddOption('--baseinstalldir',GFPpkg.CompilerOptions.LocalInstallDir);
+      if GFPpkg.Options.SectionList[ i ] is TFppkgRepositoryOptionSection then
+        CondAddOption('--searchpath', TFppkgRepositoryOptionSection(GFPpkg.Options.SectionList[ i ]).Path);
     end;
-  CondAddOption('--localunitdir',GFPpkg.CompilerOptions.LocalInstallDir);
-  CondAddOption('--globalunitdir',GFPpkg.CompilerOptions.GlobalInstallDir);
 
   { Run FPMake }
   FPMakeBin:='fpmake'+ExeExt;

+ 198 - 10
packages/fppkg/src/pkgfppkg.pp

@@ -11,27 +11,48 @@ uses
   fprepos,
   pkgmessages,
   pkgglobals,
-  pkgoptions;
+  pkgoptions,
+  pkgPackagesStructure;
 
 type
 
   { TpkgFPpkg }
 
+  TpkgPackageKind = (pkgpkInstalled, pkgpkAvailable, pkgpkBoth);
   TpkgFPpkg = class(TComponent)
   private
+    FFPMakeRepositoryList: TComponentList;
+    FRepositoryList: TComponentList;
     FOptions: TFppkgOptions;
     FCompilerOptions: TCompilerOptions;
     FFpmakeCompilerOptions: TCompilerOptions;
-  protected
+    procedure ScanPackagesOnDisk(ACompilerOptions: TCompilerOptions; ARepositoryList: TComponentList);
+    function  FindPackage(ARepositoryList: TComponentList; APackageName: string; APackageKind: TpkgPackageKind): TFPPackage;
   public
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
 
     procedure InitializeGlobalOptions(CfgFile: string);
     procedure InitializeCompilerOptions;
+    procedure ScanAvailablePackages;
+    procedure ScanPackages;
+
+    function FPMakeRepoFindPackage(APackageName: string; APackageKind: TpkgPackageKind): TFPPackage;
+    function FindPackage(APackageName: string; APackageKind: TpkgPackageKind): TFPPackage;
+    function PackageByName(APackageName: string; APackageKind: TpkgPackageKind): TFPPackage;
+
+    function FindRepository(ARepositoryName: string): TFPRepository;
+    function RepositoryByName(ARepositoryName: string): TFPRepository;
+
+    procedure ScanInstalledPackagesForAvailablePackages;
+
     property Options: TFppkgOptions read FOptions;
     property CompilerOptions: TCompilerOptions read FCompilerOptions;
     property FpmakeCompilerOptions: TCompilerOptions read FFpmakeCompilerOptions;
+    property FPMakeRepositoryList: TComponentList read FFPMakeRepositoryList;
+    property RepositoryList: TComponentList read FRepositoryList;
+  public
+
   end;
 
 implementation
@@ -44,24 +65,58 @@ begin
   FOptions := TFppkgOptions.Create;
   FCompilerOptions := TCompilerOptions.Create;
   FFpmakeCompilerOptions := TCompilerOptions.Create;
+  FRepositoryList := TComponentList.Create(False);
+  FFPMakeRepositoryList := TComponentList.Create(False);
 end;
 
 destructor TpkgFPpkg.Destroy;
 begin
+  FFPMakeRepositoryList.Free;
+  FRepositoryList.Free;
   FCompilerOptions.Free;
   FFpmakeCompilerOptions.Free;
   FOptions.Free;
   inherited Destroy;
 end;
 
+procedure TpkgFPpkg.ScanPackagesOnDisk(ACompilerOptions: TCompilerOptions;
+  ARepositoryList: TComponentList);
+var
+  i: Integer;
+  InstPackages: TFPInstalledPackagesStructure;
+  Path: string;
+  RepoOption: TFppkgRepositoryOptionSection;
+  Repo: TFPRepository;
+begin
+  FOptions.BindToCompilerOptions(ACompilerOptions);
+  for i := 0 to FOptions.SectionList.Count -1 do
+    begin
+      if FOptions.SectionList[i] is TFppkgRepositoryOptionSection then
+        begin
+          RepoOption := TFppkgRepositoryOptionSection(FOptions.SectionList[i]);
+          Path := RepoOption.Path;
+          if Path <> '' then
+            begin
+              Repo := TFPRepository.Create(Self);
+              ARepositoryList.Add(Repo);
+              Repo.RepositoryType := fprtInstalled;
+              Repo.RepositoryName := RepoOption.RepositoryName;
+              Repo.Description := RepoOption.Description;
+              InstPackages := TFPInstalledPackagesStructure.Create(Self, Path, ACompilerOptions);
+              InstPackages.AddPackagesToRepository(Repo);
+              InstPackages.Prefix:=RepoOption.Prefix;
+              Repo.DefaultPackagesStructure := InstPackages;
+            end;
+        end;
+    end;
+end;
+
 procedure TpkgFPpkg.InitializeGlobalOptions(CfgFile: string);
 var
-  i : integer;
-  GeneratedConfig,
-  UseGlobalConfig : boolean;
+  GeneratedConfig: boolean;
+  FirstRepoConf: TFppkgOptionSection;
 begin
   GeneratedConfig:=false;
-  UseGlobalConfig:=false;
   // First try specified config file
   if (CfgFile<>'') then
     begin
@@ -76,9 +131,7 @@ begin
         begin
           // If not, try to find a global configuration file
           cfgfile:=GetAppConfigFile(True,False);
-          if FileExists(cfgfile) then
-            UseGlobalConfig := true
-          else
+          if not FileExists(cfgfile) then
             begin
               // Create a new configuration file
               if not IsSuperUser then // Make a local, not global, configuration file
@@ -95,6 +148,14 @@ begin
       FOptions.LoadFromFile(cfgfile);
     end;
   FOptions.CommandLineSection.CompilerConfig:=FOptions.GlobalSection.CompilerConfig;
+  if FOptions.GlobalSection.InstallRepository <> '' then
+    FOptions.CommandLineSection.InstallRepository:=FOptions.GlobalSection.InstallRepository
+  else
+    begin
+      FirstRepoConf :=  FOptions.GetSectionByName('Repository');
+      if Assigned(FirstRepoConf) then
+        FOptions.CommandLineSection.InstallRepository := (FirstRepoConf as TFppkgRepositoryOptionSection).RepositoryName;
+    end;
   // Tracing of what we've done above, need to be done after the verbosity is set
   if GeneratedConfig then
     pkgglobals.Log(llDebug,SLogGeneratingGlobalConfig,[cfgfile])
@@ -109,7 +170,7 @@ var
   S : String;
 begin
   // Load default compiler config
-  S:=FOptions.GlobalSection.CompilerConfigDir+FOptions.GlobalSection.CompilerConfig;
+  S:=FOptions.GlobalSection.CompilerConfigDir+FOptions.CommandLineSection.CompilerConfig;
   FCompilerOptions.UpdateLocalRepositoryOption(FOptions);
   if FileExists(S) then
     begin
@@ -148,5 +209,132 @@ begin
   FFPMakeCompilerOptions.LogValues(llDebug,'fpmake-building');
 end;
 
+procedure TpkgFPpkg.ScanAvailablePackages;
+var
+  Repo: TFPRepository;
+  InstPackages: TFPCustomPackagesStructure;
+begin
+  if (FOptions.GlobalSection.RemoteMirrorsURL<>'') or
+    ((FOptions.GlobalSection.RemoteRepository<>'') and (FOptions.GlobalSection.RemoteRepository<>'auto')) then
+    begin
+      // In case of a re-scan (for example after an update), remove the old list
+      Repo := FindRepository('Available');
+      if Assigned(Repo) then
+        begin
+          RepositoryList.Remove(Repo);
+          Repo.Free;
+        end;
+
+      Repo := TFPRepository.Create(Self);
+      FRepositoryList.Add(Repo);
+      Repo.RepositoryName := 'Available';
+      Repo.Description := 'Packages available for download';
+      Repo.RepositoryType := fprtAvailable;
+      InstPackages := TFPRemotePackagesStructure.Create(Self, FOptions);
+      InstPackages.AddPackagesToRepository(Repo);
+      Repo.DefaultPackagesStructure := InstPackages;
+    end;
+end;
+
+procedure TpkgFPpkg.ScanPackages;
+begin
+  ScanPackagesOnDisk(FFpmakeCompilerOptions, FPMakeRepositoryList);
+  ScanPackagesOnDisk(FCompilerOptions, RepositoryList);
+  ScanAvailablePackages;
+end;
+
+function TpkgFPpkg.FPMakeRepoFindPackage(APackageName: string;
+  APackageKind: TpkgPackageKind): TFPPackage;
+begin
+  Result := FindPackage(FPMakeRepositoryList, APackageName, APackageKind);
+end;
+
+function TpkgFPpkg.FindPackage(APackageName: string;
+  APackageKind: TpkgPackageKind): TFPPackage;
+begin
+  Result := FindPackage(RepositoryList, APackageName, APackageKind);
+end;
+
+function TpkgFPpkg.FindPackage(ARepositoryList: TComponentList; APackageName: string; APackageKind: TpkgPackageKind): TFPPackage;
+var
+  i: Integer;
+  Repo: TFPRepository;
+begin
+  Result := nil;
+  for i := ARepositoryList.Count-1 downto 0 do
+    begin
+      Repo := ARepositoryList.Items[i] as TFPRepository;
+      if ((APackageKind=pkgpkInstalled) and (Repo.RepositoryType = fprtInstalled)) or
+        ((APackageKind=pkgpkAvailable) and (Repo.RepositoryType = fprtAvailable)) or
+        (APackageKind=pkgpkBoth) then
+        begin
+          Result := repo.FindPackage(APackageName);
+          if Assigned(Result) then
+            Break;
+        end;
+    end;
+end;
+
+function TpkgFPpkg.PackageByName(APackageName: string; APackageKind: TpkgPackageKind): TFPPackage;
+var
+  ErrStr: string;
+begin
+  Result := FindPackage(APackageName, APackageKind);
+  If Result=Nil then
+    begin
+      case APackageKind of
+        pkgpkInstalled : ErrStr:=SErrMissingInstallPackage;
+        pkgpkAvailable : ErrStr:=SErrMissingAvailablePackage;
+        pkgpkBoth      : ErrStr:=SErrMissingPackage;
+      end;
+    Raise EPackage.CreateFmt(ErrStr,[APackageName]);
+    end;
+end;
+
+function TpkgFPpkg.FindRepository(ARepositoryName: string): TFPRepository;
+var
+  i: Integer;
+  Repo: TFPRepository;
+begin
+  Result := nil;
+  for i := FRepositoryList.Count-1 downto 0 do
+    begin
+      Repo := FRepositoryList.Items[i] as TFPRepository;
+      if Repo.RepositoryName = ARepositoryName then
+        begin
+          Result := Repo;
+          Break;
+        end;
+    end;
+end;
+
+function TpkgFPpkg.RepositoryByName(ARepositoryName: string): TFPRepository;
+begin
+  Result := FindRepository(ARepositoryName);
+  If Result=Nil then
+    Raise EPackage.CreateFmt(SErrMissingInstallRepo,[ARepositoryName]);
+end;
+
+procedure TpkgFPpkg.ScanInstalledPackagesForAvailablePackages;
+var
+  i: Integer;
+  Repo, AvailableRepo: TFPRepository;
+  AvailStruc: TFPOriginalSourcePackagesStructure;
+begin
+  for i := FRepositoryList.Count-1 downto 0 do
+    begin
+      Repo := FRepositoryList.Items[i] as TFPRepository;
+
+      AvailableRepo := TFPRepository.Create(Self);
+      FRepositoryList.Add(AvailableRepo);
+      AvailableRepo.RepositoryType := fprtAvailable;
+      AvailableRepo.RepositoryName := Repo.RepositoryName + '_source';
+      AvailableRepo.Description := Repo.Description + ' (original sources)';
+      AvailStruc := TFPOriginalSourcePackagesStructure.Create(Self, Repo);
+      AvailStruc.AddPackagesToRepository(AvailableRepo);
+      AvailableRepo.DefaultPackagesStructure := AvailStruc;
+    end;
+end;
+
 end.
 

+ 2 - 2
packages/fppkg/src/pkghandler.pp

@@ -139,8 +139,8 @@ begin
     end
   else if (APackage.Name=CmdLinePackageName) or (APackage.Name=URLPackageName) then
     Result:=GFPpkg.Options.GlobalSection.BuildDir+ChangeFileExt(ExtractFileName(APackage.LocalFileName),'')
-  else if (APackage.RecompileBroken) and (APackage.SourcePath<>'') then
-    Result:=APackage.SourcePath
+  else if Assigned(APackage.PackagesStructure) and (APackage.PackagesStructure.GetBuildPathDirectory(APackage)<>'') then
+    Result:=APackage.PackagesStructure.GetBuildPathDirectory(APackage)
   else
     Result:=GFPpkg.Options.GlobalSection.BuildDir+APackage.Name;
 end;

+ 6 - 1
packages/fppkg/src/pkgmessages.pp

@@ -18,10 +18,14 @@ Resourcestring
   SErrMissingMakefilefpc     = 'Missing configuration Makefile.fpc';
   SErrMissingDirectory       = 'Missing directory "%s"';
   SErrMissingCompilerConfig  = 'Could not find compiler configuration "%s"';
-  SErrMissingInstallPackage  = 'Could not find package "%s"';
+  SErrMissingInstallPackage  = 'Package "%s" is not installed';
+  SErrMissingAvailablePackage= 'Package "%s" is not available';
+  SErrMissingPackage         = 'Could not fin package "%s"';
+  SErrMissingInstallRepo     = 'Could not find repository "%s"';
   SErrNoPackageSpecified     = 'No package specified';
   SErrNoPackageAvailable     = 'Package %s %s is not available';
   SErrOnlyLocalDir           = 'The specified command "%s" works only on current dir, not on a (remote) package';
+  SErrIllConfRepository      = 'Invalid configured repository "%s"';
   SErrExecutionFPMake        = 'Execution of FPMake %s failed';
   SErrException              = 'The FPC Package tool encountered the following error:';
   SErrActionAlreadyRegistered= 'Action "%s" is already registered';
@@ -106,6 +110,7 @@ Resourcestring
   SLogRepositoryName              = '  Name:             %s';
   SLogRepositoryDescription       = '  Description:      "%s"';
   SLogRepositoryPath              = '  Dir:              "%s" -> "%s"';
+  SLogRepositoryPrefix            = '  Prefix:           "%s" -> "%s"';
 
   SLogPackageInfoName             = 'Package:        %s';
   SLogPackageInfoVersion          = 'Version:        %s';

+ 55 - 35
packages/fppkg/src/pkgoptions.pp

@@ -58,6 +58,7 @@ Type
     FCompilerConfigDir: string;
     FConfigVersion: integer;
     FCompilerConfig: string;
+    FInstallRepository: string;
     FDownloader: string;
     FFPMakeCompilerConfig: string;
     FLocalRepository: string;
@@ -97,6 +98,7 @@ Type
     property Downloader: string read FDownloader write SetDownloader;
     property CompilerConfig: string read FCompilerConfig write SetCompilerConfig;
     property FPMakeCompilerConfig: string read FFPMakeCompilerConfig write SetFPMakeCompilerConfig;
+    property InstallRepository: string read FInstallRepository write FInstallRepository;
     property RemoteRepository: string read FRemoteRepository write SetRemoteRepository;
     property RemoteMirrorsURL: string read FRemoteMirrorsURL write SetRemoteMirrorsURL;
     Property CustomFPMakeOptions: string read FCustomFPMakeOptions Write SetCustomFPMakeOptions;
@@ -112,9 +114,12 @@ Type
   private
     FDescription: string;
     FPath: string;
+    FPrefix: string;
     FRepositoryName: string;
     function GetPath: string;
+    function GetPrefix: string;
     procedure SetDescription(AValue: string);
+    procedure SetPrefix(AValue: string);
     procedure SetRepositoryName(AValue: string);
     procedure SetPath(AValue: string);
   public
@@ -125,6 +130,7 @@ Type
     property RepositoryName: string read FRepositoryName write SetRepositoryName;
     property Description: string read FDescription write SetDescription;
     property Path: string read GetPath write SetPath;
+    property Prefix: string read GetPrefix write SetPrefix;
   end;
 
   { TFppkgCommandLineOptionSection }
@@ -133,7 +139,7 @@ Type
   private
     FAllowBroken: Boolean;
     FCompilerConfig: string;
-    FInstallGlobal: Boolean;
+    FInstallRepository: string;
     FRecoveryMode: Boolean;
     FShowLocation: Boolean;
     FSkipConfigurationFiles: Boolean;
@@ -141,9 +147,9 @@ Type
   public
     constructor Create(AnOptionParser: TTemplateParser); override;
     property RecoveryMode: Boolean read FRecoveryMode write FRecoveryMode;
-    property InstallGlobal: Boolean read FInstallGlobal write FInstallGlobal;
     property ShowLocation: Boolean read FShowLocation write FShowLocation;
     property CompilerConfig : string read FCompilerConfig write FCompilerConfig;
+    property InstallRepository: string read FInstallRepository write FInstallRepository;
     property SkipConfigurationFiles: Boolean read FSkipConfigurationFiles write FSkipConfigurationFiles;
     property AllowBroken : Boolean read FAllowBroken write FAllowBroken;
     property SkipFixBrokenAfterInstall: Boolean read FSkipFixBrokenAfterInstall write FSkipFixBrokenAfterInstall;
@@ -170,6 +176,7 @@ Type
     procedure LogValues(ALogLevel: TLogLevel);
 
     procedure BindToCompilerOptions(ACompilerOptions: TCompilerOptions);
+    procedure AddRepositoriesForCompilerSettings(ACompilerOptions: TCompilerOptions);
 
     property SectionList: TFppkgOptionSectionList read GetSectionList;
     property GlobalSection: TFppkgGLobalOptionSection read GetGlobalSection;
@@ -207,8 +214,6 @@ Type
     procedure LogValues(ALogLevel: TLogLevel; const ACfgName:string);
     procedure UpdateLocalRepositoryOption(FppkgOptions: TFppkgOptions);
     procedure CheckCompilerValues;
-    Function LocalUnitDir:string;
-    Function GlobalUnitDir:string;
     Function HasOptions: boolean;
     // Is set when the inifile has an old version number (which is also the case when a new file is generated)
     Property SaveInifileChanges : Boolean Read FSaveInifileChanges;
@@ -217,8 +222,8 @@ Type
     Property Compiler : String Index 1 Read GetOptString Write SetOptString;
     Property CompilerTarget : String Index 2 Read GetOptString Write SetOptString;
     Property CompilerVersion : String Index 3 Read GetOptString Write SetOptString;
-    Property GlobalInstallDir : String Index 4 Read GetOptString Write SetOptString;
-    Property LocalInstallDir : String Index 5 Read GetOptString Write SetOptString;
+    Property GlobalInstallDir : String Index 4 Read GetOptString Write SetOptString; deprecated;
+    Property LocalInstallDir : String Index 5 Read GetOptString Write SetOptString; deprecated;
     Property GlobalPrefix : String Index 6 Read GetOptString Write SetOptString;
     Property LocalPrefix : String Index 7 Read GetOptString Write SetOptString;
     Property Options : TStrings read GetOptions;
@@ -259,10 +264,12 @@ Const
   KeyFPMakeCompilerConfig  = 'FPMakeCompilerConfig';
   KeyDownloader            = 'Downloader';
   KeyCustomFPMakeOptions   = 'FPMakeOptions';
+  KeyInstallRepository     = 'InstallRepository';
 
   KeyRepositoryName        = 'Name';
   KeyRepositoryDescription = 'Description';
   KeyRepositoryPath        = 'Path';
+  KeyRepositoryPrefix      = 'Prefix';
 
   // Compiler dependent config
   KeyGlobalPrefix          = 'GlobalPrefix';
@@ -282,11 +289,22 @@ begin
   FDescription := AValue;
 end;
 
+procedure TFppkgRepositoryOptionSection.SetPrefix(AValue: string);
+begin
+  if FPrefix = AValue then Exit;
+  FPrefix := AValue;
+end;
+
 function TFppkgRepositoryOptionSection.GetPath: string;
 begin
   Result := OptionParser.ParseString(FPath);
 end;
 
+function TFppkgRepositoryOptionSection.GetPrefix: string;
+begin
+  Result := OptionParser.ParseString(FPrefix);
+end;
+
 procedure TFppkgRepositoryOptionSection.SetRepositoryName(AValue: string);
 begin
   if FRepositoryName = AValue then Exit;
@@ -307,6 +325,8 @@ begin
     Description := AValue
   else if SameText(AKey,KeyRepositoryPath) then
     Path := AValue
+  else if SameText(AKey,KeyRepositoryPrefix) then
+    Prefix := AValue
 end;
 
 procedure TFppkgRepositoryOptionSection.LogValues(ALogLevel: TLogLevel);
@@ -315,6 +335,7 @@ begin
   log(ALogLevel,SLogRepositoryName,[FRepositoryName]);
   log(ALogLevel,SLogRepositoryDescription,[FDescription]);
   log(ALogLevel,SLogRepositoryPath,[FPath,Path]);
+  log(ALogLevel,SLogRepositoryPrefix,[FPrefix,Prefix]);
 end;
 
 function TFppkgRepositoryOptionSection.AllowDuplicate: Boolean;
@@ -328,7 +349,6 @@ constructor TFppkgCommandLineOptionSection.Create(AnOptionParser: TTemplateParse
 begin
   inherited Create(AnOptionParser);
   // Parameter defaults
-  FInstallGlobal:=False;
   FRecoveryMode:=False;
   FAllowBroken:=False;
 end;
@@ -524,6 +544,8 @@ begin
     LocalRepository := AValue
   else if SameText(AKey,KeyArchivesDir) then
     ArchivesDir := AValue
+  else if SameText(AKey,KeyInstallRepository) then
+    InstallRepository := AValue
   else if SameText(AKey,KeyCustomFPMakeOptions) then
     CustomFPMakeOptions := AValue
 end;
@@ -668,9 +690,6 @@ procedure TFppkgOptions.SaveToFile(const AFileName: string);
 var
   IniFile: TStringList;
   CurrentSection: TFppkgOptionSection;
-  s: String;
-  i: Integer;
-  j: SizeInt;
 begin
   IniFile:=TStringList.Create;
   try
@@ -714,6 +733,32 @@ begin
   FOptionParser.Values['CompilerVersion'] := ACompilerOptions.CompilerVersion;
 end;
 
+procedure TFppkgOptions.AddRepositoriesForCompilerSettings(
+  ACompilerOptions: TCompilerOptions);
+var
+  CurrentSection: TFppkgRepositoryOptionSection;
+begin
+  CurrentSection := TFppkgRepositoryOptionSection.Create(FOptionParser);
+  CurrentSection.RepositoryName:='global';
+  CurrentSection.Description:='global';
+  CurrentSection.Path:=ACompilerOptions.GlobalInstallDir;
+  FSectionList.Add(CurrentSection);
+
+  CurrentSection := TFppkgRepositoryOptionSection.Create(FOptionParser);
+  CurrentSection.RepositoryName:='local';
+  CurrentSection.Description:='local';
+  CurrentSection.Path:=ACompilerOptions.LocalInstallDir;
+  FSectionList.Add(CurrentSection);
+
+  if CommandLineSection.InstallRepository='' then
+    begin
+      if IsSuperUser then
+        CommandLineSection.InstallRepository:='global'
+      else
+        CommandLineSection.InstallRepository:='local';
+    end;
+end;
+
 {*****************************************************************************
                            TCompilerOptions
 *****************************************************************************}
@@ -837,31 +882,6 @@ begin
   FCompilerOS:=AValue;
 end;
 
-
-function TCompilerOptions.LocalUnitDir:string;
-var ALocalInstallDir: string;
-begin
-  ALocalInstallDir:=LocalInstallDir;
-
-  if ALocalInstallDir<>'' then
-    result:=ALocalInstallDir+'units'+PathDelim+CompilerTarget+PathDelim
-  else
-    result:='';
-end;
-
-
-function TCompilerOptions.GlobalUnitDir:string;
-var AGlobalInstallDir: string;
-begin
-  AGlobalInstallDir:=GlobalInstallDir;
-
-  if AGlobalInstallDir<>'' then
-    result:=AGlobalInstallDir+'units'+PathDelim+CompilerTarget+PathDelim
-  else
-    result:='';
-end;
-
-
 function TCompilerOptions.HasOptions: boolean;
 begin
   result := assigned(FOptions);

+ 283 - 0
packages/fppkg/src/pkgpackagesstructure.pp

@@ -0,0 +1,283 @@
+unit pkgPackagesStructure;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes,
+  SysUtils,
+  fprepos,
+  fpxmlrep,
+  pkgoptions;
+
+type
+
+  { TFPRemotePackagesStructure }
+
+  TFPRemotePackagesStructure = class(TFPCustomPackagesStructure)
+  protected
+    FOptions: TFppkgOptions;
+  public
+    constructor Create(AOwner: TComponent; AnOptions: TFppkgOptions);
+
+    function UnzipBeforeUse: Boolean; override;
+    function AddPackagesToRepository(ARepository: TFPRepository): Boolean; override;
+  end;
+
+  { TFPCustomFileSystemPackagesStructure }
+
+  TFPCustomFileSystemPackagesStructure = class(TFPCustomPackagesStructure)
+  protected
+    FPath: string;
+    FCompilerOptions: TCompilerOptions;
+  public
+    constructor Create(AOwner: TComponent; APath: string; ACompilerOptions: TCompilerOptions);
+  end;
+
+  { TFPInstalledPackagesStructure }
+
+  TFPInstalledPackagesStructure = class(TFPCustomFileSystemPackagesStructure)
+  private
+    FPrefix: string;
+  public
+    function AddPackagesToRepository(ARepository: TFPRepository): Boolean; override;
+    function GetUnitDirectory(APackage: TFPPackage): string; override;
+    function GetPrefix: string; override;
+    function GetBaseInstallDir: string; override;
+    // The prefix is used on installing packages
+    property Prefix: string read FPrefix write FPrefix;
+  end;
+
+  { TFPCurrentDirectoryPackagesStructure }
+
+  TFPCurrentDirectoryPackagesStructure = class(TFPCustomFileSystemPackagesStructure)
+  public
+    function AddPackagesToRepository(ARepository: TFPRepository): Boolean; override;
+  end;
+
+  { TFPOriginalSourcePackagesStructure }
+
+  TFPOriginalSourcePackagesStructure = class(TFPCustomPackagesStructure)
+  private
+    FOriginalRepository: TFPRepository;
+  public
+    constructor Create(AOwner: TComponent; OriginalRepository: TFPRepository);
+    function AddPackagesToRepository(ARepository: TFPRepository): Boolean; override;
+    function GetBuildPathDirectory(APackage: TFPPackage): string; override;
+  end;
+
+implementation
+
+uses
+  fpmkunit,
+  pkgmessages,
+  pkgrepos,
+  pkgglobals;
+
+{ TFPOriginalSourcePackagesStructure }
+
+constructor TFPOriginalSourcePackagesStructure.Create(AOwner: TComponent;
+  OriginalRepository: TFPRepository);
+begin
+  inherited Create(Owner);
+  FOriginalRepository := OriginalRepository;
+end;
+
+function TFPOriginalSourcePackagesStructure.AddPackagesToRepository(
+  ARepository: TFPRepository): Boolean;
+var
+  i: Integer;
+  OrgPackage: TFPPackage;
+  P: TFPPackage;
+begin
+  Result := True;
+  for i := 0 to FOriginalRepository.PackageCount -1 do
+    begin
+      OrgPackage := FOriginalRepository.Packages[i];
+      if (OrgPackage.SourcePath<>'') and DirectoryExists(OrgPackage.SourcePath) then
+        begin
+          P:=ARepository.AddPackage(OrgPackage.Name);
+          P.PackagesStructure:=Self;
+          P.Assign(OrgPackage);
+        end;
+    end;
+end;
+
+function TFPOriginalSourcePackagesStructure.GetBuildPathDirectory(
+  APackage: TFPPackage): string;
+begin
+  Result:=APackage.SourcePath;
+end;
+
+{ TFPCurrentDirectoryPackagesStructure }
+
+function TFPCurrentDirectoryPackagesStructure.AddPackagesToRepository(
+  ARepository: TFPRepository): Boolean;
+begin
+  Result := True;
+  ARepository.AddPackage(CurrentDirPackageName);
+end;
+
+{ TFPRemotePackagesStructure }
+
+constructor TFPRemotePackagesStructure.Create(AOwner: TComponent; AnOptions: TFppkgOptions);
+begin
+  inherited Create(AOwner);
+  FOptions := AnOptions;
+end;
+
+function TFPRemotePackagesStructure.UnzipBeforeUse: Boolean;
+begin
+  Result := True;
+end;
+
+function TFPRemotePackagesStructure.AddPackagesToRepository(ARepository: TFPRepository): Boolean;
+var
+  S : String;
+  X : TFPXMLRepositoryHandler;
+  i: Integer;
+begin
+  Result := True;
+  // Repository
+  S:=FOptions.GlobalSection.LocalPackagesFile;
+  log(llDebug,SLogLoadingPackagesFile,[S]);
+  if not FileExists(S) then
+    exit;
+  try
+    X:=TFPXMLRepositoryHandler.Create;
+    With X do
+      try
+        LoadFromXml(ARepository,S);
+      finally
+        Free;
+      end;
+    for i := 0 to ARepository.PackageCount -1 do
+      ARepository.Packages[i].PackagesStructure := Self;
+  except
+    on E : Exception do
+      begin
+        Log(llError,E.Message);
+        Error(SErrCorruptPackagesFile,[S]);
+      end;
+  end;
+end;
+
+{ TFPInstalledPackagesStructure }
+
+function TFPInstalledPackagesStructure.AddPackagesToRepository(ARepository: TFPRepository): Boolean;
+
+  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;
+
+var
+  SR : TSearchRec;
+  P  : TFPPackage;
+  UF,UD : String;
+  FpmkDir : String;
+  UnitDir: String;
+begin
+  Result:=false;
+  FpmkDir:=IncludeTrailingPathDelimiter(FPath)+'fpmkinst'+PathDelim+FCompilerOptions.CompilerTarget+PathDelim;
+  if FindFirst(IncludeTrailingPathDelimiter(FpmkDir)+PathDelim+'*'+FpmkExt,faDirectory,SR)=0 then
+    begin
+      log(llDebug,SLogFindInstalledPackages,[FpmkDir]);
+      repeat
+        if ((SR.Attr and faDirectory)=0) then
+          begin
+            // Try new .fpm-file
+            UF:=FpmkDir+SR.Name;
+            P:=ARepository.AddPackage(ChangeFileExt(SR.Name,''));
+            P.LoadUnitConfigFromFile(UF);
+            P.PackagesStructure:=Self;
+            if P.IsFPMakeAddIn then
+              AddFPMakeAddIn(P);
+          end;
+      until FindNext(SR)<>0;
+    end;
+  FindClose(SR);
+
+  // Search for non-fpmkunit packages
+  UnitDir:=IncludeTrailingPathDelimiter(FPath)+'units'+PathDelim+FCompilerOptions.CompilerTarget+PathDelim;
+  if FindFirst(IncludeTrailingPathDelimiter(UnitDir)+AllFiles,faDirectory,SR)=0 then
+    begin
+      log(llDebug,SLogFindInstalledPackages,[UnitDir]);
+      repeat
+        if ((SR.Attr and faDirectory)=faDirectory) and (SR.Name<>'.') and (SR.Name<>'..') then
+          begin
+            UD:=IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(UnitDir)+SR.Name);
+            // Try new fpunits.cfg
+            UF:=UD+UnitConfigFileName;
+            if FileExists(UF) then
+              begin
+                if not Assigned(ARepository.FindPackage(SR.Name)) then
+                  begin
+                    P:=ARepository.AddPackage(SR.Name);
+                    P.PackagesStructure:=Self;
+                    P.LoadUnitConfigFromFile(UF);
+                    if P.IsFPMakeAddIn then
+                      AddFPMakeAddIn(P);
+                  end;
+              end
+            else
+              begin
+                // Try Old style Package.fpc
+                UF:=UD+'Package.fpc';
+                if FileExists(UF) then
+                  begin
+                    if not Assigned(ARepository.FindPackage(SR.Name)) then
+                      begin
+                        P:=ARepository.AddPackage(SR.Name);
+                        P.PackagesStructure:=Self;
+                        LoadPackagefpcFromFile(P,UF);
+                      end;
+                  end;
+              end;
+          end;
+      until FindNext(SR)<>0;
+    end;
+  FindClose(SR);
+
+  Result:=true;
+end;
+
+function TFPInstalledPackagesStructure.GetUnitDirectory(APackage: TFPPackage): string;
+begin
+  Result:=IncludeTrailingPathDelimiter(FPath)+'units'+PathDelim+FCompilerOptions.CompilerTarget+PathDelim+APackage.Name+PathDelim;
+end;
+
+function TFPInstalledPackagesStructure.GetPrefix: string;
+begin
+  Result:=IncludeTrailingPathDelimiter(FPrefix);
+end;
+
+function TFPInstalledPackagesStructure.GetBaseInstallDir: string;
+begin
+  Result:=FPath;
+end;
+
+{ TFPCustomFileSystemPackagesStructure }
+
+constructor TFPCustomFileSystemPackagesStructure.Create(AOwner: TComponent; APath: string;
+  ACompilerOptions: TCompilerOptions);
+begin
+  Inherited Create(AOwner);
+  FPath := APath;
+  FCompilerOptions := ACompilerOptions;
+end;
+
+
+end.
+

+ 69 - 396
packages/fppkg/src/pkgrepos.pp

@@ -13,31 +13,21 @@ uses
 function GetRemoteRepositoryURL(const AFileName:string):string;
 
 procedure LoadLocalAvailableMirrors;
-procedure LoadLocalAvailableRepository;
 function LoadManifestFromFile(const AManifestFN:string):TFPPackage;
 procedure FindInstalledPackages(ACompilerOptions:TCompilerOptions;showdups:boolean=true);
 Procedure AddFPMakeAddIn(APackage: TFPPackage);
 function  PackageIsBroken(APackage:TFPPackage; MarkForReInstall: boolean):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 InitializeFppkg;
 
-procedure ListRemoteRepository;
-procedure RebuildRemoteRepository;
-procedure SaveRemoteRepository;
 procedure ClearRemoteRepository;
 
 procedure SetDefaultRepositoryClass(ARepositoryClass: TFPRepositoryClass);
 
 var
   AvailableMirrors    : TFPMirrors;
-  AvailableRepository,
-  InstalledRepository : TFPRepository;
   GFPpkg: TpkgFppkg;
 
 
@@ -47,7 +37,8 @@ uses
   zipper,
   fpxmlrep,
   pkgglobals,
-  pkgmessages;
+  pkgmessages,
+  pkgPackagesStructure;
 
 resourcestring
   SErrRepositoryClassAlreadyAssigned = 'Default repository class is already assigned.';
@@ -173,21 +164,16 @@ function LoadManifestFromFile(const AManifestFN:string):TFPPackage;
 var
   X : TFPXMLRepositoryHandler;
   NewPackages : TFPPackages;
-  NewP,P : TFPPackage;
+  NewP : TFPPackage;
 begin
   result:=nil;
   NewPackages:=TFPPackages.Create(TFPPackage);
   X:=TFPXMLRepositoryHandler.Create;
   try
     X.LoadFromXml(NewPackages,AManifestFN);
-    // Update or Add packages to repository
     if NewPackages.Count=1 then
       begin
         NewP:=NewPackages[0];
-        // Prevent duplicate names
-{        P:=InstalledRepository.FindPackage(NewP.Name);
-        if not assigned(P) then
-          P:=InstalledRepository.AddPackage(NewP.Name); }
         result:=TFPPackage.Create(nil);
         // Copy contents
         result.Assign(NewP);
@@ -202,118 +188,8 @@ 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(llDebug,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(llDebug,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.cfg
-              UF:=UD+UnitConfigFileName;
-              if FileExistsLog(UF) then
-                begin
-                  P:=AddInstalledPackage(SR.Name,UF,Local);
-                  P.LoadUnitConfigFromFile(UF);
-                  if P.IsFPMakeAddIn then
-                    AddFPMakeAddIn(P);
-                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;
-    FindClose(SR);
-  end;
-
-  function CheckInstallDir(AInstallDir:string; const Local: boolean):boolean;
-  var
-    SR : TSearchRec;
-    P  : TFPPackage;
-    UF : String;
-  begin
-    Result:=false;
-    AInstallDir:=IncludeTrailingPathDelimiter(AInstallDir)+'fpmkinst'+PathDelim+ACompilerOptions.CompilerTarget+PathDelim;
-    if FindFirst(IncludeTrailingPathDelimiter(AInstallDir)+PathDelim+'*'+FpmkExt,faDirectory,SR)=0 then
-      begin
-        log(llDebug,SLogFindInstalledPackages,[AInstallDir]);
-        repeat
-          if ((SR.Attr and faDirectory)=0) then
-            begin
-              // Try new .fpm-file
-              UF:=AInstallDir+SR.Name;
-              P:=AddInstalledPackage(ChangeFileExt(SR.Name,''),UF,Local);
-              P.LoadUnitConfigFromFile(UF);
-              if P.IsFPMakeAddIn then
-                AddFPMakeAddIn(P);
-            end;
-        until FindNext(SR)<>0;
-      end;
-    FindClose(SR);
-  end;
-
 begin
-  if assigned(InstalledRepository) then
-    InstalledRepository.Free;
-  InstalledRepository:=GetDefaultRepositoryClass.Create(nil);
-  // First scan the global directory
-  // The local directory will overwrite the versions
-  if ACompilerOptions.GlobalInstallDir<>'' then
-    begin
-      CheckUnitDir(ACompilerOptions.GlobalUnitDir, False);
-      CheckInstallDir(ACompilerOptions.GlobalInstallDir, False);
-    end;
-  if ACompilerOptions.LocalInstallDir<>'' then
-    begin
-    CheckUnitDir(ACompilerOptions.LocalUnitDir, True);
-    CheckInstallDir(ACompilerOptions.LocalInstallDir, True);
-    end;
+  GFPpkg.ScanPackages;
 end;
 
 
@@ -342,7 +218,7 @@ begin
       if (GFPpkg.CompilerOptions.CompilerOS in D.OSes) and
          (GFPpkg.CompilerOptions.CompilerCPU in D.CPUs) then
         begin
-          DepPackage:=InstalledRepository.FindPackage(D.PackageName);
+          DepPackage:=GFPpkg.FindPackage(D.PackageName, pkgpkInstalled);
           // Don't stop on missing dependencies
           if assigned(DepPackage) then
             begin
@@ -352,26 +228,7 @@ begin
                   result:=true;
                   if MarkForReInstall then
                     begin
-                      // When the package is re-installed, use the same fpmake-options and sourcepath
-                      // as used during the initial installation. (The AvailableRepository is used to install
-                      // the package so make sure all properties are set there)
-                      AvailP:=AvailableRepository.FindPackage(APackage.Name);
-                      if not assigned(AvailP) then
-                        begin
-                          AvailP := AvailableRepository.AddPackage(APackage.Name);
-                          AvailP.Assign(APackage);
-                        end
-                      else
-                        begin
-                          AvailP.SourcePath := APackage.SourcePath;
-                          AvailP.FPMakeOptionsString := APackage.FPMakeOptionsString;
-                        end;
-                      AvailP.RecompileBroken:=true;
                       APackage.RecompileBroken:=true;
-                      // If the fpmake.pp of the original installation is not available anymore, do not
-                      // try to use it.
-                      if (AvailP.SourcePath<>'') and not FileExists(IncludeTrailingPathDelimiter(APackage.SourcePath)+'fpmake.pp') then
-                        AvailP.SourcePath:='';
                     end;
                   exit;
                 end;
@@ -385,17 +242,23 @@ end;
 
 function FindBrokenPackages(SL:TStrings):Boolean;
 var
-  i : integer;
+  i,j : integer;
   P : TFPPackage;
+  Repo: TFPRepository;
 begin
   SL.Clear;
-  for i:=0 to InstalledRepository.PackageCount-1 do
+  for i:= 0 to GFPpkg.RepositoryList.Count-1 do
     begin
-      P:=InstalledRepository.Packages[i];
-      if PackageIsBroken(P,True) then
-        begin
-          SL.Add(P.Name);
-        end;
+      Repo := GFPpkg.RepositoryList.Items[i] as TFPRepository;
+      if Repo.RepositoryType=fprtInstalled then
+        for j:=0 to Repo.PackageCount-1 do
+          begin
+            P:=Repo.Packages[j];
+            if PackageIsBroken(P,True) then
+              begin
+                SL.Add(P.Name);
+              end;
+          end;
     end;
   Result:=(SL.Count>0);
 end;
@@ -418,10 +281,10 @@ begin
   // Check for fpmkunit dependencies
   for i:=0 to high(FPMKUnitDeps) do
     begin
-      P:=InstalledRepository.FindPackage(FPMKUnitDeps[i].package);
+      P:=GFPpkg.FindPackage(FPMKUnitDeps[i].package, pkgpkInstalled);
       if P<>nil then
         begin
-          AvailP:=AvailableRepository.FindPackage(FPMKUnitDeps[i].package);
+          AvailP:=GFPpkg.FindPackage(FPMKUnitDeps[i].package, pkgpkAvailable);
           if AvailP<>nil then
             AvailVerStr:=AvailP.Version.AsString
           else
@@ -448,132 +311,75 @@ end;
                            Local Available Repository
 *****************************************************************************}
 
-procedure LoadLocalAvailableRepository;
-var
-  S : String;
-  X : TFPXMLRepositoryHandler;
-begin
-  if assigned(AvailableRepository) then
-    AvailableRepository.Free;
-  AvailableRepository:=GetDefaultRepositoryClass.Create(Nil);
-  // Repository
-  S:=GFPpkg.Options.GlobalSection.LocalPackagesFile;
-  log(llDebug,SLogLoadingPackagesFile,[S]);
-  if not FileExists(S) then
-    exit;
-  try
-    X:=TFPXMLRepositoryHandler.Create;
-    With X do
-      try
-        LoadFromXml(AvailableRepository,S);
-      finally
-        Free;
-      end;
-  except
-    on E : Exception do
+procedure ListPackages(const ShowGlobalAndLocal: boolean);
+
+  procedure AddPackageToLine(APackage: TFPPackage; var Line: string);
+  var
+    PackageVersion: string;
+  begin
+    if Assigned(APackage) then
       begin
-        Log(llError,E.Message);
-        Error(SErrCorruptPackagesFile,[S]);
-      end;
+        PackageVersion := APackage.Version.AsString;
+        if PackageIsBroken(APackage, False) then
+          PackageVersion := PackageVersion + ' (B)';
+      end
+    else
+      PackageVersion := '-';
+    Line :=  Line + Format(' %-14s', [PackageVersion])
   end;
-end;
-
-
-function PackageAvailableVersionStr(const AName:String):string;
-var
-  P : TFPPackage;
-begin
-  P:=AvailableRepository.FindPackage(AName);
-  if P<>nil then
-    result:=P.Version.AsString
-  else
-    result:='-';
-end;
-
-
-function PackageInstalledVersionStr(const AName:String;const ShowUsed: boolean = false;const Local: boolean = false):string;
-var
-  P : TFPPackage;
-begin
-  P:=InstalledRepository.FindPackage(AName);
-  if P<>nil then
-    begin
-      if not ShowUsed then
-        result:=P.Version.AsString
-      else if Local=p.InstalledLocally then
-        result:=P.Version.AsString
-      else if not P.UnusedVersion.Empty then
-        result:=P.UnusedVersion.AsString
-      else
-        result:='-';
-    end
-  else
-    result:='-';
-end;
-
 
-function PackageInstalledStateStr(const AName:String):string;
 var
-  P : TFPPackage;
-begin
-  result := '';
-  P:=InstalledRepository.FindPackage(AName);
-  if (P<>nil) and PackageIsBroken(P,false) then
-    result:='B';
-end;
-
-
-procedure ListAvailablePackages;
-var
-  InstalledP,
-  AvailP : TFPPackage;
-  i : integer;
+  i,j : integer;
   SL : TStringList;
+  PackageName : string;
+  Repo: TFPRepository;
+  Package: TFPPackage;
+  Header: string;
+  Line: string;
 begin
   SL:=TStringList.Create;
   SL.Sorted:=true;
-  for i:=0 to AvailableRepository.PackageCount-1 do
+  SL.Duplicates:=dupIgnore;
+
+  Header := Format('%-20s', ['Name']);
+  for i:=0 to GFPpkg.RepositoryList.Count-1 do
     begin
-      AvailP:=AvailableRepository.Packages[i];
-      InstalledP:=InstalledRepository.FindPackage(AvailP.Name);
-      if not assigned(InstalledP) or
-         (AvailP.Version.CompareVersion(InstalledP.Version)>0) then
-        SL.Add(Format('%-20s %-12s %-12s',[AvailP.Name,PackageInstalledVersionStr(AvailP.Name),AvailP.Version.AsString]));
+      Repo := TFPRepository(GFPpkg.RepositoryList[i]);
+      Header := Header + Format(' %-14s', [Repo.RepositoryName]);
+      for j:=0 to Repo.PackageCount-1 do
+        begin
+          SL.Add(Repo.Packages[j].Name);
+        end;
     end;
-  Writeln(Format('%-20s %-12s %-12s',['Name','Installed','Available']));
-  for i:=0 to SL.Count-1 do
-    Writeln(SL[i]);
-  FreeAndNil(SL);
-end;
-
-
-procedure ListPackages(const ShowGlobalAndLocal: boolean);
-var
-  i : integer;
-  SL : TStringList;
-  PackageName : String;
-begin
-  SL:=TStringList.Create;
-  SL.Sorted:=true;
-  SL.Duplicates:=dupIgnore;
-  for i:=0 to AvailableRepository.PackageCount-1 do
-    SL.Add(AvailableRepository.Packages[i].Name);
-  for i:=0 to InstalledRepository.PackageCount-1 do
-    SL.Add(InstalledRepository.Packages[i].Name);
   if ShowGlobalAndLocal then
-    Writeln(Format('%-20s %-14s %-14s %-3s %-12s',['Name','Installed (G)','Installed (L)','','Available']))
+    WriteLn(Header)
   else
-    Writeln(Format('%-20s %-12s %-3s %-12s',['Name','Installed','','Available']));
+    Writeln(Format('%-20s %-14s %-14s',['Name','Installed','Available']));
+
   for i:=0 to SL.Count-1 do
     begin
       PackageName:=SL[i];
       if (PackageName<>CmdLinePackageName) and (PackageName<>CurrentDirPackageName) then
         begin
+          Line:=Format('%-20s', [PackageName]);
           if ShowGlobalAndLocal then
-            Writeln(Format('%-20s %-14s %-14s %-3s %-12s',[PackageName,PackageInstalledVersionStr(PackageName,True,False),PackageInstalledVersionStr(PackageName,True,True),PackageInstalledStateStr(PackageName),PackageAvailableVersionStr(PackageName)]))
+            begin
+              for j:=0 to GFPpkg.RepositoryList.Count-1 do
+                begin
+                  Repo := TFPRepository(GFPpkg.RepositoryList[j]);
+                  Package := Repo.FindPackage(PackageName);
+                  AddPackageToLine(Package, Line);
+                end
+            end
           else
-            Writeln(Format('%-20s %-12s %-3s %-12s',[PackageName,PackageInstalledVersionStr(PackageName),PackageInstalledStateStr(PackageName),PackageAvailableVersionStr(PackageName)]));
+            begin
+              Package := GFPpkg.FindPackage(PackageName, pkgpkInstalled);
+              AddPackageToLine(Package, Line);
+              Package := GFPpkg.FindPackage(PackageName, pkgpkAvailable);
+              AddPackageToLine(Package, Line);
+            end;
         end;
+      WriteLn(Line);
     end;
   FreeAndNil(SL);
 end;
@@ -586,135 +392,6 @@ begin
   GFPpkg := TpkgFPpkg.Create(nil);
 end;
 
-
-{*****************************************************************************
-                           Remote Repository
-*****************************************************************************}
-
-
-procedure ListRemoteRepository;
-var
-  P : TFPPackage;
-  i : integer;
-  SL : TStringList;
-begin
-  SL:=TStringList.Create;
-  SL.Sorted:=true;
-  for i:=0 to InstalledRepository.PackageCount-1 do
-    begin
-      P:=InstalledRepository.Packages[i];
-      SL.Add(Format('%-20s %-12s %-20s',[P.Name,P.Version.AsString,P.FileName]));
-    end;
-  Writeln(Format('%-20s %-12s %-20s',['Name','Available','FileName']));
-  for i:=0 to SL.Count-1 do
-    Writeln(SL[i]);
-  FreeAndNil(SL);
-end;
-
-
-procedure RebuildRemoteRepository;
-
-  procedure LoadPackageManifest(const AManifestFN:string);
-  var
-    X : TFPXMLRepositoryHandler;
-    i : integer;
-    DoAdd : Boolean;
-    P,NewP : TFPPackage;
-    NewPackages : TFPPackages;
-  begin
-    NewPackages:=TFPPackages.Create(TFPPackage);
-    X:=TFPXMLRepositoryHandler.Create;
-    try
-      X.LoadFromXml(NewPackages,AManifestFN);
-      // Update or Add packages to repository
-      for i:=0 to NewPackages.Count-1 do
-        begin
-          NewP:=NewPackages[i];
-          DoAdd:=True;
-          P:=InstalledRepository.FindPackage(NewP.Name);
-          if assigned(P) then
-            begin
-              if NewP.Version.CompareVersion(P.Version)<0 then
-                begin
-                  Writeln(Format('Ignoring package %s-%s (old %s)',[NewP.Name,NewP.Version.AsString,P.Version.AsString]));
-                  DoAdd:=False;
-                end
-              else
-                Writeln(Format('Updating package %s-%s (old %s)',[NewP.Name,NewP.Version.AsString,P.Version.AsString]));
-            end
-          else
-            P:=InstalledRepository.PackageCollection.AddPackage(NewP.Name);
-          // Copy contents
-          if DoAdd then
-            P.Assign(NewP);
-        end;
-    finally
-      X.Free;
-      NewPackages.Free;
-    end;
-  end;
-
-var
-  i : integer;
-  ArchiveSL : TStringList;
-  ManifestSL : TStringList;
-begin
-  if assigned(InstalledRepository) then
-    InstalledRepository.Free;
-  InstalledRepository:=GetDefaultRepositoryClass.Create(Nil);
-  try
-    ManifestSL:=TStringList.Create;
-    ManifestSL.Add(ManifestFileName);
-    { Find all archives }
-    ArchiveSL:=TStringList.Create;
-    SearchFiles(ArchiveSL,'*.zip');
-    if ArchiveSL.Count=0 then
-      Error('No archive files found');
-    { Process all archives }
-    for i:=0 to ArchiveSL.Count-1 do
-      begin
-        Writeln('Processing ',ArchiveSL[i]);
-        { Unzip manifest.xml }
-        With TUnZipper.Create do
-          try
-            log(llCommands,SLogUnzippping,[ArchiveSL[i]]);
-            OutputPath:='.';
-            UnZipFiles(ArchiveSL[i],ManifestSL);
-          Finally
-            Free;
-          end;
-        { Load manifest.xml }
-        if FileExists(ManifestFileName) then
-          begin
-            LoadPackageManifest(ManifestFileName);
-            DeleteFile(ManifestFileName);
-          end
-        else
-          Writeln('No manifest found in archive ',ArchiveSL[i]);
-
-      end;
-  finally
-    ArchiveSL.Free;
-    ManifestSL.Free;
-  end;
-end;
-
-
-procedure SaveRemoteRepository;
-var
-  X : TFPXMLRepositoryHandler;
-begin
-  // Repository
-  Writeln('Saving repository in packages.xml');
-  X:=TFPXMLRepositoryHandler.Create;
-  With X do
-    try
-      SaveToXml(InstalledRepository,'packages.xml');
-    finally
-      Free;
-    end;
-end;
-
 procedure ClearRemoteRepository;
 begin
   CurrentRemoteRepositoryURL := '';
@@ -722,12 +399,8 @@ end;
 
 initialization
   GFPpkg := nil;
-  AvailableRepository := nil;
-  InstalledRepository := nil;
   AvailableMirrors := nil;
 finalization
-  AvailableRepository.Free;
-  InstalledRepository.Free;
   AvailableMirrors.Free;
   GFPpkg.Free;
 end.

+ 33 - 19
utils/fppkg/fppkg.pp

@@ -18,6 +18,7 @@ uses
   // Package Handler components
   pkghandler,pkgmkconv, pkgdownload,
   pkgfpmake, pkgcommands,
+  pkgPackagesStructure,
   fpmkunit
   // Downloaders
 {$if (defined(unix) and not defined(android)) or defined(windows)}
@@ -94,15 +95,15 @@ begin
   Writeln('  -h --help          This help');
   Writeln('  -v --verbose       Show more information');
   Writeln('  -d --debug         Show debugging information');
-  Writeln('  -g --global        Force installation to global (system-wide) directory');
   Writeln('  -f --force         Force installation also if the package is already installed');
   Writeln('  -r --recovery      Recovery mode, use always internal fpmkunit');
   Writeln('  -b --broken        Do not stop on broken packages');
-  Writeln('  -l --showlocation  Show if the packages are installed globally or locally');
+  Writeln('  -l --showlocation  Show in which repository the the packages are installed');
   Writeln('  -o --options=value Pass extra options to the compiler');
   Writeln('  -n                 Do not read the default configuration files');
   Writeln('  -p --prefix=value  Specify the prefix');
   Writeln('  -s --skipbroken    Skip the rebuild of depending packages after installation');
+  Writeln('  -i --installlocation Specify the repository to install packages into');
   Writeln('  --compiler=value   Specify the compiler-executable');
   Writeln('  --cpu=value        Specify the target cpu to compile for');
   Writeln('  --os=value         Specify the target operating system to compile for');
@@ -215,8 +216,8 @@ begin
         LogLevels:=AllLogLevels
       else if CheckOption(I,'d','debug') then
         LogLevels:=AllLogLevels+[llDebug]
-      else if CheckOption(I,'g','global') then
-        GFPpkg.Options.CommandLineSection.InstallGlobal:=true
+      else if CheckOption(I,'i','installrepository') then
+        GFPpkg.Options.CommandLineSection.InstallRepository:=OptionArg(I)
       else if CheckOption(I,'r','recovery') then
         GFPpkg.Options.CommandLineSection.RecoveryMode:=true
       else if CheckOption(I,'n','') then
@@ -281,10 +282,11 @@ end;
 
 procedure TMakeTool.DoRun;
 var
-  ActionPackage : TFPPackage;
   OldCurrDir : String;
   i      : Integer;
   SL     : TStringList;
+  Repo: TFPRepository;
+  InstPackages: TFPCurrentDirectoryPackagesStructure;
 begin
   OldCurrDir:=GetCurrentDir;
   Try
@@ -296,18 +298,17 @@ begin
     for i := 0 to FPMKUnitDepDefaultCount-1 do
       FPMKUnitDeps[i]:=FPMKUnitDepsDefaults[i];
 
-    // Scan is special, it doesn't need a valid local setup
-    if (ParaAction='scan') then
-      begin
-        RebuildRemoteRepository;
-        ListRemoteRepository;
-        SaveRemoteRepository;
-        halt(0);
-      end;
-
     MaybeCreateLocalDirs;
     if not GFPpkg.Options.CommandLineSection.SkipConfigurationFiles then
-      GFPpkg.InitializeCompilerOptions
+      begin
+        GFPpkg.InitializeCompilerOptions;
+        if GFPpkg.Options.GlobalSection.ConfigVersion = 4 then
+          begin
+            // This version did not have any repository configured, but used a
+            // 'local' and 'global' compiler-setting.
+            GFPpkg.Options.AddRepositoriesForCompilerSettings(GFPpkg.CompilerOptions);
+          end;
+      end
     else
       begin
         GFPpkg.FPMakeCompilerOptions.InitCompilerDefaults;
@@ -337,7 +338,6 @@ begin
             pkgglobals.Log(llWarning,E.Message);
         end;
       end;
-    LoadLocalAvailableRepository;
     FindInstalledPackages(GFPpkg.FPMakeCompilerOptions,true);
     CheckFPMakeDependencies;
     // We only need to reload the status when we use a different
@@ -364,9 +364,25 @@ begin
         FreeAndNil(SL);
       end;
 
+    if (ParaAction='install') or (ParaAction='uninstall') or
+      (ParaAction='fixbroken') then
+      GFPpkg.ScanInstalledPackagesForAvailablePackages;
+
     if ParaPackages.Count=0 then
       begin
-        ActionPackage:=AvailableRepository.AddPackage(CurrentDirPackageName);
+        // Do not add the fake-repository with the contents of the current directory
+        // when a list of packages is shown. (The fake repository should not be shown)
+        if ParaAction<>'list' then
+          begin
+            Repo := TFPRepository.Create(GFPpkg);
+            GFPpkg.RepositoryList.Add(Repo);
+            Repo.RepositoryType := fprtAvailable;
+            Repo.RepositoryName := 'CurrentDirectory';
+            Repo.Description := 'Package in current directory';
+            InstPackages := TFPCurrentDirectoryPackagesStructure.Create(GFPpkg, '', GFPpkg.CompilerOptions);
+            InstPackages.AddPackagesToRepository(Repo);
+            Repo.DefaultPackagesStructure := InstPackages;
+          end;
         pkghandler.ExecuteAction(CurrentDirPackageName,ParaAction);
       end
     else
@@ -376,8 +392,6 @@ begin
           begin
             if sametext(ExtractFileExt(ParaPackages[i]),'.zip') and FileExists(ParaPackages[i]) then
               begin
-                ActionPackage:=AvailableRepository.AddPackage(CmdLinePackageName);
-                ActionPackage.LocalFileName:=ExpandFileName(ParaPackages[i]);
                 pkghandler.ExecuteAction(CmdLinePackageName,ParaAction);
               end
             else