Win-Installer: fixes/rewrite for ability to create secondary installations.

git-svn-id: trunk@42527 -
This commit is contained in:
martin 2013-08-30 14:49:13 +00:00
parent 4e1b98a5f2
commit cac49d63f6
5 changed files with 226 additions and 85 deletions

View File

@ -5,7 +5,7 @@ function GetAppId(param:string): String;
var var
s: String; s: String;
begin begin
if ( (CheckSecondInstall <> nil) and (CheckSecondInstall.Checked) ) or IsSecondaryUpdate then if (IsSecondaryCheckBoxChecked) or IsSecondaryUpdate then
begin begin
// Secondary // Secondary
s := RemoveBackslashUnlessRoot(Lowercase(WizardDirValue)); s := RemoveBackslashUnlessRoot(Lowercase(WizardDirValue));
@ -23,7 +23,7 @@ function GetPCPForDelete(param:string): String;
// Name: {code:GetPCPForDelete}*.xml; Type: files; Tasks: delusersettings // Name: {code:GetPCPForDelete}*.xml; Type: files; Tasks: delusersettings
// ... delete primary conf // ... delete primary conf
begin begin
if ( (CheckSecondInstall <> nil) and (CheckSecondInstall.Checked) ) then if (IsSecondaryCheckBoxChecked) then // or IsSecondaryUpdate
begin begin
if SecondPCP = '' then if SecondPCP = '' then
Result := AddBackslash(WizardDirValue) // some fallback Result := AddBackslash(WizardDirValue) // some fallback

View File

@ -1,10 +1,4 @@
type
TCfgFileState = (csNoFile, csUnreadable, csParsedOk);
var
NewCFGFile: TStringList;
function LoadCFGFile(AFolder: String; var AList: TStringList): Boolean; function LoadCFGFile(AFolder: String; var AList: TStringList): Boolean;
var var
cfgfile: String; cfgfile: String;
@ -32,30 +26,19 @@ begin
AList.add('--primary-config-path=' + APCP); AList.add('--primary-config-path=' + APCP);
end; end;
function ParseCFGFile(AFolder: String; var APrimConfDir: String): TCfgFileState; function ParseCFGList(AConfig: TStringList; var APrimConfDir: String): TCfgFileState;
var var
s, cfgfile: String; s: String;
i: Integer; i: Integer;
l: TStringList;
begin begin
cfgfile := AddBackslash(AFolder) + 'lazarus.cfg';
Result := csNoFile;
if not FileExists(cfgfile) then begin
Log('ParseCFGFile not existent');
exit;
end;
Result := csUnreadable; Result := csUnreadable;
l := TStringList.Create; for i := 0 to AConfig.Count - 1 do
l.LoadFromFile(cfgfile); if copy(AConfig[i], 1, 6) = '--pcp=' then
for i := 0 to l.Count - 1 do s := copy(AConfig[i], 7, length(AConfig[i]))
if copy(l[i], 1, 6) = '--pcp=' then
s := copy(l[i], 7, length(l[i]))
else else
if copy(l[i], 1, 22) = '--primary-config-path=' then if copy(AConfig[i], 1, 22) = '--primary-config-path=' then
s := copy(l[i], 23, length(l[i])); s := copy(AConfig[i], 23, length(AConfig[i]));
l.Free; // AConfig.Free;
if s = '' then if s = '' then
exit; exit;
@ -81,3 +64,23 @@ begin
Log('ParseCFGFile OK'); Log('ParseCFGFile OK');
end; end;
function ParseCFGFile(AFolder: String; var APrimConfDir: String): TCfgFileState;
var
s, cfgfile: String;
i: Integer;
l: TStringList;
begin
cfgfile := AddBackslash(AFolder) + 'lazarus.cfg';
Result := csNoFile;
if not FileExists(cfgfile) then begin
Log('ParseCFGFile not existent');
exit;
end;
l := TStringList.Create;
l.LoadFromFile(cfgfile);
Result := ParseCFGList(l, APrimConfDir);
l.Free;
end;

View File

