Browse Source

--- Merging r16892 into '.':
U packages/fpmkunit/src/fpmkunit.pp
--- Merging r16893 into '.':
G packages/fpmkunit/src/fpmkunit.pp
--- Merging r16894 into '.':
G packages/fpmkunit/src/fpmkunit.pp
--- Merging r16895 into '.':
G packages/fpmkunit/src/fpmkunit.pp
--- Merging r16897 into '.':
U utils/fppkg/pkgcommands.pp
U utils/fppkg/pkgmessages.pp
U utils/fppkg/pkgglobals.pp
--- Merging r16900 into '.':
G packages/fpmkunit/src/fpmkunit.pp
--- Merging r16910 into '.':
G packages/fpmkunit/src/fpmkunit.pp
--- Merging r16912 into '.':
G utils/fppkg/pkgcommands.pp
G utils/fppkg/pkgmessages.pp
--- Merging r16918 into '.':
G utils/fppkg/pkgcommands.pp
U utils/fppkg/pkgfpmake.pp
U utils/fppkg/fprepos.pp
U utils/fppkg/pkghandler.pp
U utils/fppkg/pkgrepos.pp
G packages/fpmkunit/src/fpmkunit.pp

# revisions: 16461,16462,16463,16464,16467,16469,16484,16591,16892,16893,16894,16895,16897,16900,16910,16912,16918
------------------------------------------------------------------------
r16461 | joost | 2010-11-28 13:26:06 +0100 (Sun, 28 Nov 2010) | 7 lines
Changed paths:
M /trunk/packages/fpmkunit/src/fpmkunit.pp

* Fixed mem-leak
* Added BuildEngine.CmdRenameFile
* Added TPackage.IsFPMakeAddIn, when this flag is set, the package can be
needed when compiling fpmake.pp files.
* Added ability to override TCustomInstaller.Packages
* GetInstallFiles and GetInstallSourceFiles are virtual now

------------------------------------------------------------------------
------------------------------------------------------------------------
r16462 | joost | 2010-11-28 14:01:05 +0100 (Sun, 28 Nov 2010) | 11 lines
Changed paths:
M /trunk/utils/fppkg/fprepos.pp
M /trunk/utils/fppkg/pkgfpmake.pp
M /trunk/utils/fppkg/pkgglobals.pp
M /trunk/utils/fppkg/pkgmessages.pp
M /trunk/utils/fppkg/pkgoptions.pp
M /trunk/utils/fppkg/pkgrepos.pp

* Added TPackage.IsFPMakeAddIn, when this flag is set, the package can be
needed when compiling fpmake.pp files.
* Pass -dCOMPILED_BY_FPPKG to the compiler when compiling fpmake files. This
is used to show better error-messages when a fpmake-addin is missing
* FPMKUnitDeps is not a constant anymore, but a variable, so that extra
dependencies for the fpmake.pp files can be added. (The add-ins)
* Added TFPMKUnitDep.Def, so that it canbe detected is an AddIn is available




------------------------------------------------------------------------
------------------------------------------------------------------------
r16463 | joost | 2010-11-28 18:08:39 +0100 (Sun, 28 Nov 2010) | 1 line
Changed paths:
M /trunk/utils/fppkg/fppkg.pp

* Do not handle packages twice, when they are passed on the command line
------------------------------------------------------------------------
------------------------------------------------------------------------
r16464 | joost | 2010-11-28 18:12:34 +0100 (Sun, 28 Nov 2010) | 1 line
Changed paths:
M /trunk/packages/fpmkunit/src/fpmkunit.pp

* Added ability to override which files should be stored in the archive of a package
------------------------------------------------------------------------
------------------------------------------------------------------------
r16467 | michael | 2010-11-28 20:39:05 +0100 (Sun, 28 Nov 2010) | 1 line
Changed paths:
M /trunk/packages/fpmkunit/src/fpmkunit.pp

* Applied patch from Darius Blaszijk to implement fpdoc building
------------------------------------------------------------------------
------------------------------------------------------------------------
r16469 | michael | 2010-11-28 21:19:02 +0100 (Sun, 28 Nov 2010) | 1 line
Changed paths:
M /trunk/packages/fpmkunit/src/fpmkunit.pp

