Browse Source

* detect broken packages, give an error if broken packages are found and the user
needs to run 'fppkg fixbroken' first
* add --broken option to skip the broken package detection at startup
* call fixbroken implicitly after an install command from the commandline has been processed.
This will make sure that all packages depending on the installed package will also be recompiled
and reinstalled.
Warning: Installing a new rtl will recompile everything. Currently this will
give a broken installation because not all packages compile yet.

git-svn-id: trunk@10595 -

peter 17 years ago
parent
commit
a4c852d729

+ 17 - 0
utils/fppkg/fppkg.pp

@@ -245,6 +245,8 @@ begin
         GlobalOptions.InstallGlobal:=true
         GlobalOptions.InstallGlobal:=true
       else if CheckOption(I,'r','recovery') then
       else if CheckOption(I,'r','recovery') then
         GlobalOptions.RecoveryMode:=true
         GlobalOptions.RecoveryMode:=true
+      else if CheckOption(I,'b','broken') then
+        GlobalOptions.AllowBroken:=true
       else if CheckOption(I,'h','help') then
       else if CheckOption(I,'h','help') then
         begin
         begin
           ShowUsage;
           ShowUsage;
@@ -275,6 +277,7 @@ var
   OldCurrDir : String;
   OldCurrDir : String;
   Res    : Boolean;
   Res    : Boolean;
   i      : Integer;
   i      : Integer;
+  SL     : TStringList;
 begin
 begin
   OldCurrDir:=GetCurrentDir;
   OldCurrDir:=GetCurrentDir;
   Try
   Try
@@ -304,6 +307,16 @@ begin
     if GlobalOptions.CompilerConfig<>GlobalOptions.FPMakeCompilerConfig then
     if GlobalOptions.CompilerConfig<>GlobalOptions.FPMakeCompilerConfig then
       FindInstalledPackages(CompilerOptions,true);
       FindInstalledPackages(CompilerOptions,true);
 
 
+    // Check for broken dependencies
+    if not GlobalOptions.AllowBroken and
+       not((ParaPackages.Count=0) and (ParaAction='fixbroken')) then
+      begin
+        SL:=TStringList.Create;
+        if FindBrokenPackages(SL) then
+          Error(SErrBrokenPackagesFound);
+        FreeAndNil(SL);
+      end;
+
     if ParaPackages.Count=0 then
     if ParaPackages.Count=0 then
       begin
       begin
         Log(vlDebug,SLogCommandLineAction,['[<currentdir>]',ParaAction]);
         Log(vlDebug,SLogCommandLineAction,['[<currentdir>]',ParaAction]);
@@ -333,6 +346,10 @@ begin
           end;
           end;
       end;
       end;
 
 
+    // Recompile all packages dependent on this package
+    if res and (ParaAction='install') then
+      pkghandler.ExecuteAction(nil,'fixbroken');
+
     Terminate;
     Terminate;
 
 
   except
   except

+ 51 - 3
utils/fppkg/pkgcommands.pp

@@ -110,6 +110,13 @@ type
     Function Execute(const Args:TActionArgs):boolean;override;
     Function Execute(const Args:TActionArgs):boolean;override;
   end;
   end;
 
 
+  { TCommandFixBroken }
+
+  TCommandFixBroken = Class(TPackagehandler)
+  Public
+    Function Execute(const Args:TActionArgs):boolean;override;
+  end;
+
 
 
 function TCommandAddConfig.Execute(const Args:TActionArgs):boolean;
 function TCommandAddConfig.Execute(const Args:TActionArgs):boolean;
 begin
 begin
@@ -249,13 +256,22 @@ end;
 
 
 
 
 function TCommandInstall.Execute(const Args:TActionArgs):boolean;
 function TCommandInstall.Execute(const Args:TActionArgs):boolean;
+var
+  S : String;
 begin
 begin
   if assigned(CurrentPackage) then
   if assigned(CurrentPackage) then
     ExecuteAction(CurrentPackage,'build',Args);
     ExecuteAction(CurrentPackage,'build',Args);
   ExecuteAction(CurrentPackage,'fpmakeinstall',Args);
   ExecuteAction(CurrentPackage,'fpmakeinstall',Args);
-  // Update local status file
+  // Update version information from generated fpunits.conf
   if assigned(CurrentPackage) then
   if assigned(CurrentPackage) then