@ -2,6 +2,83 @@
var var
wpAskConfDir: TInputDirWizardPage; wpAskConfDir: TInputDirWizardPage;
// Additional Elements on TargetDir wizard page
CheckSecondInstall: TCheckBox; // Also used by GetAppId
CheckSecondLabel: TLabel;
CfgLoadedFromDir: String; // The directory from which lazarus was uninstalled
CFGFileForLoadedFromDir: TStringList;
CFGPathForLoadedFromDir: String; // the PCP
CFGStateForLoadedFromDir: TCfgFileState;
Procedure ClearExistingConfigForFolder;
begin
CfgLoadedFromDir := '';
if CFGFileForLoadedFromDir <> nil then
CFGFileForLoadedFromDir.Clear;
CFGPathForLoadedFromDir := '';
CFGStateForLoadedFromDir := csNoFile;
end;
Procedure LoadExistingConfigForFolder(AFolder: String);
begin
CfgLoadedFromDir := AFolder;
LoadCFGFile(AFolder, CFGFileForLoadedFromDir);
CFGStateForLoadedFromDir := ParseCFGList(CFGFileForLoadedFromDir, CFGPathForLoadedFromDir);
end;
function HasConfigLoadedFromDir(AFolder: String; FallBackToUninstallDir: Boolean): Boolean;
begin
Result := (CfgLoadedFromDir = AFolder) and
(CFGFileForLoadedFromDir <> nil) and
(CFGFileForLoadedFromDir.Count > 0); // only if content
if (not Result) and FallBackToUninstallDir then
Result := HasSavedConfigFromUninstall(AFolder);
end;
// Did the loadedconf contain a pcp?
function HasPCPLoadedFromDir(AFolder: String; FallBackToUninstallDir: Boolean): Boolean;
begin
Result := False;
if HasConfigLoadedFromDir(AFolder, False) then
Result := CFGPathForLoadedFromDir <> ''
else
if FallBackToUninstallDir and HasSavedConfigFromUninstall(AFolder) then
Result := GetSavedPCPFromUninstall(AFolder) <> '';
end;
function GetConfigLoadedFromDir(AFolder: String; FallBackToUninstallDir: Boolean): TStringList;
begin
Result := nil;
if HasConfigLoadedFromDir(AFolder, False) then
Result := CFGFileForLoadedFromDir
else
if FallBackToUninstallDir and HasSavedConfigFromUninstall(AFolder) then
Result := GetSavedConfigFromUninstall(AFolder);
end;
function GetPCPLoadedFromDir(AFolder: String; FallBackToUninstallDir: Boolean): String;
begin
Result := '';
if HasConfigLoadedFromDir(AFolder, False) then
Result := CFGPathForLoadedFromDir
else
if FallBackToUninstallDir and HasSavedConfigFromUninstall(AFolder) then
Result := GetSavedPCPFromUninstall(AFolder);
end;
function GetStateLoadedFromDir(AFolder: String; FallBackToUninstallDir: Boolean): TCfgFileState;
begin
Result := csNoFile;
if HasConfigLoadedFromDir(AFolder, False) then
Result := CFGStateForLoadedFromDir
else
if FallBackToUninstallDir and HasSavedConfigFromUninstall(AFolder) then
Result := GetSavedStateFromUninstall(AFolder);
end;
Procedure AddSecondaryCheckBoxToTargetDirWizzard; Procedure AddSecondaryCheckBoxToTargetDirWizzard;
begin begin
if (CheckSecondInstall <> nil) then if (CheckSecondInstall <> nil) then
@ -40,12 +117,26 @@ begin
wpAskConfDir.Add('Folder for config'); wpAskConfDir.Add('Folder for config');
end; end;
Procedure CreateOrSaveConfigFile; function IsSecondaryCheckBoxChecked: Boolean;
begin begin
if (CheckSecondInstall <> nil) and (CheckSecondInstall.Checked) then begin Result := (CheckSecondInstall <> nil) and (CheckSecondInstall.Checked);
if (NewCFGFile <> nil) then end;
Procedure CreateOrSaveConfigFile;
var
CfgFile: TStringList;
begin
if not (IsSecondaryCheckBoxChecked or IsSecondaryUpdate) then
exit;
if IsSecondaryCheckBoxChecked then begin
CfgFile := GetConfigLoadedFromDir(WizardDirValue, True);
if (GetPCPLoadedFromDir(WizardDirValue, True) <> SecondPCP) or (CfgFile = nil) then
CreateCFGFile(SecondPCP, CfgFile);
if (SecondPCP <> '') then
try try
NewCFGFile.SaveToFile(AddBackslash(WizardDirValue) + 'lazarus.cfg') CfgFile.SaveToFile(AddBackslash(WizardDirValue) + 'lazarus.cfg')
ForceDirectories(SecondPCP); ForceDirectories(SecondPCP);
except except
MsgBox('Internal Error (1): Could not save CFG for secondary install', mbConfirmation, MB_OK); MsgBox('Internal Error (1): Could not save CFG for secondary install', mbConfirmation, MB_OK);
@ -54,30 +145,35 @@ begin
MsgBox('Internal Error (2): Could not save CFG for secondary install', mbConfirmation, MB_OK); MsgBox('Internal Error (2): Could not save CFG for secondary install', mbConfirmation, MB_OK);
end; end;
end end
else else
if (UninstallDoneState <> uiUnknown) and (IsSecondaryUpdate) and // NO checkbox.checked
if (DidRunUninstaller) and (IsSecondaryUpdate) and
(not FileExists(AddBackslash(WizardDirValue) + 'lazarus.cfg')) (not FileExists(AddBackslash(WizardDirValue) + 'lazarus.cfg'))
then begin then begin
// cfg was uninstalled / restore // cfg was uninstalled / restore
if (NewCFGFile <> nil) then CfgFile := GetConfigLoadedFromDir(WizardDirValue, True);
if (CfgFile <> nil) then
try try
NewCFGFile.SaveToFile(AddBackslash(WizardDirValue) + 'lazarus.cfg') CfgFile.SaveToFile(AddBackslash(WizardDirValue) + 'lazarus.cfg')
except except
MsgBox('Internal Error (3): Could not restore CFG for secondary install', mbConfirmation, MB_OK); MsgBox('Internal Error (3): Could not restore CFG for secondary install', mbConfirmation, MB_OK);
end end
else else
if (UninstDir = WizardDirValue) and (CFGFileForUninstDir <> nil) and begin
(CFGFileForUninstDir.count > 0)
then begin
try
CFGFileForUninstDir.SaveToFile(AddBackslash(WizardDirValue) + 'lazarus.cfg')
except
MsgBox('Internal Error (4): Could not restore CFG for secondary install', mbConfirmation, MB_OK);
end
end
else begin
MsgBox('Internal Error (5): Could not restore CFG for secondary install', mbConfirmation, MB_OK); MsgBox('Internal Error (5): Could not restore CFG for secondary install', mbConfirmation, MB_OK);
end; end;
end; end
else
// NO checkbox.checked
if (IsSecondaryUpdate) and
(not FileExists(AddBackslash(WizardDirValue) + 'lazarus.cfg'))
then begin
// where is the config gone ???????
MsgBox('Internal Error (4): Pre-Existing Configfile was removed?', mbConfirmation, MB_OK);
end
;
end; end;