* Added patch from Darius Blaszijk to be less verbose by default
------------------------------------------------------------------------
------------------------------------------------------------------------
r16484 | joost | 2010-11-30 14:51:01 +0100 (Tue, 30 Nov 2010) | 1 line
Changed paths:
M /trunk/packages/fpmkunit/src/fpmkunit.pp

* Patch from Darius Blaszijk to support version numbers with less then three digits, bug #17833
------------------------------------------------------------------------
------------------------------------------------------------------------
r16591 | marco | 2010-12-19 14:17:27 +0100 (Sun, 19 Dec 2010) | 2 lines
Changed paths:
M /trunk/packages/fpmkunit/src/fpmkunit.pp

* patch from Darius that makes lower verbosity output more FPC language independant using -vq. Mantis #18051

------------------------------------------------------------------------
------------------------------------------------------------------------
r16892 | joost | 2011-02-08 10:32:32 +0100 (Tue, 08 Feb 2011) | 1 line
Changed paths:
M /trunk/packages/fpmkunit/src/fpmkunit.pp

* Check if compilation has failed
------------------------------------------------------------------------
------------------------------------------------------------------------
r16893 | joost | 2011-02-08 10:53:47 +0100 (Tue, 08 Feb 2011) | 2 lines
Changed paths:
M /trunk/packages/fpmkunit/src/fpmkunit.pp

* Show arguments of command in the error message if the command failed
* Make sure that compile-info is shown so that parsing the compiler-output makes sense
------------------------------------------------------------------------
------------------------------------------------------------------------
r16894 | joost | 2011-02-08 12:26:53 +0100 (Tue, 08 Feb 2011) | 2 lines
Changed paths:
M /trunk/packages/fpmkunit/src/fpmkunit.pp

* Show the fpc-console output when compilation failed
* Remove the compiler-error message numbers from console output
------------------------------------------------------------------------
------------------------------------------------------------------------
r16895 | joost | 2011-02-08 17:20:10 +0100 (Tue, 08 Feb 2011) | 1 line
Changed paths:
M /trunk/packages/fpmkunit/src/fpmkunit.pp

* Show messages when the builds starts and when the installation is complete
------------------------------------------------------------------------
------------------------------------------------------------------------
r16897 | joost | 2011-02-08 18:10:58 +0100 (Tue, 08 Feb 2011) | 1 line
Changed paths:
M /trunk/utils/fppkg/pkgcommands.pp
M /trunk/utils/fppkg/pkgglobals.pp
M /trunk/utils/fppkg/pkgmessages.pp

* Show message to inform why depending packages are installed
------------------------------------------------------------------------
------------------------------------------------------------------------
r16900 | joost | 2011-02-09 15:47:57 +0100 (Wed, 09 Feb 2011) | 2 lines
Changed paths:
M /trunk/packages/fpmkunit/src/fpmkunit.pp

* Made Searchfiles public
* Show messages when clean or install has completed
------------------------------------------------------------------------
------------------------------------------------------------------------
r16910 | joost | 2011-02-12 21:09:51 +0100 (Sat, 12 Feb 2011) | 1 line
Changed paths:
M /trunk/packages/fpmkunit/src/fpmkunit.pp

* Fixed parsing fpc-output
------------------------------------------------------------------------
------------------------------------------------------------------------
r16912 | joost | 2011-02-13 16:12:13 +0100 (Sun, 13 Feb 2011) | 1 line
Changed paths:
M /trunk/utils/fppkg/pkgcommands.pp
M /trunk/utils/fppkg/pkgmessages.pp

* Show that dependencies are being installed
------------------------------------------------------------------------
------------------------------------------------------------------------
r16918 | joost | 2011-02-15 16:56:21 +0100 (Tue, 15 Feb 2011) | 7 lines
Changed paths:
M /trunk/packages/fpmkunit/src/fpmkunit.pp
M /trunk/utils/fppkg/fprepos.pp
M /trunk/utils/fppkg/pkgcommands.pp
M /trunk/utils/fppkg/pkgfpmake.pp
M /trunk/utils/fppkg/pkghandler.pp
M /trunk/utils/fppkg/pkgrepos.pp