-    CurrentPackage.InstalledVersion.Assign(CurrentPackage.Version);
+    begin
+      if GlobalOptions.InstallGlobal then
+        S:=CompilerOptions.GlobalUnitDir
+      else
+        S:=CompilerOptions.LocalUnitDir;
+      S:=IncludeTrailingPathDelimiter(S)+CurrentPackage.Name+PathDelim+UnitConfigFileName;
+      LoadUnitConfigFromFile(CurrentPackage,S);
+    end;
   Result:=true;
   Result:=true;
 end;
 end;
 
 
@@ -321,7 +337,15 @@ begin
                 end;
                 end;
             end
             end
           else
           else
-            status:='OK';
+            begin
+              if PackageIsBroken(DepPackage) then
+                begin
+                  status:='Broken, recompiling';
+                  L.Add(DepPackage.Name);
+                end
+              else
+                status:='OK';
+            end;
           Log(vlInfo,SLogPackageDependency,
           Log(vlInfo,SLogPackageDependency,
               [D.PackageName,D.MinVersion.AsString,DepPackage.InstalledVersion.AsString,DepPackage.Version.AsString,status]);
               [D.PackageName,D.MinVersion.AsString,DepPackage.InstalledVersion.AsString,DepPackage.Version.AsString,status]);
         end
         end
@@ -342,6 +366,29 @@ begin
 end;
 end;
 
 
 
 
+function TCommandFixBroken.Execute(const Args:TActionArgs):boolean;
+var
+  i : integer;
+  P : TFPPackage;
+  SL : TStringList;
+begin
+  SL:=TStringList.Create;
+  repeat
+    FindBrokenPackages(SL);
+    if SL.Count=0 then
+      break;
+    for i:=0 to SL.Count-1 do
+      begin
+        P:=CurrentRepository.PackageByName(SL[i]);
+        ExecuteAction(P,'build');
+        ExecuteAction(P,'install');
+      end;
+  until false;
+  FreeAndNil(SL);
+  Result:=true;
+end;
+
+
 initialization
 initialization
   RegisterPkgHandler('update',TCommandUpdate);
   RegisterPkgHandler('update',TCommandUpdate);
   RegisterPkgHandler('showall',TCommandShowAll);
   RegisterPkgHandler('showall',TCommandShowAll);
@@ -355,4 +402,5 @@ initialization
   RegisterPkgHandler('clean',TCommandClean);
   RegisterPkgHandler('clean',TCommandClean);
   RegisterPkgHandler('archive',TCommandArchive);
   RegisterPkgHandler('archive',TCommandArchive);
   RegisterPkgHandler('installdependencies',TCommandInstallDependencies);
   RegisterPkgHandler('installdependencies',TCommandInstallDependencies);
+  RegisterPkgHandler('fixbroken',TCommandFixBroken);
 end.
 end.

+ 6 - 1
utils/fppkg/pkgfpmake.pp

@@ -222,7 +222,12 @@ begin
         CreateFPMKUnitSource(TempBuildDir+PathDelim+'fpmkunit.pp');
         CreateFPMKUnitSource(TempBuildDir+PathDelim+'fpmkunit.pp');
       // Call compiler
       // Call compiler
       If ExecuteProcess(FPMakeCompilerOptions.Compiler,OOptions+' '+FPmakeSrc)<>0 then
       If ExecuteProcess(FPMakeCompilerOptions.Compiler,OOptions+' '+FPmakeSrc)<>0 then
-        Error(SErrFailedToCompileFPCMake);
+        begin
+          if not GlobalOptions.RecoveryMode then
+            Error(SErrCompileFailureFPMakeTryRecovery)
+          else
+            Error(SErrCompileFailureFPMake);
+        end;
       // Cleanup units
       // Cleanup units
       DeleteDir(TempBuildDir);
       DeleteDir(TempBuildDir);
     end
     end

+ 6 - 1
utils/fppkg/pkgmessages.pp

@@ -26,7 +26,8 @@ Resourcestring
   SErrException              = 'The FPC Package tool encountered the following error:';
   SErrException              = 'The FPC Package tool encountered the following error:';
   SErrActionAlreadyRegistered= 'Action "%s" is already registered';
   SErrActionAlreadyRegistered= 'Action "%s" is already registered';
   SErrActionNotFound         = 'Action "%s" is not supported';
   SErrActionNotFound         = 'Action "%s" is not supported';