View File

@ -20,8 +20,10 @@ var
UnInstaller: String; // Registry 'UninstallString' UnInstaller: String; // Registry 'UninstallString'
PathEqual: Boolean; PathEqual: Boolean;
UninstDir: String; UninstDir: String; // The directory from which lazarus was uninstalled
CFGFileForUninstDir: TStringList; CFGFileForUninstDir: TStringList;
CFGPathForUninstDir: String; // the PCP
CFGStateForUninstDir: TCfgFileState;
var var
wpAskUnistall: TWizardPage; wpAskUnistall: TWizardPage;
@ -40,6 +42,41 @@ begin
end; end;
end; end;
function DidRunUninstaller: Boolean;
begin
Result := (UninstallDoneState <> uiUnknown);
end;
// check if: unistall was run, and run in AFolder, and did have a lazarus.cfg file
function HasSavedConfigFromUninstall(AFolder: String): Boolean;
begin
Result := DidRunUninstaller and
(UninstDir = AFolder) and
(CFGFileForUninstDir <> nil) and
(CFGFileForUninstDir.Count > 0); // only if content
end;
function GetSavedConfigFromUninstall(AFolder: String): TStringList;
begin
Result := nil;
if HasSavedConfigFromUninstall(AFolder) then
Result := CFGFileForUninstDir;
end;
function GetSavedPCPFromUninstall(AFolder: String): String;
begin
Result := '';
if HasSavedConfigFromUninstall(AFolder) then
Result := CFGPathForUninstDir;
end;
function GetSavedStateFromUninstall(AFolder: String): TCfgFileState;
begin
Result := csNoFile;
if HasSavedConfigFromUninstall(AFolder) then
Result := CFGStateForUninstDir;
end;
function GetUninstallData(ARegName: String): String; // Get one entry from registry e.g. 'UninstallString' function GetUninstallData(ARegName: String): String; // Get one entry from registry e.g. 'UninstallString'
var var
Path: String; Path: String;
@ -89,7 +126,7 @@ begin
end end
else else
begin begin
if ( (CheckSecondInstall <> nil) and (CheckSecondInstall.Checked) ) or IsSecondaryUpdate then if (IsSecondaryCheckBoxChecked) or IsSecondaryUpdate then
begin begin
ForcePrimaryAppId := True; ForcePrimaryAppId := True;
Log('REDO UninstallState '+GetUninstallData('Inno Setup: App Path')+' // '+WizardDirValue); Log('REDO UninstallState '+GetUninstallData('Inno Setup: App Path')+' // '+WizardDirValue);
@ -138,7 +175,7 @@ begin
UpdateUninstallInfo; UpdateUninstallInfo;
Log('UnInstUpdateGUI UninstallState='+dbgsUiState(UninstallState)+ Log('UnInstUpdateGUI UninstallState='+dbgsUiState(UninstallState)+
' IsSecondaryUpdate='+dbgsBool(IsSecondaryUpdate)+ ' IsSecondaryUpdate='+dbgsBool(IsSecondaryUpdate)+
' Check='+dbgsBool((CheckSecondInstall <> nil) and (CheckSecondInstall.Checked)) ' Check='+dbgsBool(IsSecondaryCheckBoxChecked)
); );
WizardForm.NextButton.Enabled := (UninstallState = uiDone) or (UninstallState = uiDestNeeded) or wpCheckBox.Checked; WizardForm.NextButton.Enabled := (UninstallState = uiDone) or (UninstallState = uiDestNeeded) or wpCheckBox.Checked;
@ -156,7 +193,7 @@ begin
Log('SkipAskUninst UninstallState='+dbgsUiState(UninstallState)+ Log('SkipAskUninst UninstallState='+dbgsUiState(UninstallState)+
', OldPath='+OldPath+' OldName='+OldName+' UnInstaller='+UnInstaller + ', OldPath='+OldPath+' OldName='+OldName+' UnInstaller='+UnInstaller +
' IsSecondaryUpdate='+dbgsBool(IsSecondaryUpdate)+ ' IsSecondaryUpdate='+dbgsBool(IsSecondaryUpdate)+
' Check='+dbgsBool((CheckSecondInstall <> nil) and (CheckSecondInstall.Checked)) ' Check='+dbgsBool(IsSecondaryCheckBoxChecked)
); );
Result := UninstallState = uiDone; Result := UninstallState = uiDone;
if Result Then exit; if Result Then exit;
@ -253,8 +290,11 @@ begin
b := (UnInstaller <> '') and FileExists(UnInstaller); b := (UnInstaller <> '') and FileExists(UnInstaller);
if b then begin if b then begin
LoadCFGFile(WizardDirValue, CFGFileForUninstDir); if PathEqual then begin
UninstDir := WizardDirValue; LoadCFGFile(OldPath, CFGFileForUninstDir);
CFGStateForUninstDir := ParseCFGList(CFGFileForUninstDir, CFGPathForUninstDir);
UninstDir := OldPath;
end;
if UninstallState = uiInconsistent then if UninstallState = uiInconsistent then
b := Exec(UnInstaller, '/VERBOSE /NORESTART','', SW_SHOW, ewWaitUntilTerminated, i) b := Exec(UnInstaller, '/VERBOSE /NORESTART','', SW_SHOW, ewWaitUntilTerminated, i)