* Store the location of the original source when installing a package
* Store the used options to install a package in fpunits.conf
* When a package is re-installed because of broken dependencies, use the stored
source-path and options if possible
* When installing a package from the current directory, make sure the right
directory is used. It could be changed while installing dependencies.

------------------------------------------------------------------------

git-svn-id: branches/fixes_2_4@16971 -

marco 14 years ago
parent
commit
436103f89a

+ 128 - 26
packages/fpmkunit/src/fpmkunit.pp

@@ -869,6 +869,7 @@ Type
     FRunMode: TRunMode;
     FListMode : Boolean;
     FLogLevels : TVerboseLevels;
+    FFPMakeOptionsString: string;
   Protected
     Procedure Log(Level : TVerboseLevel; Const Msg : String);
     Procedure CreatePackages; virtual;
@@ -891,6 +892,7 @@ Type
     Destructor destroy; override;
     Function AddPackage(Const AName : String) : TPackage;
     Function Run : Boolean;
+    Property FPMakeOptionsString: string read FFPMakeOptionsString;
     //files in package
     Property Packages : TPackages Read GetPackages;
     Property RunMode : TRunMode Read FRunMode;
@@ -985,6 +987,8 @@ Procedure SplitCommand(Const Cmd : String; Var Exe,Options : String);
 Procedure AddCustomFpmakeCommandlineOption(const ACommandLineOption, HelpMessage : string);
 Function GetCustomFpmakeCommandlineOptionValue(const ACommandLineOption : string) : string;
 
+procedure SearchFiles(const AFileName: string; Recursive: boolean; var List: TStrings);
+
 Implementation
 
 uses typinfo, rtlconsts;
@@ -1011,7 +1015,7 @@ ResourceString
   SErrNoPackagesDefined = 'No action possible: No packages were defined.';
   SErrInstaller         = 'The installer encountered the following error:';
   SErrDepUnknownTarget  = 'Unknown target for unit "%s" in dependencies for %s in package %s';
-  SErrExternalCommandFailed = 'External command "%s" failed with exit code %d';
+  SErrExternalCommandFailed = 'External command "%s" failed with exit code %d. Console output:'+LineEnding+'%s';
   SErrCreatingDirectory = 'Failed to create directory "%s"';
   SErrDeletingFile      = 'Failed to delete file "%s"';
   SErrMovingFile        = 'Failed to move file "%s" to "%s"';
@@ -1038,7 +1042,10 @@ ResourceString
   SWarnDepUnitNotFound     = 'Warning: Dependency on unit %s is not supported for %s';
   SWarnTargetDependsOnPackage = 'Warning: Target %s of package %s depends on another package (%s). These kind of dependencies are not processed';
   SWarnDependOnOtherPlatformPackage = 'Warning: Package %s depends on package %s which is not available for the %s platform';
-  SWarnDone               = 'Done';
+  SWarnStartBuildingPackage = 'Start building package %s';
+  SWarnBuildingPackagecomplete = '[%3.0f%%] Built target %s';
+  SWarnInstallationPackagecomplete = 'Installation package %s succeeded';
+  SWarnCleanPackagecomplete = 'Clean of package %s completed';
 
   SInfoCompilingPackage   = 'Compiling package %s';
   SInfoPackageAlreadyProcessed = 'Package %s is already processed';
@@ -1140,14 +1147,15 @@ Const
   KeyNeedLibC = 'NeedLibC';
   KeyDepends  = 'Depends';
   KeyAddIn    = 'FPMakeAddIn';
+  KeySourcePath = 'SourcePath';
+  KeyFPMakeOptions = 'FPMakeOptions';
 
 {****************************************************************************
                                 Helpers
 ****************************************************************************}
 
-function ExecuteFPC(Verbose: boolean; const Path: string; const ComLine: string): integer;
+function ExecuteFPC(Verbose: boolean; const Path: string; const ComLine: string; ConsoleOutput: TMemoryStream): integer;
 var
-  M: TMemoryStream;
   P: TProcess;
   BytesRead: longint;
 
@@ -1172,20 +1180,20 @@ var
     snum: string;
   begin
     // make sure we have room
-    M.SetSize(BytesRead + READ_BYTES);
+    ConsoleOutput.SetSize(BytesRead + READ_BYTES);
 
     // try reading it