-  SErrFailedToCompileFPCMake = 'Could not compile fpmake driver program';
+  SErrCompileFailureFPMake   = 'Could not compile fpmake driver program';
+  SErrCompileFailureFPMakeTryRecovery = 'Could not compile fpmake driver program, try adding "--recovery"';
   SErrNoFTPDownload          = 'This binary has no support for FTP downloads.';
   SErrNoFTPDownload          = 'This binary has no support for FTP downloads.';
   SErrNoHTTPDownload         = 'This binary has no support for HTTP downloads.';
   SErrNoHTTPDownload         = 'This binary has no support for HTTP downloads.';
   SErrBackupFailed           = 'Backup of file "%s" to file "%s" failed.';
   SErrBackupFailed           = 'Backup of file "%s" to file "%s" failed.';
@@ -47,6 +48,7 @@ Resourcestring
   SErrLoginFailed            = 'FTP LOGIN command failed.';
   SErrLoginFailed            = 'FTP LOGIN command failed.';
   SErrCWDFailed              = 'FTP CWD "%s" command failed.';
   SErrCWDFailed              = 'FTP CWD "%s" command failed.';
   SErrGETFailed              = 'FTP GET "%s" command failed.';
   SErrGETFailed              = 'FTP GET "%s" command failed.';
+  SErrBrokenPackagesFound    = 'Found broken packages, run "fppkg fixbroken" first';
 
 
   SLogGeneratingFPMake       = 'Generating fpmake.pp';
   SLogGeneratingFPMake       = 'Generating fpmake.pp';
   SLogNotCompilingFPMake     = 'Skipping compiling of fpmake.pp, fpmake executable already exists';
   SLogNotCompilingFPMake     = 'Skipping compiling of fpmake.pp, fpmake executable already exists';
@@ -73,6 +75,7 @@ Resourcestring
   SLogSelectedMirror         = 'Selected mirror "%s"';
   SLogSelectedMirror         = 'Selected mirror "%s"';
   SLogUpgradingConfig        = 'Configuration file "%s" is updated with new configuration settings';
   SLogUpgradingConfig        = 'Configuration file "%s" is updated with new configuration settings';
   SLogPackageDependency      = 'Dependency on package %s %s, installed %s, available %s  (%s)';
   SLogPackageDependency      = 'Dependency on package %s %s, installed %s, available %s  (%s)';
+  SLogPackageChecksumChanged = 'Package %s needs to be rebuild, dependency %s is modified';
 
 
   SDbgFound                  = 'Found';
   SDbgFound                  = 'Found';
   SDbgNotFound               = 'Not Found';
   SDbgNotFound               = 'Not Found';
@@ -81,6 +84,8 @@ Resourcestring
   SDbgBackupFile             = 'Creating Backup File "%s"';
   SDbgBackupFile             = 'Creating Backup File "%s"';
   SDbgPackageMultipleLocations = 'Multiple installations found for package %s, using installation "%s"';
   SDbgPackageMultipleLocations = 'Multiple installations found for package %s, using installation "%s"';
   SDbgPackageDependencyOtherTarget  = 'Dependency on package %s is not for %s';
   SDbgPackageDependencyOtherTarget  = 'Dependency on package %s is not for %s';
+  SDbgObsoleteDependency     = 'Obsolete dependency found on package %s';
+
 
 
 implementation
 implementation
 
 

+ 3 - 0
utils/fppkg/pkgoptions.pp

@@ -45,6 +45,7 @@ Type
     FFPMakeCompilerConfig : String;
     FFPMakeCompilerConfig : String;
     // Parameter options
     // Parameter options
     FCompilerConfig : String;
     FCompilerConfig : String;
+    FAllowBroken,
     FInstallGlobal,
     FInstallGlobal,
     FRecoveryMode   : Boolean;
     FRecoveryMode   : Boolean;
     function  GetOptString(Index: integer): String;
     function  GetOptString(Index: integer): String;
@@ -74,6 +75,7 @@ Type
     Property CompilerConfig : String Read FCompilerConfig Write FCompilerConfig;
     Property CompilerConfig : String Read FCompilerConfig Write FCompilerConfig;
     Property InstallGlobal : Boolean Read FInstallGlobal Write FInstallGlobal;
     Property InstallGlobal : Boolean Read FInstallGlobal Write FInstallGlobal;
     Property RecoveryMode : Boolean Read FRecoveryMode Write FRecoveryMode;
     Property RecoveryMode : Boolean Read FRecoveryMode Write FRecoveryMode;
+    Property AllowBroken : Boolean Read FAllowBroken Write FAllowBroken;
   end;
   end;
 
 
 
 