View File

@ -266,23 +266,30 @@ Name: ru; MessagesFile: lazarus.ru.isl
Name: sl; MessagesFile: compiler:Languages\Slovenian.isl Name: sl; MessagesFile: compiler:Languages\Slovenian.isl
[Code] [Code]
type
TCfgFileState = (csNoFile, csUnreadable, csParsedOk);
var var
ForcePrimaryAppId: Boolean; // GetAppId should ignore secondary ForcePrimaryAppId: Boolean; // GetAppId should ignore secondary
// Additional Elements on TargetDir wizard page
CheckSecondInstall: TCheckBox; // Also used by GetAppId
CheckSecondLabel: TLabel;
IsSecondaryUpdate: Boolean; // Also used by GetAppId IsSecondaryUpdate: Boolean; // Also used by GetAppId
// User Selected
SecondPCP: String; // used by common.GetPCPForDelete SecondPCP: String; // used by common.GetPCPForDelete
function IsSecondaryCheckBoxChecked: Boolean; forward; // in secondary.pas
function DidRunUninstaller: Boolean; forward; // in uninst.pas
function HasSavedConfigFromUninstall(AFolder: String): Boolean; forward; // in uninst.pas
function GetSavedConfigFromUninstall(AFolder: String): TStringList; forward; // in uninst.pas
function GetSavedPCPFromUninstall(AFolder: String): String; forward; // in uninst.pas
function GetSavedStateFromUninstall(AFolder: String): TCfgFileState; forward; // in uninst.pas
#include "innoscript\common.pas" #include "innoscript\common.pas"
#include "innoscript\conffile.pas" ; // Check/Load lazarus.cfg file // Create TStringList data #include "innoscript\conffile.pas"
#include "innoscript\uninst.pas" ; // Uninstall of previous installation
#include "innoscript\about.pas" ; // Info displayed during install progress
#include "innoscript\secondary.pas" #include "innoscript\secondary.pas"
#include "innoscript\uninst.pas"
#include "innoscript\about.pas"
procedure CurPageChanged(CurPageID: Integer); procedure CurPageChanged(CurPageID: Integer);
@ -311,6 +318,7 @@ begin
begin begin
Log('NextButton in SelectDir'); Log('NextButton in SelectDir');
IsSecondaryUpdate := False; IsSecondaryUpdate := False;
ClearExistingConfigForFolder;
folder := WizardDirValue; folder := WizardDirValue;
if Pos( ' ', folder ) > 0 then if Pos( ' ', folder ) > 0 then
@ -322,16 +330,21 @@ begin
end; end;
FolderEmpty := IsDirEmpty(folder); FolderEmpty := IsDirEmpty(folder);
LoadExistingConfigForFolder(folder);
IsSecondaryUpdate := HasPCPLoadedFromDir(folder, True); // check for uninstalled file too
SecondPCP := GetPCPLoadedFromDir(folder, True);
// TODO:
// If we came back AFTER running uninstall,
// AND changed the folder for and back (ending with the uninstall folder selected)
// TEHN we should ask, if the uninstalled (todo uninstall cfg file) should be restored?
UpdateUninstallInfo; UpdateUninstallInfo;
if FolderEmpty then if FolderEmpty then
exit; exit;
if ( (CheckSecondInstall <> nil) and (CheckSecondInstall.Checked) ) then if (IsSecondaryCheckBoxChecked) then
begin begin // Secondary
// Secondary case GetStateLoadedFromDir(folder, True) of
// ALways set "SecondPCP", if avail
case ParseCFGFile(folder, SecondPCP) of
csNoFile: begin csNoFile: begin
Result := False; Result := False;
MsgBox(Format(CustomMessage('FolderForSecondNoFile'), [#13#10]), mbConfirmation, MB_OK); MsgBox(Format(CustomMessage('FolderForSecondNoFile'), [#13#10]), mbConfirmation, MB_OK);
@ -355,33 +368,24 @@ begin
(UninstallState = uiInconsistent) (UninstallState = uiInconsistent)
then begin then begin
Result := MsgBox(Format(CustomMessage('FolderForSecondUpgrading'), [#13#10, SecondPCP]), mbConfirmation, MB_YESNO) = IDYES; Result := MsgBox(Format(CustomMessage('FolderForSecondUpgrading'), [#13#10, SecondPCP]), mbConfirmation, MB_YESNO) = IDYES;
IsSecondaryUpdate := True;
UpdateUninstallInfo;
end; end;
end; end;
end; end;
// MUST always be loaded, if leaving this page
LoadCFGFile(folder, NewCFGFile);
end end
else else
begin begin
// Dir NOT empty: do not warn, if uiDestNeeded => folder content is updatable lazarus
if ((UninstallState = uiDone) or (UninstallState = UIOtherNeeded)) or if ((UninstallState = uiDone) or (UninstallState = UIOtherNeeded)) or
(UninstallState = uiInconsistent) (UninstallState = uiInconsistent)
then
begin
// Dir NOT empty
Result := MsgBox(SaveCustomMessage('FolderNotEmpty', 'The target folder is not empty. Continue with installation?'),
mbConfirmation, MB_YESNO) = IDYES;
end;
if Result and
(ParseCFGFile(folder, SecondPCP) = csParsedOk)
then begin then begin
IsSecondaryUpdate := True; if IsSecondaryUpdate then
LoadCFGFile(folder, NewCFGFile); Result := MsgBox(Format(SaveCustomMessage('FolderForSecondUpgrading', 'The target folder is not empty.%0:sIt contains a secondary Lazarus installation using the following folder for configuration:%0:s%1:s%0:s%0:sContinue with installation?'),
UpdateUninstallInfo; {}[#13#10, SecondPCP]), mbConfirmation, MB_YESNO) = IDYES
end; else
Result := MsgBox(SaveCustomMessage('FolderNotEmpty', 'The target folder is not empty. Continue with installation?'),
mbConfirmation, MB_YESNO) = IDYES;
end;
end; end;
end; end;
@ -395,7 +399,6 @@ begin
end; end;
SecondPCP := s; SecondPCP := s;
CreateCFGFile(SecondPCP, NewCFGFile);
end; end;
end; end;
@ -405,11 +408,10 @@ begin
Result := False Result := False
if PageId = wpAskConfDir.ID then begin if PageId = wpAskConfDir.ID then begin
Log('ShouldSkip AskConfDir IsSecondaryUpdate='+dbgsBool(IsSecondaryUpdate)+ Log('ShouldSkip AskConfDir IsSecondaryUpdate='+dbgsBool(IsSecondaryUpdate)+
' Check='+dbgsBool((CheckSecondInstall <> nil) and (CheckSecondInstall.Checked)) ' Check='+dbgsBool(IsSecondaryCheckBoxChecked)
); );
Result := (IsSecondaryUpdate) or Result := (not IsSecondaryCheckBoxChecked) or IsSecondaryUpdate;
( (CheckSecondInstall = nil) or (not CheckSecondInstall.Checked) );
end; end;
// UnInst uses: SkipAskUninst() // UnInst uses: SkipAskUninst()
end; end;
@ -431,7 +433,7 @@ begin
Result := Result + MemoGroupInfo + NewLine; Result := Result + MemoGroupInfo + NewLine;
if MemoTasksInfo <> '' then if MemoTasksInfo <> '' then
Result := Result + MemoTasksInfo + NewLine; Result := Result + MemoTasksInfo + NewLine;
if (CheckSecondInstall <> nil) and (CheckSecondInstall.Checked) then begin if (IsSecondaryCheckBoxChecked) then begin
if IsSecondaryUpdate then if IsSecondaryUpdate then
Result := Result + Format(SaveCustomMessage('SecondTaskUpdate', ''), [NewLine, Space, SecondPCP]) Result := Result + Format(SaveCustomMessage('SecondTaskUpdate', ''), [NewLine, Space, SecondPCP])
else else