-    n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
+    n := P.Output.Read((ConsoleOutput.Memory + BytesRead)^, READ_BYTES);
     if n > 0 then
     begin
       Inc(BytesRead, n);
 
       sLine := '';
-      BuffPos := M.Position;
+      BuffPos := ConsoleOutput.Position;
 
       //read lines from the stream
       repeat
-        M.Read(ch,1);
+        ConsoleOutput.Read(ch,1);
 
         if ch in [#10, #13] then
         begin
@@ -1203,14 +1211,14 @@ var
             end;
 
           sLine := '';
-          BuffPos := M.Position;
+          BuffPos := ConsoleOutput.Position;
         end
         else
           sLine := sLine + ch;
 
-      until M.Position = M.Size;
+      until ConsoleOutput.Position >= BytesRead;
 
-      M.Position := BuffPos;
+      ConsoleOutput.Position := BuffPos;
     end
     else
     begin
@@ -1222,16 +1230,14 @@ var
   end;
 
 begin
+  result := -1;
+  BytesRead := 0;
+  P := TProcess.Create(nil);
   try
-    M := TMemoryStream.Create;
-    BytesRead := 0;
-
-    P := TProcess.Create(nil);
-
     if Verbose then
       P.CommandLine := Path + ' ' + ComLine
     else
-      P.CommandLine := Path + ' -vq ' + ComLine;
+      P.CommandLine := Path + ' -viq ' + ComLine;
 
     P.Options := [poUsePipes];
 
@@ -1242,12 +1248,86 @@ begin
     // read last part
     repeat
     until ReadFromStream = 0;
+    ConsoleOutput.SetSize(BytesRead);
+
+    result := P.ExitStatus;
   finally
     P.Free;
-    M.Free;
   end;
 end;
 
+function ParsecompilerOutput(M: TMemoryStream; Verbose: boolean): string;
+type
+  TParseCompilerOutputState = (cosBeginOfLine, cosSearchColon, cosParseNumber, cosOther);
+
+var
+  presult: pchar;
+  state: TParseCompilerOutputState;
+  ch: char;
+  eolchar: char;
+begin
+  m.Seek(0, soBeginning);
+  setlength(Result,M.Size);
+
+  if verbose then
+    begin
+      m.Read(Result[1],M.Size);
+      Exit;
+    end;
+
+  presult := @Result[1];
+  eolchar := RightStr(LineEnding,1)[1];
+  m.Seek(0,soBeginning);
+  state := cosBeginOfLine;
+  while m.Position<m.Size do
+    begin
+      ch := char(m.ReadByte);
+      case state of
+        cosBeginOfLine:
+          begin
+            if ch='(' then
+              state := cosParseNumber
+            else if ch=' ' then
+              begin
+                presult^ := ch;
+                inc(presult);
+              end
+            else
+              begin
+                presult^ := ch;
+                inc(presult);
+                state := cosSearchColon;
+              end;
+          end;
+        cosParseNumber:
+          begin
+            if ch=')' then
+              begin
+              state := cosOther;
+              // Omit the space behind the number
+              ch := char(m.ReadByte);
+              assert(ch=' ');
+              end;
+          end;
+        cosOther:
+          begin
+            presult^ := ch;
+            inc(presult);
+            if ch=eolchar then
+              state := cosBeginOfLine;
+          end;
+        cosSearchColon:
+          begin
+            presult^ := ch;
+            inc(presult);
+            if (ch=':') or (ch=eolchar) then
+              state := cosBeginOfLine;
+          end;
+      end;
+    end;
+  setlength(Result,presult-@result[1]);
+end;
+
 Function QuoteXML(S : String) : string;
 
   Procedure W(Var J : Integer; Var R : String; T : String);
@@ -2477,6 +2557,9 @@ begin
         Values[KeyChecksum]:=IntToStr(DateTimeToFileDate(Now));
         Values[KeyCPU]:=CPUToString(ACPU);
         Values[KeyOS]:=OSToString(AOS);
+        //Installer;
+        Values[KeySourcePath]:=IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(Installer.BuildEngine.FStartDir)+Directory);
+        Values[KeyFPMakeOptions]:=trim(Installer.FPMakeOptionsString);
         Deps:='';
         for i:=0 to Dependencies.Count-1 do
           begin