@@ -261,6 +263,7 @@ begin
   FCompilerConfig:=FDefaultCompilerConfig;
   FCompilerConfig:=FDefaultCompilerConfig;
   FInstallGlobal:=False;
   FInstallGlobal:=False;
   FRecoveryMode:=False;
   FRecoveryMode:=False;
+  FAllowBroken:=False;
 end;
 end;
 
 
 
 

+ 134 - 66
utils/fppkg/pkgrepos.pp

@@ -13,8 +13,11 @@ function GetRemoteRepositoryURL(const AFileName:string):string;
 procedure LoadLocalMirrors;
 procedure LoadLocalMirrors;
 procedure LoadLocalRepository;
 procedure LoadLocalRepository;
 function  LoadOrCreatePackage(const AName:string):TFPPackage;
 function  LoadOrCreatePackage(const AName:string):TFPPackage;
+procedure LoadUnitConfigFromFile(APackage:TFPPackage;const AFileName: String);
 function  LoadPackageManifest(const AManifestFN:string):TFPPackage;
 function  LoadPackageManifest(const AManifestFN:string):TFPPackage;
 procedure FindInstalledPackages(ACompilerOptions:TCompilerOptions;showdups:boolean=true);
 procedure FindInstalledPackages(ACompilerOptions:TCompilerOptions;showdups:boolean=true);
+function  PackageIsBroken(APackage:TFPPackage):boolean;
+function  FindBrokenPackages(SL:TStrings):Boolean;
 procedure CheckFPMakeDependencies;
 procedure CheckFPMakeDependencies;
 procedure ListLocalRepository(all:boolean=false);
 procedure ListLocalRepository(all:boolean=false);
 
 
@@ -244,66 +247,71 @@ begin
 end;
 end;
 
 
 
 
+procedure LoadUnitConfigFromFile(APackage:TFPPackage;const AFileName: String);
+Var
+  L,DepSL : TStrings;
+  DepName,
+  V : String;
+  DepChecksum : Cardinal;
+  i,j,k : integer;
+  D : TFPDependency;
+begin
+  L:=TStringList.Create;
+  Try
+    ReadIniFile(AFileName,L);
+{$warning TODO Maybe check also CPU-OS}
+    // Read fpunits.conf
+    V:=L.Values['version'];
+    APackage.InstalledVersion.AsString:=V;
+    V:=L.Values['checksum'];
+    if V<>'' then
+      APackage.InstalledChecksum:=StrToInt(V)
+    else
+      APackage.InstalledChecksum:=$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);
 procedure FindInstalledPackages(ACompilerOptions:TCompilerOptions;showdups:boolean=true);
 
 
-  procedure LoadUnitConfigFromFile(APackage:TFPPackage;const AFileName: String);
-  Var
-    L,DepSL : TStrings;
-    DepName,
-    V : String;
-    DepChecksum : Cardinal;
-    i,j,k : integer;
-    D : TFPDependency;
+  procedure LogDuplicatePackages(APackage:TFPPackage;const AFileName: String);
   begin
   begin
-    L:=TStringList.Create;
-    Try
-      ReadIniFile(AFileName,L);
-      // Log packages found in multiple locations (local and global) ?
-      if not APackage.InstalledVersion.Empty then
-        begin
-          if showdups then
-            Log(vlDebug,SDbgPackageMultipleLocations,[APackage.Name,ExtractFilePath(AFileName)]);
-        end;
-{$warning TODO Maybe check also CPU-OS}
-      // Read fpunits.conf
-      V:=L.Values['version'];
-      APackage.InstalledVersion.AsString:=V;
-      V:=L.Values['checksum'];
-      if V<>'' then
-        APackage.InstalledChecksum:=StrToInt(V)
-      else
-        APackage.InstalledChecksum:=$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;
+    // Log packages found in multiple locations (local and global) ?
+    if not APackage.InstalledVersion.Empty then
+      begin
+        if showdups then
+          Log(vlDebug,SDbgPackageMultipleLocations,[APackage.Name,ExtractFilePath(AFileName)]);
+      end;
   end;
   end;
 
 
   procedure LoadPackagefpcFromFile(APackage:TFPPackage;const AFileName: String);
   procedure LoadPackagefpcFromFile(APackage:TFPPackage;const AFileName: String);