@@ -3054,12 +3137,13 @@ end;
 
 procedure TCustomInstaller.AnalyzeOptions;
 
-  Function CheckOption(Index : Integer;const Short,Long : String): Boolean;
+  Function CheckOption(Index : Integer;const Short,Long : String; AddToOptionString: boolean = true): Boolean;
   var
     O : String;
   begin
     O:=Paramstr(Index);
     Result:=(O='-'+short) or (O='--'+long) or (copy(O,1,Length(Long)+3)=('--'+long+'='));
+    if AddToOptionString and Result then FFPMakeOptionsString := FFPMakeOptionsString+' '+O;
   end;
 
   Function CheckCustomOption(Index : Integer; out CustOptName: string): Boolean;
@@ -3078,6 +3162,7 @@ procedure TCustomInstaller.AnalyzeOptions;
     O:=copy(O,3,i-3);
     CustOptName:=O;
     Result:=CustomFpmakeCommandlineOptions.IndexOfName(O)>-1;
+    if Result then FFPMakeOptionsString := FFPMakeOptionsString+' '+Paramstr(Index);
   end;
 
 
@@ -3144,9 +3229,9 @@ begin
   While (I<ParamCount) do
     begin
     Inc(I);
-    if CheckOption(I,'v','verbose') then
+    if CheckOption(I,'v','verbose',false) then
       FLogLevels:=AllMessages
-    else if CheckOption(I,'d','debug') then
+    else if CheckOption(I,'d','debug',false) then
       FLogLevels:=AllMessages+[vlDebug]
     else if CheckCommand(I,'m','compile') then
       FRunMode:=rmCompile
@@ -3274,7 +3359,6 @@ procedure TCustomInstaller.Compile(Force: Boolean);
 begin
   FBuildEngine.ForceCompile:=Force;
   FBuildEngine.Compile(Packages);
-  Log(vlWarning,SWarnDone);
 end;
 
 
@@ -3404,6 +3488,9 @@ end;
 procedure TBuildEngine.ExecuteCommand(const Cmd,Args : String; IgnoreError : Boolean = False);
 Var
   E : Integer;
+  cmdLine: string;
+  ConsoleOutput: TMemoryStream;
+  s: string;
 begin
   Log(vlInfo,SInfoExecutingCommand,[Cmd,Args]);
   if ListMode then
@@ -3411,9 +3498,21 @@ begin
   else
     begin
       // We should check cmd for spaces, and move all after first space to args.
-      E:=ExecuteFPC(Verbose, cmd, args);
-      If (E<>0) and (not IgnoreError) then
-        Error(SErrExternalCommandFailed,[Cmd,E]);
+      ConsoleOutput := TMemoryStream.Create;
+      try
+        E:=ExecuteFPC(Verbose, cmd, args, ConsoleOutput);
+        If (E<>0) and (not IgnoreError) then
+          begin
+            if trim(Args)<>'' then
+              cmdLine := cmd + ' ' + trim(args)
+            else
+              cmdline := cmd;
+            s := ParsecompilerOutput(ConsoleOutput,Verbose);
+            Error(SErrExternalCommandFailed,[cmdLine,E,s]);
+          end;
+      finally
+        ConsoleOutput.Free;
+      end;
     end;
 end;
 
@@ -4800,11 +4899,12 @@ begin
   For I:=0 to Packages.Count-1 do
     begin
       P:=Packages.PackageItems[i];
+      log(vlWarning,SWarnStartBuildingPackage,[P.Name]);
       If PackageOK(P) then
         MaybeCompile(P);
 
       //show compile progress
-      writeln('[', (I + 1)/Packages.Count * 100:3:0, '%] Built target ', P.Name);
+      log(vlWarning,SWarnBuildingPackagecomplete,[(I + 1)/Packages.Count * 100, P.Name]);
     end;
   If Assigned(AfterCompile) then
     AfterCompile(Self);
@@ -4823,6 +4923,7 @@ begin
       P:=Packages.PackageItems[i];
       If PackageOK(P) then
         Install(P);
+      log(vlWarning, SWarnInstallationPackagecomplete, [P.Name]);
     end;
   If Assigned(AfterInstall) then
     AfterInstall(Self);
@@ -4878,6 +4979,7 @@ begin
     P:=Packages.PackageItems[i];
     If PackageOK(P) then
       Clean(P);
+    log(vlWarning, SWarnCleanPackagecomplete, [P.Name]);
     end;
   If Assigned(AfterClean) then
     AfterClean(Self);

+ 10 - 0
utils/fppkg/fprepos.pp

@@ -113,6 +113,9 @@ type
     FAuthor: String;
     FDescription: String;
     FEmail: String;
+    FFPMakeOptionsString: string;
+    FRecompileBroken: boolean;
+    FSourcePath: string;
     FInstalledLocally: boolean;
     FIsFPMakeAddIn: boolean;
     FLicense: String;
@@ -143,6 +146,7 @@ type
     // 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;
   Published
     Property Name : String Read FName Write SetName;
     Property Author : String Read FAuthor Write FAuthor;
@@ -157,6 +161,9 @@ type
     Property CPUs : TCPUs Read FCPUs Write FCPUs;
     Property Checksum : Cardinal Read FChecksum Write FChecksum;
     Property IsFPMakeAddIn : boolean read FIsFPMakeAddIn write FIsFPMakeAddIn;
+    // These properties are used to re-compile the package, when it's dependencies are changed.
+    Property SourcePath : string read FSourcePath write FSourcePath;
+    Property FPMakeOptionsString : string read FFPMakeOptionsString write FFPMakeOptionsString;
     // Manual package from commandline not in official repository
     Property LocalFileName : String Read FLocalFileName Write FLocalFileName;
   end;
@@ -661,6 +668,9 @@ begin
       Description:=P.Description;
       HomepageURL:=P.HomepageURL;
       DownloadURL:=P.DownloadURL;
+      SourcePath:=P.SourcePath;
+      FPMakeOptionsString:=P.FPMakeOptionsString;
+      InstalledLocally:=P.InstalledLocally;
       OSes:=P.OSes;
       CPUs:=P.CPUs;
       FileName:=P.FileName;

+ 36 - 7
utils/fppkg/pkgcommands.pp

@@ -227,6 +227,8 @@ end;
 
 
 procedure TCommandBuild.Execute;
+var
+  P: TFPPackage;
 begin
   if PackageName<>'' then
     begin
@@ -245,7 +247,12 @@ begin
       else
         begin
           ExecuteAction(PackageName,'installdependencies');
-          ExecuteAction(PackageName,'unzip');
+          // 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');
@@ -275,10 +282,26 @@ begin
       P:=InstalledRepository.FindPackage(S);
       if not assigned(P) then
         P:=InstalledRepository.AddPackage(S);
-      if IsSuperUser or GlobalOptions.InstallGlobal then
-        UFN:=CompilerOptions.GlobalUnitDir
+      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
+            UFN:=CompilerOptions.LocalUnitDir
+          else
+            UFN:=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
-        UFN:=CompilerOptions.LocalUnitDir;
+        begin
+          if (IsSuperUser or GlobalOptions.InstallGlobal) then
+            UFN:=CompilerOptions.GlobalUnitDir
+          else
+            UFN:=CompilerOptions.LocalUnitDir;
+        end;
       UFN:=IncludeTrailingPathDelimiter(UFN)+S+PathDelim+UnitConfigFileName;
       LoadUnitConfigFromFile(P,UFN);
     end
@@ -352,7 +375,7 @@ begin
             end
           else
             begin
-              if PackageIsBroken(InstalledP) then
+              if PackageIsBroken(InstalledP, True) then
                 begin
                   status:='Broken, recompiling';
                   L.Add(D.PackageName);
@@ -371,8 +394,13 @@ begin
   if assigned(MissingDependency) then
     Error(SErrNoPackageAvailable,[MissingDependency.PackageName,MissingDependency.MinVersion.AsString]);
   // Install needed updates
-  for i:=0 to L.Count-1 do
-    ExecuteAction(L[i],'install');
+  if L.Count > 0 then
+    begin
+      pkgglobals.Log(vlProgres,SProgrInstallDependencies);
+      for i:=0 to L.Count-1 do
+        ExecuteAction(L[i],'install');
+      pkgglobals.Log(vlProgres,SProgrDependenciesInstalled);
+    end;
   FreeAndNil(L);
   if FreeManifest then
     FreeAndNil(P);