@@ -340,6 +348,7 @@ procedure FindInstalledPackages(ACompilerOptions:TCompilerOptions;showdups:boole
               if FileExistsLog(UF) then
               if FileExistsLog(UF) then
                 begin
                 begin
                   P:=LoadOrCreatePackage(SR.Name);
                   P:=LoadOrCreatePackage(SR.Name);
+                  LogDuplicatePackages(P,UF);
                   LoadUnitConfigFromFile(P,UF)
                   LoadUnitConfigFromFile(P,UF)
                 end
                 end
               else
               else
@@ -349,6 +358,7 @@ procedure FindInstalledPackages(ACompilerOptions:TCompilerOptions;showdups:boole
                   if FileExistsLog(UF) then
                   if FileExistsLog(UF) then
                     begin
                     begin
                       P:=LoadOrCreatePackage(SR.Name);
                       P:=LoadOrCreatePackage(SR.Name);
+                      LogDuplicatePackages(P,UF);
                       LoadPackagefpcFromFile(P,UF);
                       LoadPackagefpcFromFile(P,UF);
                     end;
                     end;
                 end;
                 end;
@@ -368,6 +378,57 @@ begin
 end;
 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:=CurrentRepository.FindPackage(D.PackageName);
+          // Don't stop on missing dependencies
+          if assigned(DepPackage) then
+            begin
+              if (DepPackage.InstalledChecksum<>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 CurrentRepository.PackageCount-1 do
+    begin
+      P:=CurrentRepository.Packages[i];
+      // Process only installed packages
+      if not P.InstalledVersion.Empty then
+        begin
+          if PackageIsBroken(P) then
+            SL.Add(P.Name);
+        end;
+    end;
+  Result:=(SL.Count>0);
+end;
+
+
 procedure CheckFPMakeDependencies;
 procedure CheckFPMakeDependencies;
 var
 var
   i : Integer;
   i : Integer;
@@ -405,16 +466,20 @@ procedure ListLocalRepository(all:boolean=false);
 var
 var
   P : TFPPackage;
   P : TFPPackage;
   i : integer;
   i : integer;
+  SL : TStringList;
 begin
 begin
-  Writeln(Format('%-20s %-12s %-12s',['Name','Installed','Available']));
+  SL:=TStringList.Create;
+  SL.Sorted:=true;
   for i:=0 to CurrentRepository.PackageCount-1 do
   for i:=0 to CurrentRepository.PackageCount-1 do
     begin
     begin
       P:=CurrentRepository.Packages[i];
       P:=CurrentRepository.Packages[i];
       if all or (P.Version.CompareVersion(P.InstalledVersion)>0) then
       if all or (P.Version.CompareVersion(P.InstalledVersion)>0) then
-        begin
-          Writeln(Format('%-20s %-12s %-12s',[P.Name,P.InstalledVersion.AsString,P.Version.AsString]));
-        end;
+        SL.Add(Format('%-20s %-12s %-12s',[P.Name,P.InstalledVersion.AsString,P.Version.AsString]));
     end;
     end;
+  Writeln(Format('%-20s %-12s %-12s',['Name','Installed','Available']));
+  for i:=0 to SL.Count-1 do
+    Writeln(SL[i]);
+  FreeAndNil(SL);
 end;
 end;
 
 
 
 
@@ -426,13 +491,19 @@ procedure ListRemoteRepository;
 var
 var
   P : TFPPackage;
   P : TFPPackage;
   i : integer;
   i : integer;
+  SL : TStringList;
 begin
 begin
-  Writeln(Format('%-20s %-12s %-20s',['Name','Available','FileName']));
+  SL:=TStringList.Create;
+  SL.Sorted:=true;
   for i:=0 to CurrentRepository.PackageCount-1 do
   for i:=0 to CurrentRepository.PackageCount-1 do
     begin
     begin
       P:=CurrentRepository.Packages[i];
       P:=CurrentRepository.Packages[i];
-      Writeln(Format('%-20s %-12s %-20s',[P.Name,P.Version.AsString,P.FileName]));
+      SL.Add(Format('%-20s %-12s %-20s',[P.Name,P.Version.AsString,P.FileName]));
     end;
     end;
+  Writeln(Format('%-20s %-12s %-20s',['Name','Available','FileName']));
+  for i:=0 to SL.Count-1 do
+    Writeln(SL[i]);
+  FreeAndNil(SL);
 end;
 end;
 
 
 
 
@@ -499,7 +570,4 @@ begin
     end;
     end;
 end;
 end;
 
 
-
-
-initialization
 end.
 end.