@@ -389,6 +417,7 @@ begin
     FindBrokenPackages(SL);
     if SL.Count=0 then
       break;
+    pkgglobals.Log(vlProgres,SProgrReinstallDependent);
     for i:=0 to SL.Count-1 do
       begin
         ExecuteAction(SL[i],'build');

+ 28 - 18
utils/fppkg/pkgfpmake.pp

@@ -295,32 +295,42 @@ begin
   { Maybe compile fpmake executable? }
   ExecuteAction(PackageName,'compilefpmake');
   { Create options }
-  AddOption('--nofpccfg');
   if vlDebug in LogLevels then
     AddOption('--debug')
   else if vlInfo in LogLevels then
     AddOption('--verbose');
-  AddOption('--compiler='+CompilerOptions.Compiler);
-  AddOption('--cpu='+CPUToString(CompilerOptions.CompilerCPU));
-  AddOption('--os='+OSToString(CompilerOptions.CompilerOS));
-  if CompilerOptions.HasOptions then
-    AddOption('--options='+CompilerOptions.Options.DelimitedText);
-  if IsSuperUser or GlobalOptions.InstallGlobal then
+  if P.RecompileBroken and
+     (P.FPMakeOptionsString<>'') then // Check for a empty FPMakeOptionString for packages being installed with an old fpmkunit
     begin
-      CondAddOption('--prefix',CompilerOptions.GlobalPrefix);
-      CondAddOption('--baseinstalldir',CompilerOptions.GlobalInstallDir);
+      // When the package is being reinstalled because of broken dependencies, use the same fpmake-options
+      // as were used to compile the package in the first place.
+      OOptions:=P.FPMakeOptionsString;
     end
   else
     begin
-      CondAddOption('--prefix',CompilerOptions.LocalPrefix);
-      CondAddOption('--baseinstalldir',CompilerOptions.LocalInstallDir);
-    end;
-  CondAddOption('--localunitdir',CompilerOptions.LocalUnitDir);
-  CondAddOption('--globalunitdir',CompilerOptions.GlobalUnitDir);
-  if GlobalOptions.CustomFPMakeOptions<>'' then
-    begin
-    AddOption('--ignoreinvalidoption');
-    AddOption(GlobalOptions.CustomFPMakeOptions);
+      AddOption('--nofpccfg');
+      AddOption('--compiler='+CompilerOptions.Compiler);
+      AddOption('--cpu='+CPUToString(CompilerOptions.CompilerCPU));
+      AddOption('--os='+OSToString(CompilerOptions.CompilerOS));
+      if CompilerOptions.HasOptions then
+        AddOption('--options='+CompilerOptions.Options.DelimitedText);
+      if IsSuperUser or GlobalOptions.InstallGlobal then
+        begin
+          CondAddOption('--prefix',CompilerOptions.GlobalPrefix);
+          CondAddOption('--baseinstalldir',CompilerOptions.GlobalInstallDir);
+        end
+      else
+        begin
+          CondAddOption('--prefix',CompilerOptions.LocalPrefix);
+          CondAddOption('--baseinstalldir',CompilerOptions.LocalInstallDir);
+        end;
+      CondAddOption('--localunitdir',CompilerOptions.LocalUnitDir);
+      CondAddOption('--globalunitdir',CompilerOptions.GlobalUnitDir);
+      if GlobalOptions.CustomFPMakeOptions<>'' then
+        begin
+        AddOption('--ignoreinvalidoption');
+        AddOption(GlobalOptions.CustomFPMakeOptions);
+        end;
     end;
   { Run FPMake }
   FPMakeBin:='fpmake'+ExeExt;

+ 2 - 2
utils/fppkg/pkgglobals.pp

@@ -52,11 +52,11 @@ Const
   );
 
 Type
-  TLogLevel = (vlError,vlWarning,vlInfo,vlCommands,vlDebug);
+  TLogLevel = (vlError,vlWarning,vlInfo,vlCommands,vlDebug,vlProgres);
   TLogLevels = Set of TLogLevel;
 
 const
-  DefaultLogLevels = [vlError,vlWarning];
+  DefaultLogLevels = [vlError,vlWarning, vlProgres];
   AllLogLevels = [vlError,vlWarning,vlCommands,vlInfo];
 
 type

+ 15 - 1
utils/fppkg/pkghandler.pp

@@ -57,6 +57,7 @@ uses
 var
   PkgHandlerList  : TFPHashList;
   ExecutedActions : TFPHashList;
+  CurrentDir      : string;
 
 procedure RegisterPkgHandler(const AAction:string;pkghandlerclass:TPackageHandlerClass);
 begin
@@ -106,9 +107,22 @@ end;
 function PackageBuildPath(APackage:TFPPackage):String;
 begin
   if APackage.Name=CurrentDirPackageName then
-    Result:='.'
+    begin
+      // It could be that to resolve some dependencies, the current directory changes. The first time
+      // PackageBuildPath is called the dependencies are not resolved yet, so store the current directory
+      // for later calls.
+      if CurrentDir='' then
+        begin
+          Result:='.';
+          CurrentDir := SysUtils.GetCurrentDir;
+        end
+      else
+        Result:=CurrentDir;
+    end
   else if APackage.Name=CmdLinePackageName then
     Result:=GlobalOptions.BuildDir+ChangeFileExt(ExtractFileName(APackage.LocalFileName),'')
+  else if (APackage.RecompileBroken) and (APackage.SourcePath<>'') then
+    Result:=APackage.SourcePath
   else
     Result:=GlobalOptions.BuildDir+APackage.Name;
 end;

+ 3 - 0
utils/fppkg/pkgmessages.pp

@@ -109,6 +109,9 @@ Resourcestring
   SDbgPackageDependencyOtherTarget  = 'Dependency on package %s is not for %s';
   SDbgObsoleteDependency     = 'Obsolete dependency found on package %s';
 
+  SProgrReinstallDependent   = 'Re-install packages which are dependent on just installed packages';
+  SProgrInstallDependencies  = 'Install dependencies';
+  SProgrDependenciesInstalled= 'Dependencies installed';
 
 implementation
 

+ 33 - 5
utils/fppkg/pkgrepos.pp

@@ -15,7 +15,7 @@ procedure LoadLocalAvailableRepository;
 procedure LoadUnitConfigFromFile(APackage:TFPPackage;const AFileName: String);
 function LoadManifestFromFile(const AManifestFN:string):TFPPackage;
 procedure FindInstalledPackages(ACompilerOptions:TCompilerOptions;showdups:boolean=true);
-function  PackageIsBroken(APackage:TFPPackage):boolean;
+function  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;
@@ -216,6 +216,8 @@ begin
     V:=L.Values['version'];
     APackage.Version.AsString:=V;
     APackage.IsFPMakeAddIn:=Upcase(L.Values['FPMakeAddIn'])='Y';
+    APackage.SourcePath:=L.Values['SourcePath'];
+    APackage.FPMakeOptionsString:=L.Values['FPMakeOptions'];
     V:=L.Values['checksum'];
     if V<>'' then
       APackage.Checksum:=StrToInt(V)
@@ -347,11 +349,12 @@ begin
 end;
 
 
-function PackageIsBroken(APackage:TFPPackage):boolean;
+function PackageIsBroken(APackage:TFPPackage; MarkForReInstall: boolean):boolean;
 var
   j : integer;
   D : TFPDependency;
   DepPackage : TFPPackage;
+  AvailP: TFPPackage;
 begin
   result:=false;
   for j:=0 to APackage.Dependencies.Count-1 do
@@ -368,6 +371,29 @@ begin
                 begin
                   Log(vlInfo,SLogPackageChecksumChanged,[APackage.Name,D.PackageName]);
                   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;
             end
@@ -387,8 +413,10 @@ begin
   for i:=0 to InstalledRepository.PackageCount-1 do
     begin
       P:=InstalledRepository.Packages[i];
-      if PackageIsBroken(P) then
-        SL.Add(P.Name);
+      if PackageIsBroken(P,True) then
+        begin
+          SL.Add(P.Name);
+        end;
     end;
   Result:=(SL.Count>0);
 end;
@@ -507,7 +535,7 @@ var
 begin
   result := '';
   P:=InstalledRepository.FindPackage(AName);
-  if (P<>nil) and PackageIsBroken(P) then
+  if (P<>nil) and PackageIsBroken(P,false) then
     result:='B';
 end;