Installer: Write environment variables as expandable strings
[msysgit.git] / share / WinGit / install.iss
blobba5b355c3e0b2a00228f0ca66083cda8b527f651
1 ; Uncomment the line below to be able to compile the script from within the IDE.
2 ;#define COMPILE_FROM_IDE
4 #define APP_NAME 'Git'
5 #ifdef COMPILE_FROM_IDE
6 #define APP_VERSION 'Snapshot'
7 #else
8 #define APP_VERSION '%APPVERSION%'
9 #endif
10 #define APP_URL 'http://msysgit.googlecode.com/'
11 #define APP_BUILTINS 'etc\fileList-builtins.txt'
12 #define APP_BINDIMAGE 'etc\fileList-bindimage.txt'
14 #define PLINK_PATH_ERROR_MSG 'Please enter a valid path to a Plink executable.'
16 #define DROP_HANDLER_GUID '{{86C86720-42A0-1069-A2E8-08002B30309D}'
18 [Setup]
19 ; Compiler-related
20 Compression=lzma2/ultra
21 LZMAUseSeparateProcess=yes
22 OutputBaseFilename={#APP_NAME+'-'+APP_VERSION}
23 OutputDir={#GetEnv('USERPROFILE')}
24 SolidCompression=yes
25 #ifdef COMPILE_FROM_IDE
26 SourceDir={#GetEnv('TEMP')}\WinGit
27 #endif
29 ; Installer-related
30 AllowNoIcons=yes
31 AppName={#APP_NAME}
32 AppPublisher=The Git Development Community
33 AppPublisherURL={#APP_URL}
34 AppVersion={#APP_VERSION}
35 ChangesEnvironment=yes
36 DefaultDirName={pf}\{#APP_NAME}
37 DisableDirPage=auto
38 DefaultGroupName={#APP_NAME}
39 DisableProgramGroupPage=auto
40 DisableReadyPage=yes
41 InfoBeforeFile=gpl-2.0.rtf
42 PrivilegesRequired=none
43 UninstallDisplayIcon={app}\etc\git.ico
44 #ifndef COMPILE_FROM_IDE
45 #if Pos('-',APP_VERSION)>0
46 VersionInfoVersion={#Copy(APP_VERSION,1,Pos('-',APP_VERSION)-1)}
47 #else
48 VersionInfoVersion={#APP_VERSION}
49 #endif
50 #endif
52 ; Cosmetic
53 SetupIconFile=etc\git.ico
54 WizardImageBackColor=clWhite
55 WizardImageStretch=no
56 WizardImageFile=git.bmp
57 WizardSmallImageFile=gitsmall.bmp
59 [Types]
60 ; Define a custom type to avoid getting the three default types.
61 Name: default; Description: Default installation; Flags: iscustom
63 [Components]
64 Name: icons; Description: Additional icons
65 Name: icons\quicklaunch; Description: In the Quick Launch
66 Name: icons\desktop; Description: On the Desktop
67 Name: ext; Description: Windows Explorer integration; Types: default
68 Name: ext\reg; Description: Simple context menu (Registry based); Flags: exclusive; Types: default
69 Name: ext\reg\shellhere; Description: Git Bash Here; Types: default
70 Name: ext\reg\guihere; Description: Git GUI Here; Types: default
71 Name: ext\cheetah; Description: Advanced context menu (git-cheetah plugin); Flags: exclusive; Types: default
72 Name: assoc; Description: Associate .git* configuration files with the default text editor; Types: default
73 Name: assoc_sh; Description: Associate .sh files to be run with Bash; Types: default
74 Name: consolefont; Description: Use a TrueType font in all console windows (not only for Git Bash)
76 [Files]
77 ; Install files that might be in use during setup under a different name.
78 Source: git-cheetah\git_shell_ext.dll; DestDir: {app}\git-cheetah; DestName: git_shell_ext.dll.new; Flags: replacesameversion; Components: ext\cheetah; AfterInstall: DeleteFromVirtualStore
79 Source: git-cheetah\git_shell_ext64.dll; DestDir: {app}\git-cheetah; DestName: git_shell_ext64.dll.new; Flags: replacesameversion; Components: ext\cheetah; AfterInstall: DeleteFromVirtualStore
81 Source: *; DestDir: {app}; Excludes: \*.bmp, gpl-2.0.rtf, \*.iss, \tmp.*, \bin\*install*, \git-cheetah\git_shell_ext.dll, \git-cheetah\git_shell_ext64.dll; Flags: recursesubdirs replacesameversion sortfilesbyextension; AfterInstall: DeleteFromVirtualStore
82 Source: ReleaseNotes.rtf; DestDir: {app}; Flags: isreadme replacesameversion; AfterInstall: DeleteFromVirtualStore
84 [Icons]
85 Name: {group}\Git GUI; Filename: {app}\bin\wish.exe; Parameters: """{app}\libexec\git-core\git-gui"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico
86 Name: {group}\Git Bash; Filename: {syswow64}\cmd.exe; Parameters: "/c """"{app}\bin\sh.exe"" --login -i"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico; OnlyBelowVersion: 6.0
87 Name: {group}\Git Bash; Filename: {app}\bin\sh.exe; Parameters: "--login -i"; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico; MinVersion: 6.0
89 [Messages]
90 BeveledLabel={#APP_URL}
91 SetupAppTitle={#APP_NAME} Setup
92 SetupWindowTitle={#APP_NAME} Setup
94 [Registry]
95 ; There is no "Console" key in HKLM.
96 Root: HKCU; Subkey: Console; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: uninsclearvalue; Components: consolefont
97 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Components: consolefont
98 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Components: consolefont
99 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Components: consolefont
101 Root: HKCU; Subkey: Console\Git Bash; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
102 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
103 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
104 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
106 ; Note that we write the Registry values below either to HKLM or to HKCU depending on whether the user running the installer
107 ; is a member of the local Administrators group or not (see the "Check" argument).
109 ; File associations for configuration files that may be contained in a repository (so this does not include ".gitconfig").
110 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
111 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
112 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
113 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
114 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
115 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
117 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
118 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
119 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
120 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
121 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
122 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
124 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
125 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
126 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
127 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
128 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
129 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
131 ; Associate .sh extension with sh.exe so those files are double-clickable,
132 ; startable from cmd.exe, and when files are dropped on them they are passed
133 ; as arguments to the script.
135 ; Install under HKEY_LOCAL_MACHINE if an administrator is installing.
136 Root: HKLM; Subkey: Software\Classes\.sh; ValueType: string; ValueData: sh_auto_file; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
137 Root: HKLM; Subkey: Software\Classes\sh_auto_file; ValueType: string; ValueData: "Shell Script"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
138 Root: HKLM; Subkey: Software\Classes\sh_auto_file\shell\open\command; ValueType: string; ValueData: "{syswow64}\cmd.exe /C """"{app}\bin\sh.exe"" ""--login"" ""%1"" %*"""; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh; OnlyBelowVersion: 6.0
139 Root: HKLM; Subkey: Software\Classes\sh_auto_file\shell\open\command; ValueType: string; ValueData: """{app}\bin\sh.exe"" ""--login"" ""%1"" %*"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh; MinVersion: 6.0
140 Root: HKLM; Subkey: Software\Classes\sh_auto_file\DefaultIcon; ValueType: string; ValueData: "%SystemRoot%\System32\shell32.dll,-153"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
141 Root: HKLM; Subkey: Software\Classes\sh_auto_file\ShellEx\DropHandler; ValueType: string; ValueData: {#DROP_HANDLER_GUID}; Flags: uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
143 ; Install under HKEY_CURRENT_USER if a non-administrator is installing.
144 Root: HKCU; Subkey: Software\Classes\.sh; ValueType: string; ValueData: sh_auto_file; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
145 Root: HKCU; Subkey: Software\Classes\sh_auto_file; ValueType: string; ValueData: "Shell Script"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
146 Root: HKCU; Subkey: Software\Classes\sh_auto_file\shell\open\command; ValueType: string; ValueData: "{syswow64}\cmd.exe /C """"{app}\bin\sh.exe"" ""--login"" ""%1"" %*"""; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh; OnlyBelowVersion: 6.0
147 Root: HKCU; Subkey: Software\Classes\sh_auto_file\shell\open\command; ValueType: string; ValueData: """{app}\bin\sh.exe"" ""--login"" ""%1"" %*"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh; MinVersion: 6.0
148 Root: HKCU; Subkey: Software\Classes\sh_auto_file\DefaultIcon; ValueType: string; ValueData: "%SystemRoot%\System32\shell32.dll,-153"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
149 Root: HKCU; Subkey: Software\Classes\sh_auto_file\ShellEx\DropHandler; ValueType: string; ValueData: {#DROP_HANDLER_GUID}; Flags: uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
151 [UninstallDelete]
152 ; Delete the built-ins.
153 Type: files; Name: {app}\bin\git-*.exe
154 Type: files; Name: {app}\libexec\git-core\git-*.exe
155 Type: files; Name: {app}\libexec\git-core\git.exe
157 ; Delete any (temporary) git-cheetah files.
158 Type: files; Name: {app}\git-cheetah\*.*
160 ; Delete any manually created shortcuts.
161 Type: files; Name: {userappdata}\Microsoft\Internet Explorer\Quick Launch\Git Bash.lnk
162 Type: files; Name: {code:GetShellFolder|desktop}\Git Bash.lnk
163 Type: files; Name: {app}\Git Bash.lnk
165 ; Delete a home directory inside the msysGit directory.
166 Type: dirifempty; Name: {app}\home\{username}
167 Type: dirifempty; Name: {app}\home
169 [Code]
170 #include "helpers.inc.iss"
171 #include "environment.inc.iss"
172 #include "putty.inc.iss"
173 #include "modules.inc.iss"
175 function CreateHardLink(lpFileName,lpExistingFileName:String;lpSecurityAttributes:Integer):Boolean;
176 #ifdef UNICODE
177 external 'CreateHardLinkW@Kernel32.dll stdcall delayload setuponly';
178 #else
179 external 'CreateHardLinkA@Kernel32.dll stdcall delayload setuponly';
180 #endif
182 function BindImageEx(Flags:DWORD;ImageName,DllPath,SymbolPath:AnsiString;StatusRoutine:Integer):Boolean;
183 external 'BindImageEx@Imagehlp.dll stdcall delayload setuponly';
185 const
186 // Git Path options.
187 GP_BashOnly = 1;
188 GP_Cmd = 2;
189 GP_CmdTools = 3;
191 // Git SSH options.
192 GS_OpenSSH = 1;
193 GS_Plink = 2;
195 // Git line ending conversion options.
196 GC_LFOnly = 1;
197 GC_CRLFAlways = 2;
198 GC_CRLFCommitAsIs = 3;
200 // BindImageEx API constants.
201 BIND_NO_BOUND_IMPORTS = $00000001;
202 BIND_NO_UPDATE = $00000002;
203 BIND_ALL_IMAGES = $00000004;
204 BIND_CACHE_IMPORT_DLLS = $00000008;
207 // Wizard page and variables for the Path options.
208 PathPage:TWizardPage;
209 RdbPath:array[GP_BashOnly..GP_CmdTools] of TRadioButton;
211 // Wizard page and variables for the SSH options.
212 PuTTYPage:TWizardPage;
213 RdbSSH:array[GS_OpenSSH..GS_Plink] of TRadioButton;
214 EdtPlink:TEdit;
216 // Wizard page and variables for the line ending conversion options.
217 CRLFPage:TWizardPage;
218 RdbCRLF:array[GC_LFOnly..GC_CRLFCommitAsIs] of TRadioButton;
220 // Wizard page and variables for the processes page.
221 SessionHandle:DWORD;
222 Processes:ProcessList;
223 ProcessesPage:TWizardPage;
224 ProcessesListBox:TListBox;
225 ProcessesRefresh,ContinueButton:TButton;
228 Specific helper functions
231 procedure BrowseForPuTTYFolder(Sender:TObject);
233 Name:String;
234 begin
235 if GetOpenFileName(
236 'Please select a Plink executable'
237 , Name
238 , ExtractFilePath(EdtPlink.Text)
239 , 'Executable Files|*.exe'
240 , 'exe'
242 then begin
243 if IsPlinkExecutable(Name) then begin
244 EdtPlink.Text:=Name;
245 RdbSSH[GS_Plink].Checked:=True;
246 end else begin
247 // This message box only gets triggered on interactive use, so it
248 // does not need to be suppressible for silent installations.
249 MsgBox('{#PLINK_PATH_ERROR_MSG}',mbError,MB_OK);
250 end;
251 end;
252 end;
254 procedure DeleteContextMenuEntries;
256 AppDir,Command,Msg:String;
257 RootKey,i:Integer;
258 Keys:TArrayOfString;
259 begin
260 AppDir:=ExpandConstant('{app}');
262 if IsAdminLoggedOn then begin
263 RootKey:=HKEY_LOCAL_MACHINE;
264 end else begin
265 RootKey:=HKEY_CURRENT_USER;
266 end;
268 SetArrayLength(Keys,4);
269 Keys[0]:='SOFTWARE\Classes\Directory\shell\git_shell';
270 Keys[1]:='SOFTWARE\Classes\Directory\Background\shell\git_shell';
271 Keys[2]:='SOFTWARE\Classes\Directory\shell\git_gui';
272 Keys[3]:='SOFTWARE\Classes\Directory\Background\shell\git_gui';
274 for i:=0 to Length(Keys)-1 do begin
275 Command:='';
276 RegQueryStringValue(RootKey,Keys[i]+'\command','',Command);
277 if Pos(AppDir,Command)>0 then begin
278 if not RegDeleteKeyIncludingSubkeys(RootKey,Keys[i]) then begin
279 Msg:='Line {#__LINE__}: Unable to remove "Git Bash / GUI Here" shell extension.';
281 // This is not a critical error, so just notify the user and continue.
282 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
283 Log(Msg);
284 end;
285 end;
286 end;
287 end;
289 procedure RefreshProcessList(Sender:TObject);
291 Version:TWindowsVersion;
292 Modules:TArrayOfString;
293 ProcsCloseRequired,ProcsCloseOptional:ProcessList;
294 i:Longint;
295 Caption:String;
296 ManualClosingRequired:Boolean;
297 begin
298 GetWindowsVersionEx(Version);
300 // Use the Restart Manager API when installing the shell extension on Windows Vista and above.
301 if Version.Major>=6 then begin
302 SetArrayLength(Modules,5);
303 Modules[0]:=ExpandConstant('{app}\bin\msys-1.0.dll');
304 Modules[1]:=ExpandConstant('{app}\bin\tcl85.dll');
305 Modules[2]:=ExpandConstant('{app}\bin\tk85.dll');
306 Modules[3]:=ExpandConstant('{app}\git-cheetah\git_shell_ext.dll');
307 Modules[4]:=ExpandConstant('{app}\git-cheetah\git_shell_ext64.dll');
308 SessionHandle:=FindProcessesUsingModules(Modules,Processes);
309 end else begin
310 SetArrayLength(Modules,3);
311 Modules[0]:=ExpandConstant('{app}\bin\msys-1.0.dll');
312 Modules[1]:=ExpandConstant('{app}\bin\tcl85.dll');
313 Modules[2]:=ExpandConstant('{app}\bin\tk85.dll');
314 SessionHandle:=FindProcessesUsingModules(Modules,ProcsCloseRequired);
316 SetArrayLength(Modules,2);
317 Modules[0]:=ExpandConstant('{app}\git-cheetah\git_shell_ext.dll');
318 Modules[1]:=ExpandConstant('{app}\git-cheetah\git_shell_ext64.dll');
319 SessionHandle:=FindProcessesUsingModules(Modules,ProcsCloseOptional) or SessionHandle;
321 // Misuse the "Restartable" flag to indicate which processes are required
322 // to be closed before setup can continue, and which just should be closed
323 // in order to make changes take effect immediately.
324 SetArrayLength(Processes,GetArrayLength(ProcsCloseRequired)+GetArrayLength(ProcsCloseOptional));
325 for i:=0 to GetArrayLength(ProcsCloseRequired)-1 do begin
326 Processes[i]:=ProcsCloseRequired[i];
327 Processes[i].Restartable:=False;
328 end;
329 for i:=0 to GetArrayLength(ProcsCloseOptional)-1 do begin
330 Processes[GetArrayLength(ProcsCloseRequired)+i]:=ProcsCloseOptional[i];
331 Processes[GetArrayLength(ProcsCloseRequired)+i].Restartable:=True;
332 end;
333 end;
335 ManualClosingRequired:=False;
337 ProcessesListBox.Items.Clear;
338 if (Sender=NIL) or (SessionHandle>0) then begin
339 for i:=0 to GetArrayLength(Processes)-1 do begin
340 Caption:=Processes[i].Name+' (PID '+IntToStr(Processes[i].ID);
341 if Processes[i].Restartable then begin
342 Caption:=Caption+', closing is optional';
343 end else begin
344 Caption:=Caption+', closing is required';
345 ManualClosingRequired:=True;
346 end;
347 Caption:=Caption+')';
348 ProcessesListBox.Items.Append(Caption);
349 end;
350 end;
352 if ContinueButton<>NIL then begin
353 ContinueButton.Enabled:=not ManualClosingRequired;
354 end;
355 end;
357 procedure SetAndMarkEnvString(Name,Value:String;Expandable:Boolean);
359 Env:TArrayOfString;
360 FileName,Msg:String;
361 begin
362 SetArrayLength(Env,1);
363 Env[0]:=Value;
365 // Try to set the variable as specified by the user.
366 if not SetEnvStrings(Name,Env,Expandable,IsAdminLoggedOn,True) then begin
367 Msg:='Line {#__LINE__}: Unable to set the '+Name+' environment variable.';
369 // This is not a critical error, so just notify the user and continue.
370 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
371 Log(Msg);
372 end else begin
373 // Mark that we have changed the variable by writing its value to a file.
374 FileName:=ExpandConstant('{app}')+'\setup.ini';
375 if not SetIniString('Environment',Name,Value,FileName) then begin
376 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
378 // This is not a critical error, so just notify the user and continue.
379 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
380 Log(Msg);
381 end;
382 end;
383 end;
385 procedure DeleteMarkedEnvString(Name:String);
387 Env:TArrayOfString;
388 FileName,Msg:String;
389 begin
390 Env:=GetEnvStrings(Name,IsAdminLoggedOn);
391 FileName:=ExpandConstant('{app}')+'\setup.ini';
393 if (GetArrayLength(Env)=1) and
394 (CompareStr(RemoveQuotes(Env[0]),GetIniString('Environment',Name,'',FileName))=0) then begin
395 if not SetEnvStrings(Name,[],False,IsAdminLoggedOn,True) then begin
396 Msg:='Line {#__LINE__}: Unable to delete the '+Name+' environment variable.';
398 // This is not a critical error, so just notify the user and continue.
399 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
400 Log(Msg);
401 end;
402 end;
403 end;
406 Setup event functions
409 procedure InitializeWizard;
411 PrevPageID:Integer;
412 LblGitBash,LblGitCmd,LblGitCmdTools,LblGitCmdToolsWarn:TLabel;
413 LblOpenSSH,LblPlink:TLabel;
414 PuTTYSessions,EnvSSH:TArrayOfString;
415 LblLFOnly,LblCRLFAlways,LblCRLFCommitAsIs:TLabel;
416 BtnPlink:TButton;
417 Data:String;
418 begin
420 PrevPageID:=wpSelectProgramGroup;
423 * Create a custom page for modifying the environment.
426 PathPage:=CreateCustomPage(
427 PrevPageID
428 , 'Adjusting your PATH environment'
429 , 'How would you like to use Git from the command line?'
431 PrevPageID:=PathPage.ID;
433 // 1st choice
434 RdbPath[GP_BashOnly]:=TRadioButton.Create(PathPage);
435 with RdbPath[GP_BashOnly] do begin
436 Parent:=PathPage.Surface;
437 Caption:='Use Git Bash only';
438 Left:=ScaleX(4);
439 Top:=ScaleY(8);
440 Width:=ScaleX(405);
441 Height:=ScaleY(17);
442 Font.Style:=[fsBold];
443 TabOrder:=0;
444 Checked:=True;
445 end;
446 LblGitBash:=TLabel.Create(PathPage);
447 with LblGitBash do begin
448 Parent:=PathPage.Surface;
449 Caption:=
450 'This is the most conservative choice if you are concerned about the stability' + #13 +
451 'of your system. Your PATH will not be modified.';
452 Left:=ScaleX(28);
453 Top:=ScaleY(32);
454 Width:=ScaleX(405);
455 Height:=ScaleY(26);
456 end;
458 // 2nd choice
459 RdbPath[GP_Cmd]:=TRadioButton.Create(PathPage);
460 with RdbPath[GP_Cmd] do begin
461 Parent:=PathPage.Surface;
462 Caption:='Run Git from the Windows Command Prompt';
463 Left:=ScaleX(4);
464 Top:=ScaleY(76);
465 Width:=ScaleX(405);
466 Height:=ScaleY(17);
467 Font.Style:=[fsBold];
468 TabOrder:=1;
469 end;
470 LblGitCmd:=TLabel.Create(PathPage);
471 with LblGitCmd do begin
472 Parent:=PathPage.Surface;
473 Caption:=
474 'This option is considered safe and no conflicts with other tools are known.' + #13 +
475 'Only Git will be added to your PATH. Use this option if you want to use Git' + #13 +
476 'from a Cygwin Prompt (make sure to not have Cygwin''s Git installed).';
477 Left:=ScaleX(28);
478 Top:=ScaleY(100);
479 Width:=ScaleX(405);
480 Height:=ScaleY(39);
481 end;
483 // 3rd choice
484 RdbPath[GP_CmdTools]:=TRadioButton.Create(PathPage);
485 with RdbPath[GP_CmdTools] do begin
486 Parent:=PathPage.Surface;
487 Caption:='Run Git and included Unix tools from the Windows Command Prompt';
488 Left:=ScaleX(4);
489 Top:=ScaleY(152);
490 Width:=ScaleX(405);
491 Height:=ScaleY(17);
492 Font.Style:=[fsBold];
493 TabOrder:=2;
494 end;
495 LblGitCmdTools:=TLabel.Create(PathPage);
496 with LblGitCmdTools do begin
497 Parent:=PathPage.Surface;
498 Caption:='Both Git and its accompanying Unix tools will be added to your PATH.';
499 Left:=ScaleX(28);
500 Top:=ScaleY(176);
501 Width:=ScaleX(405);
502 Height:=ScaleY(13);
503 end;
504 LblGitCmdToolsWarn:=TLabel.Create(PathPage);
505 with LblGitCmdToolsWarn do begin
506 Parent:=PathPage.Surface;
507 Caption:=
508 'Warning: This will override Windows tools like find.exe and' + #13 +
509 'sort.exe. Select this option only if you understand the implications.';
510 Left:=ScaleX(28);
511 Top:=ScaleY(192);
512 Width:=ScaleX(405);
513 Height:=ScaleY(26);
514 Font.Color:=255;
515 Font.Style:=[fsBold];
516 end;
518 // Restore the setting chosen during a previous install.
519 Data:=GetPreviousData('Path Option','BashOnly');
520 if Data='BashOnly' then begin
521 RdbPath[GP_BashOnly].Checked:=True;
522 end else if Data='Cmd' then begin
523 RdbPath[GP_Cmd].Checked:=True;
524 end else if Data='CmdTools' then begin
525 RdbPath[GP_CmdTools].Checked:=True;
526 end;
529 * Create a custom page for using (Tortoise)Plink instead of OpenSSH
530 * if at least one PuTTY session is found in the Registry.
533 if RegGetSubkeyNames(HKEY_CURRENT_USER,'Software\SimonTatham\PuTTY\Sessions',PuTTYSessions) and (GetArrayLength(PuTTYSessions)>0) then begin
534 PuTTYPage:=CreateCustomPage(
535 PrevPageID
536 , 'Choosing the SSH executable'
537 , 'Which Secure Shell client program would you like Git to use?'
539 PrevPageID:=PuTTYPage.ID;
541 // 1st choice
542 RdbSSH[GS_OpenSSH]:=TRadioButton.Create(PuTTYPage);
543 with RdbSSH[GS_OpenSSH] do begin
544 Parent:=PuTTYPage.Surface;
545 Caption:='Use OpenSSH';
546 Left:=ScaleX(4);
547 Top:=ScaleY(8);
548 Width:=ScaleX(405);
549 Height:=ScaleY(17);
550 Font.Style:=[fsBold];
551 TabOrder:=0;
552 Checked:=True;
553 end;
554 LblOpenSSH:=TLabel.Create(PuTTYPage);
555 with LblOpenSSH do begin
556 Parent:=PuTTYPage.Surface;
557 Caption:=
558 'This uses ssh.exe that comes with Git. The GIT_SSH and SVN_SSH' + #13 +
559 'environment variables will not be modified.';
560 Left:=ScaleX(28);
561 Top:=ScaleY(32);
562 Width:=ScaleX(405);
563 Height:=ScaleY(26);
564 end;
566 // 2nd choice
567 RdbSSH[GS_Plink]:=TRadioButton.Create(PuTTYPage);
568 with RdbSSH[GS_Plink] do begin
569 Parent:=PuTTYPage.Surface;
570 Caption:='Use (Tortoise)Plink';
571 Left:=ScaleX(4);
572 Top:=ScaleY(76);
573 Width:=ScaleX(405);
574 Height:=ScaleY(17);
575 Font.Style:=[fsBold];
576 TabOrder:=1;
577 end;
578 LblPlink:=TLabel.Create(PuTTYPage);
579 with LblPlink do begin
580 Parent:=PuTTYPage.Surface;
581 Caption:=
582 'PuTTY sessions were found in your Registry. You may specify the path' + #13 +
583 'to an existing copy of (Tortoise)Plink.exe from the TortoiseGit/SVN/CVS' + #13 +
584 'or PuTTY applications. The GIT_SSH and SVN_SSH environment' + #13 +
585 'variables will be adjusted to point to the following executable:';
586 Left:=ScaleX(28);
587 Top:=ScaleY(100);
588 Width:=ScaleX(405);
589 Height:=ScaleY(52);
590 end;
591 EdtPlink:=TEdit.Create(PuTTYPage);
592 with EdtPlink do begin
593 Parent:=PuTTYPage.Surface;
595 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
596 if (GetArrayLength(EnvSSH)=1) and IsPlinkExecutable(EnvSSH[0]) then begin
597 Text:=EnvSSH[0];
598 end;
599 if not FileExists(Text) then begin
600 Text:=GetPreviousData('Plink Path','');
601 end;
602 if not FileExists(Text) then begin
603 Text:=GuessPlinkExecutable;
604 end;
605 if not FileExists(Text) then begin
606 Text:='';
607 end;
609 Left:=ScaleX(28);
610 Top:=ScaleY(161);
611 Width:=ScaleX(316);
612 Height:=ScaleY(13);
613 end;
614 BtnPlink:=TButton.Create(PuTTYPage);
615 with BtnPlink do begin
616 Parent:=PuTTYPage.Surface;
617 Caption:='...';
618 OnClick:=@BrowseForPuTTYFolder;
619 Left:=ScaleX(348);
620 Top:=ScaleY(161);
621 Width:=ScaleX(21);
622 Height:=ScaleY(21);
623 end;
625 // Restore the setting chosen during a previous install.
626 Data:=GetPreviousData('SSH Option','OpenSSH');
627 if Data='OpenSSH' then begin
628 RdbSSH[GS_OpenSSH].Checked:=True;
629 end else if Data='Plink' then begin
630 RdbSSH[GS_Plink].Checked:=True;
631 end;
632 end else begin
633 PuTTYPage:=NIL;
634 end;
637 * Create a custom page for the core.autocrlf setting.
640 CRLFPage:=CreateCustomPage(
641 PrevPageID
642 , 'Configuring the line ending conversions'
643 , 'How should Git treat line endings in text files?'
645 PrevPageID:=CRLFPage.ID;
647 // 1st choice
648 RdbCRLF[GC_CRLFAlways]:=TRadioButton.Create(CRLFPage);
649 with RdbCRLF[GC_CRLFAlways] do begin
650 Parent:=CRLFPage.Surface;
651 Caption:='Checkout Windows-style, commit Unix-style line endings';
652 Left:=ScaleX(4);
653 Top:=ScaleY(8);
654 Width:=ScaleX(405);
655 Height:=ScaleY(17);
656 Font.Style:=[fsBold];
657 TabOrder:=0;
658 Checked:=True;
659 end;
660 LblCRLFAlways:=TLabel.Create(CRLFPage);
661 with LblCRLFAlways do begin
662 Parent:=CRLFPage.Surface;
663 Caption:=
664 'Git will convert LF to CRLF when checking out text files. When committing' + #13 +
665 'text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
666 'this is the recommended setting on Windows ("core.autocrlf" is set to "true").';
667 Left:=ScaleX(28);
668 Top:=ScaleY(32);
669 Width:=ScaleX(405);
670 Height:=ScaleY(47);
671 end;
673 // 2nd choice
674 RdbCRLF[GC_LFOnly]:=TRadioButton.Create(CRLFPage);
675 with RdbCRLF[GC_LFOnly] do begin
676 Parent:=CRLFPage.Surface;
677 Caption:='Checkout as-is, commit Unix-style line endings';
678 Left:=ScaleX(4);
679 Top:=ScaleY(80);
680 Width:=ScaleX(405);
681 Height:=ScaleY(17);
682 Font.Style:=[fsBold];
683 TabOrder:=1;
684 Checked:=False;
685 end;
686 LblLFOnly:=TLabel.Create(CRLFPage);
687 with LblLFOnly do begin
688 Parent:=CRLFPage.Surface;
689 Caption:=
690 'Git will not perform any conversion when checking out text files. When' + #13 +
691 'committing text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
692 'this is the recommended setting on Unix ("core.autocrlf" is set to "input").';
693 Left:=ScaleX(28);
694 Top:=ScaleY(104);
695 Width:=ScaleX(405);
696 Height:=ScaleY(47);
697 end;
699 // 3rd choice
700 RdbCRLF[GC_CRLFCommitAsIs]:=TRadioButton.Create(CRLFPage);
701 with RdbCRLF[GC_CRLFCommitAsIs] do begin
702 Parent:=CRLFPage.Surface;
703 Caption:='Checkout as-is, commit as-is';
704 Left:=ScaleX(4);
705 Top:=ScaleY(152);
706 Width:=ScaleX(405);
707 Height:=ScaleY(17);
708 Font.Style:=[fsBold];
709 TabOrder:=2;
710 Checked:=False;
711 end;
712 LblCRLFCommitAsIs:=TLabel.Create(CRLFPage);
713 with LblCRLFCommitAsIs do begin
714 Parent:=CRLFPage.Surface;
715 Caption:=
716 'Git will not perform any conversions when checking out or committing' + #13 +
717 'text files. Choosing this option is not recommended for cross-platform' + #13 +
718 'projects ("core.autocrlf" is set to "false").';
719 Left:=ScaleX(28);
720 Top:=ScaleY(176);
721 Width:=ScaleX(405);
722 Height:=ScaleY(47);
723 end;
725 // Restore the setting chosen during a previous install.
726 Data:=GetPreviousData('CRLF Option','CRLFAlways');
727 if Data='LFOnly' then begin
728 RdbCRLF[GC_LFOnly].Checked:=True;
729 end else if Data='CRLFAlways' then begin
730 RdbCRLF[GC_CRLFAlways].Checked:=True;
731 end else if Data='CRLFCommitAsIs' then begin
732 RdbCRLF[GC_CRLFCommitAsIs].Checked:=True;
733 end;
736 * Create a custom page for finding the processes that lock a module.
739 ProcessesPage:=CreateCustomPage(
740 wpPreparing
741 , 'Replacing in-use files'
742 , 'The following applications use files that need to be replaced, please close them.'
745 ProcessesListBox:=TListBox.Create(ProcessesPage);
746 with ProcessesListBox do begin
747 Parent:=ProcessesPage.Surface;
748 Width:=ProcessesPage.SurfaceWidth;
749 Height:=ProcessesPage.SurfaceHeight-ScaleY(8);
750 end;
752 ProcessesRefresh:=TNewButton.Create(WizardForm);
753 with ProcessesRefresh do begin
754 Parent:=WizardForm;
755 Width:=WizardForm.CancelButton.Width;
756 Height:=WizardForm.CancelButton.Height;
757 Top:=WizardForm.CancelButton.Top;
758 Left:=WizardForm.ClientWidth-(WizardForm.CancelButton.Left+WizardForm.CancelButton.Width);
759 Caption:='&Refresh';
760 OnClick:=@RefreshProcessList;
761 end;
763 // This button is only used by the uninstaller.
764 ContinueButton:=NIL;
766 // Initially hide the Refresh button, show it when the process page becomes current.
767 ProcessesRefresh.Hide;
768 end;
770 function ShouldSkipPage(PageID:Integer):Boolean;
771 begin
772 if (ProcessesPage<>NIL) and (PageID=ProcessesPage.ID) then begin
773 // This page is only reached forward (by pressing "Next", never by pressing "Back").
774 RefreshProcessList(NIL);
775 Result:=(GetArrayLength(Processes)=0);
776 end else begin
777 Result:=False;
778 end;
779 end;
781 procedure CurPageChanged(CurPageID:Integer);
782 begin
783 if CurPageID=wpInfoBefore then begin
784 if WizardForm.NextButton.Enabled then begin
785 // By default, do not show a blinking cursor for InfoBeforeFile.
786 WizardForm.ActiveControl:=WizardForm.NextButton;
787 end;
788 end else if CurPageID=wpSelectDir then begin
789 if not IsDirWritable(WizardDirValue) then begin
790 // If the default directory is not writable, choose another default that most likely is.
791 // This will be checked later again when the user clicks "Next".
792 WizardForm.DirEdit.Text:=ExpandConstant('{userpf}\{#APP_NAME}');
793 end;
794 end else if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin
795 // Show the "Refresh" button only on the processes page.
796 ProcessesRefresh.Show;
797 end else begin
798 ProcessesRefresh.Hide;
799 end;
800 end;
802 function NextButtonClick(CurPageID:Integer):Boolean;
804 i:Integer;
805 Version:TWindowsVersion;
806 begin
807 // On a silent install, if your NextButtonClick function returns False
808 // prior to installation starting, Setup will exit automatically.
809 Result:=True;
811 if CurPageID=wpSelectDir then begin
812 if not IsDirWritable(WizardDirValue) then begin
813 SuppressibleMsgBox(
814 'The specified installation directory does not seem to be writable. ' +
815 + 'Please choose another directory or restart setup as a user with sufficient permissions.'
816 , mbCriticalError
817 , MB_OK
818 , IDOK
820 Result:=False;
821 Exit;
822 end;
823 end;
825 if (PuTTYPage<>NIL) and (CurPageID=PuTTYPage.ID) then begin
826 Result:=RdbSSH[GS_OpenSSH].Checked or
827 (RdbSSH[GS_Plink].Checked and FileExists(EdtPlink.Text));
828 if not Result then begin
829 SuppressibleMsgBox('{#PLINK_PATH_ERROR_MSG}',mbError,MB_OK,IDOK);
830 end;
831 end else if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin
832 // It would have been nicer to just disable the "Next" button, but the
833 // WizardForm exports the button just read-only.
834 for i:=0 to GetArrayLength(Processes)-1 do begin
835 if not Processes[i].Restartable then begin
836 SuppressibleMsgBox(
837 'Setup cannot continue until you close at least those applications in the list that are marked as "closing is required".'
838 , mbCriticalError
839 , MB_OK
840 , IDOK
842 Result:=False;
843 Exit;
844 end;
845 end;
847 Result:=(GetArrayLength(Processes)=0);
849 if not Result then begin
850 GetWindowsVersionEx(Version);
851 if Version.Major>=6 then begin
852 Result:=(SuppressibleMsgBox(
853 'If you continue without closing the listed applications they will be closed and restarted automatically.' + #13 + #13 +
854 'Are you sure you want to continue?'
855 , mbConfirmation
856 , MB_YESNO
857 , IDNO
858 )=IDYES);
859 end else begin
860 Result:=(SuppressibleMsgBox(
861 'If you continue without closing the listed applications you will need to log off and on again before changes take effect.' + #13 + #13 +
862 'Are you sure you want to continue anyway?'
863 , mbConfirmation
864 , MB_YESNO
865 , IDNO
866 )=IDYES);
867 end;
868 end;
869 end;
870 end;
872 procedure CurStepChanged(CurStep:TSetupStep);
874 AppDir,DllPath,FileName,TempName,Cmd,Msg:String;
875 BuiltIns,ImageNames,EnvPath,EnvHome:TArrayOfString;
876 Count,i:Longint;
877 LinkCreated:Boolean;
878 FindRec:TFindRec;
879 RootKey:Integer;
880 Version:TWindowsVersion;
881 begin
882 if CurStep=ssInstall then begin
883 // Shutdown locking processes just before the actual installation starts.
884 if SessionHandle>0 then try
885 RmShutdown(SessionHandle,RmShutdownOnlyRegistered,0);
886 except
887 Log('Line {#__LINE__}: RmShutdown not supported.');
888 end;
890 Exit;
891 end;
893 // Make sure the code below is only executed just after the actual installation finishes.
894 if CurStep<>ssPostInstall then begin
895 Exit;
896 end;
898 AppDir:=ExpandConstant('{app}');
901 Bind the imported function addresses
905 DllPath:=ExpandConstant('{app}\bin;{sys}');
907 // Load the list of images from a text file.
908 FileName:=AppDir+'\{#APP_BINDIMAGE}';
909 if LoadStringsFromFile(FileName,ImageNames) then begin
910 Count:=GetArrayLength(ImageNames)-1;
911 for i:=0 to Count do begin
912 FileName:=AppDir+'\'+ImageNames[i];
913 if not BindImageEx(BIND_NO_BOUND_IMPORTS or BIND_CACHE_IMPORT_DLLS,FileName,DllPath,'',0) then begin
914 Log('Line {#__LINE__}: Error calling BindImageEx for "'+FileName+'".');
915 end;
916 end;
917 end;
918 except
919 Log('Line {#__LINE__}: An exception occurred while calling BindImageEx.');
920 end;
923 Create the built-ins
926 // Load the built-ins from a text file.
927 FileName:=AppDir+'\{#APP_BUILTINS}';
928 if LoadStringsFromFile(FileName,BuiltIns) then begin
929 Count:=GetArrayLength(BuiltIns)-1;
931 // Delete those scripts from "bin" which have been replaced by built-ins in "libexec\git-core".
932 for i:=0 to Count do begin
933 FileName:=AppDir+'\bin\'+ChangeFileExt(ExtractFileName(BuiltIns[i]),'');
934 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
935 Log('Line {#__LINE__}: Unable to delete script "'+FileName+'", ignoring.');
936 end;
937 end;
939 // Create built-ins as aliases for git.exe.
940 for i:=0 to Count do begin
941 FileName:=AppDir+'\'+BuiltIns[i];
943 // Delete any existing built-in.
944 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
945 Log('Line {#__LINE__}: Unable to delete existing built-in "'+FileName+'", skipping.');
946 continue;
947 end;
950 // This will throw an exception on pre-Win2k systems.
951 LinkCreated:=CreateHardLink(FileName,AppDir+'\bin\git.exe',0);
952 except
953 LinkCreated:=False;
954 Log('Line {#__LINE__}: Creating hardlink "'+FileName+'" failed, will try a copy.');
955 end;
957 if not LinkCreated then begin
958 if not FileCopy(AppDir+'\bin\git.exe',FileName,False) then begin
959 Log('Line {#__LINE__}: Creating copy "'+FileName+'" failed, giving up.');
960 // This is not a critical error, Git could basically be used without the
961 // aliases for built-ins, so we continue.
962 end;
963 end;
964 end;
966 // Delete any duplicate files in case we are updating from a non-libexec to a libexec directory layout.
967 if FindFirst(AppDir+'\libexec\git-core\*',FindRec) then begin
968 repeat
969 if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY)=0 then begin
970 FileName:=AppDir+'\bin\'+FindRec.name;
971 if (Pos(FindRec.name,'git.exe')<>1) and FileExists(FileName) and (not DeleteFile(FileName)) then begin
972 Log('Line {#__LINE__}: Unable to delete dupe "'+FileName+'", ignoring.');
973 end;
974 end;
975 until not FindNext(FindRec);
976 FindClose(FindRec);
977 end;
978 end else begin
979 Msg:='Line {#__LINE__}: Unable to read file "{#APP_BUILTINS}".';
981 // This is in fact a critical error, but "Abort" does not work during ssPostInstall anymore and
982 // we have no other way of aborting the installation, so just notify the user and continue.
983 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
984 Log(Msg);
985 end;
988 Adapt core.autocrlf
991 if RdbCRLF[GC_LFOnly].checked then begin
992 Cmd:='core.autocrlf input';
993 end else if RdbCRLF[GC_CRLFAlways].checked then begin
994 Cmd:='core.autocrlf true';
995 end else begin
996 Cmd:='core.autocrlf false';
997 end;
998 if not Exec(AppDir + '\bin\git.exe', 'config -f gitconfig ' + Cmd,
999 AppDir + '\etc', SW_HIDE, ewWaitUntilTerminated, i) then begin
1000 Msg:='Unable to configure the line ending conversion: ' + Cmd;
1002 // This is not a critical error, so just notify the user and continue.
1003 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1004 Log(Msg);
1005 end;
1008 Modify the environment
1010 This must happen no later than ssPostInstall to make
1011 "ChangesEnvironment=yes" not happend before the change!
1014 // Delete GIT_SSH and SVN_SSH if a previous installation set them (this is required for the GS_OpenSSH case).
1015 DeleteMarkedEnvString('GIT_SSH');
1016 DeleteMarkedEnvString('SVN_SSH');
1018 if (PuTTYPage<>NIL) and RdbSSH[GS_Plink].Checked then begin
1019 SetAndMarkEnvString('GIT_SSH',EdtPlink.Text,True);
1020 SetAndMarkEnvString('SVN_SSH',EdtPlink.Text,True);
1021 end;
1023 // Get the current user's directories in PATH.
1024 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
1026 // First, remove the installation directory from PATH in any case.
1027 for i:=0 to GetArrayLength(EnvPath)-1 do begin
1028 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
1029 EnvPath[i]:='';
1030 end;
1031 end;
1033 // Delete HOME if a previous installation modified it.
1034 DeleteMarkedEnvString('HOME');
1036 // Modify the PATH variable as requested by the user.
1037 if RdbPath[GP_Cmd].Checked or RdbPath[GP_CmdTools].Checked then begin
1038 i:=GetArrayLength(EnvPath);
1039 SetArrayLength(EnvPath,i+1);
1041 // List \cmd before \bin so \cmd has higher priority and programs in
1042 // there will be called in favor of those in \bin.
1043 EnvPath[i]:=AppDir+'\cmd';
1045 if RdbPath[GP_CmdTools].Checked then begin
1046 SetArrayLength(EnvPath,i+2);
1047 EnvPath[i+1]:=AppDir+'\bin';
1049 // Set HOME for the Windows Command Prompt, but only if it has not been set manually before.
1050 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
1051 i:=GetArrayLength(EnvHome);
1052 if (i=0) or ((i=1) and (Length(EnvHome[0])=0)) then begin
1053 SetAndMarkEnvString('HOME','%HOMEDRIVE%%HOMEPATH%',True);
1054 end;
1055 end;
1056 end;
1058 // Set the current user's PATH directories.
1059 if not SetEnvStrings('PATH',EnvPath,True,IsAdminLoggedOn,True) then begin
1060 Msg:='Line {#__LINE__}: Unable to set the PATH environment variable.';
1062 // This is not a critical error, so just notify the user and continue.
1063 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1064 Log(Msg);
1065 end;
1068 Create shortcuts that need to be created regardless of the "Don't create a Start Menu folder" toggle
1071 GetWindowsVersionEx(Version);
1072 if Version.Major<6 then begin
1073 Cmd:=ExpandConstant('{syswow64}\cmd.exe');
1074 TempName:='/c ""'+AppDir+'\bin\sh.exe" --login -i"';
1075 end else begin
1076 Cmd:=AppDir+'\bin\sh.exe';
1077 TempName:='--login -i';
1078 end;
1079 FileName:=AppDir+'\etc\git.ico';
1081 if IsComponentSelected('icons\quicklaunch') then begin
1082 CreateShellLink(
1083 ExpandConstant('{userappdata}\Microsoft\Internet Explorer\Quick Launch\Git Bash.lnk')
1084 , 'Git Bash'
1085 , Cmd
1086 , TempName
1087 , '%HOMEDRIVE%%HOMEPATH%'
1088 , FileName
1090 , SW_SHOWNORMAL
1092 end;
1094 if IsComponentSelected('icons\desktop') then begin
1095 CreateShellLink(
1096 GetShellFolder('desktop')+'\Git Bash.lnk'
1097 , 'Git Bash'
1098 , Cmd
1099 , TempName
1100 , '%HOMEDRIVE%%HOMEPATH%'
1101 , FileName
1103 , SW_SHOWNORMAL
1105 end;
1108 Create the Windows Explorer integrations
1111 if IsAdminLoggedOn then begin
1112 RootKey:=HKEY_LOCAL_MACHINE;
1113 end else begin
1114 RootKey:=HKEY_CURRENT_USER;
1115 end;
1117 if IsComponentSelected('ext\reg\shellhere') then begin
1118 Msg:='Git Ba&sh Here';
1119 Cmd:='"'+ExpandConstant('{syswow64}')+'\wscript" "'+AppDir+'\Git Bash.vbs" "%1"';
1120 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell','',Msg)) or
1121 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','',Cmd)) or
1122 (StringChangeEx(Cmd,'%1','%v',false)<>1) or
1123 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\Background\shell\git_shell','',Msg)) or
1124 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\Background\shell\git_shell\command','',Cmd)) then
1125 begin
1126 Msg:='Line {#__LINE__}: Unable to create "Git Bash Here" shell extension.';
1128 // This is not a critical error, so just notify the user and continue.
1129 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1130 Log(Msg);
1131 end;
1132 end;
1134 if IsComponentSelected('ext\reg\guihere') then begin
1135 Msg:='Git &GUI Here';
1136 Cmd:='"'+AppDir+'\bin\wish.exe" "'+AppDir+'\libexec\git-core\git-gui" "--working-dir" "%1"';
1137 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui','',Msg)) or
1138 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui\command','',Cmd)) or
1139 (StringChangeEx(Cmd,'%1','%v',false)<>1) or
1140 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\Background\shell\git_gui','',Msg)) or
1141 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\Background\shell\git_gui\command','',Cmd))
1142 then begin
1143 Msg:='Line {#__LINE__}: Unable to create "Git GUI Here" shell extension.';
1145 // This is not a critical error, so just notify the user and continue.
1146 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1147 Log(Msg);
1148 end;
1149 end;
1151 // It is either the Registry-based context menu entries, or the shell extension.
1152 if IsComponentSelected('ext\cheetah') then begin
1153 DeleteContextMenuEntries;
1155 if isWin64 then begin
1156 FileName:=AppDir+'\git-cheetah\git_shell_ext64.dll';
1157 end else begin
1158 FileName:=AppDir+'\git-cheetah\git_shell_ext.dll';
1159 end;
1160 if not ReplaceInUseFile(FileName,FileName+'.new',True,Msg) then begin
1161 // This is in fact a critical error, but "Abort" does not work during ssPostInstall anymore and
1162 // we have no other way of aborting the installation, so just notify the user and continue.
1163 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1164 Log('Line {#__LINE__}: Replacing file "'+FileName+'" failed.');
1165 end;
1166 end;
1169 Restart any processes that were shut down via the Restart Manager
1172 if SessionHandle>0 then try
1173 RmRestart(SessionHandle,0,0);
1174 RmEndSession(SessionHandle);
1175 except
1176 Log('Line {#__LINE__}: RmRestart not supported.');
1177 end;
1178 end;
1180 procedure RegisterPreviousData(PreviousDataKey:Integer);
1182 Data:String;
1183 begin
1184 // Git Path options.
1185 Data:='';
1186 if RdbPath[GP_BashOnly].Checked then begin
1187 Data:='BashOnly';
1188 end else if RdbPath[GP_Cmd].Checked then begin
1189 Data:='Cmd';
1190 end else if RdbPath[GP_CmdTools].Checked then begin
1191 Data:='CmdTools';
1192 end;
1193 SetPreviousData(PreviousDataKey,'Path Option',Data);
1195 // Git SSH options.
1196 Data:='';
1197 if (PuTTYPage=NIL) or RdbSSH[GS_OpenSSH].Checked then begin
1198 Data:='OpenSSH';
1199 end else if RdbSSH[GS_Plink].Checked then begin
1200 Data:='Plink';
1201 SetPreviousData(PreviousDataKey,'Plink Path',EdtPlink.Text);
1202 end;
1203 SetPreviousData(PreviousDataKey,'SSH Option',Data);
1205 // Line ending conversion options.
1206 Data:='';
1207 if RdbCRLF[GC_LFOnly].Checked then begin
1208 Data:='LFOnly';
1209 end else if RdbCRLF[GC_CRLFAlways].Checked then begin
1210 Data:='CRLFAlways';
1211 end else if RdbCRLF[GC_CRLFCommitAsIs].Checked then begin
1212 Data:='CRLFCommitAsIs';
1213 end;
1214 SetPreviousData(PreviousDataKey,'CRLF Option',Data);
1215 end;
1218 Uninstall event functions
1221 function InitializeUninstall:Boolean;
1223 Form:TSetupForm;
1224 Info:TLabel;
1225 ExitButton,RefreshButton:TButton;
1226 begin
1227 Result:=True;
1229 Form:=CreateCustomForm;
1231 Form.Caption:='Git Uninstall: Removing in-use files';
1232 Form.ClientWidth:=ScaleX(500);
1233 Form.ClientHeight:=ScaleY(256);
1234 Form.Center;
1236 Info:=TLabel.Create(Form);
1237 with Info do begin
1238 Parent:=Form;
1239 Left:=ScaleX(11);
1240 Top:=ScaleY(11);
1241 AutoSize:=True;
1242 Caption:='The following applications use files that need to be removed, please close them.';
1243 end;
1245 ContinueButton:=TButton.Create(Form);
1246 with ContinueButton do begin
1247 Parent:=Form;
1248 Left:=Form.ClientWidth-ScaleX(75+10);
1249 Top:=Form.ClientHeight-ScaleY(23+10);
1250 Width:=ScaleX(75);
1251 Height:=ScaleY(23);
1252 Caption:='Continue';
1253 ModalResult:=mrOk;
1254 end;
1256 ExitButton:=TButton.Create(Form);
1257 with ExitButton do begin
1258 Parent:=Form;
1259 Left:=ContinueButton.Left-ScaleX(75+6);
1260 Top:=ContinueButton.Top;
1261 Width:=ScaleX(75);
1262 Height:=ScaleY(23);
1263 Caption:='Exit';
1264 ModalResult:=mrCancel;
1265 Cancel:=True;
1266 end;
1268 RefreshButton:=TButton.Create(Form);
1269 with RefreshButton do begin
1270 Parent:=Form;
1271 Left:=ScaleX(10);
1272 Top:=ExitButton.Top;
1273 Width:=ScaleX(75);
1274 Height:=ScaleY(23);
1275 Caption:='Refresh';
1276 OnClick:=@RefreshProcessList;
1277 end;
1279 ProcessesListBox:=TListBox.Create(Form);
1280 with ProcessesListBox do begin
1281 Parent:=Form;
1282 Left:=ScaleX(11);
1283 Top:=Info.Top+Info.Height+11;
1284 Width:=Form.ClientWidth-ScaleX(11*2);
1285 Height:=ContinueButton.Top-ScaleY(11*4);
1286 end;
1288 Form.ActiveControl:=ContinueButton;
1290 RefreshProcessList(NIL);
1291 if GetArrayLength(Processes)>0 then begin
1292 // Now that these dialogs are going to be shown, we should probably
1293 // disable the "Are you sure to remove Git?" confirmation dialog, but
1294 // unfortunately that is not possible with Inno Setup currently.
1295 Result:=(Form.ShowModal()=mrOk);
1297 // Note: The number of processes might have changed during a refresh.
1298 if Result and (GetArrayLength(Processes)>0) then begin
1299 Result:=(SuppressibleMsgBox(
1300 'If you continue without closing the listed applications, you will need to log off and on again to remove some files manually.' + #13 + #13 +
1301 'Are you sure you want to continue anyway?'
1302 , mbConfirmation
1303 , MB_YESNO
1304 , IDNO
1305 )=IDYES);
1306 end;
1307 end;
1308 finally
1309 Form.free;
1310 end;
1311 end;
1313 // PreUninstall
1315 // Even though the name of this function suggests otherwise most of the
1316 // code below is only executed right before the actual uninstallation.
1317 // This happens because of the if-guard right in the beginning of this
1318 // function.
1319 procedure CurUninstallStepChanged(CurUninstallStep:TUninstallStep);
1321 AppDir,FileName,Msg:String;
1322 EnvPath:TArrayOfString;
1323 i:Longint;
1324 begin
1325 if CurUninstallStep<>usUninstall then begin
1326 Exit;
1327 end;
1329 // Reset the console font (the FontType is reset in the Registry section).
1330 if IsComponentInstalled('consolefont') then begin
1331 if SuppressibleMsgBox('Do you want to revert the TrueType font setting for all console windows?',mbConfirmation,MB_YESNO,IDYES)=IDYES then begin
1332 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontFamily',0);
1333 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontSize',0);
1334 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontWeight',0);
1335 end;
1336 end;
1339 Modify the environment
1341 This must happen no later than usUninstall to make
1342 "ChangesEnvironment=yes" not happend before the change!
1345 AppDir:=ExpandConstant('{app}');
1346 FileName:=AppDir+'\setup.ini';
1348 // Delete the current user's GIT_SSH and SVN_SSH if we set it.
1349 DeleteMarkedEnvString('GIT_SSH');
1350 DeleteMarkedEnvString('SVN_SSH');
1352 // Get the current user's directories in PATH.
1353 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
1355 // Remove the installation directory from PATH in any case, even if it
1356 // was not added by the installer.
1357 for i:=0 to GetArrayLength(EnvPath)-1 do begin
1358 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
1359 EnvPath[i]:='';
1360 end;
1361 end;
1363 // Reset the current user's directories in PATH.
1364 if not SetEnvStrings('PATH',EnvPath,True,IsAdminLoggedOn,True) then begin
1365 Msg:='Line {#__LINE__}: Unable to revert any possible changes to PATH.';
1367 // This is not a critical error, so just notify the user and continue.
1368 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1369 Log(Msg);
1370 end;
1372 // Reset the current user's HOME if we modified it.
1373 DeleteMarkedEnvString('HOME');
1375 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
1376 Msg:='Line {#__LINE__}: Unable to delete file "'+FileName+'".';
1378 // This is not a critical error, so just notify the user and continue.
1379 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1380 Log(Msg);
1381 end;
1384 Delete the Windows Explorer integrations
1387 DeleteContextMenuEntries;
1389 if isWin64 then begin
1390 FileName:=AppDir+'\git-cheetah\git_shell_ext64.dll';
1391 end else begin
1392 FileName:=AppDir+'\git-cheetah\git_shell_ext.dll';
1393 end;
1394 if FileExists(FileName) then begin
1395 if not UnregisterServer(Is64BitInstallMode,FileName,False) then begin
1396 Msg:='Line {#__LINE__}: Unable to unregister file "'+FileName+'". Please do it manually by running "regsvr32 /u '+ExtractFileName(FileName)+'".';
1398 // This is not a critical error, so just notify the user and continue.
1399 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1400 Log(Msg);
1401 end;
1403 if not DeleteFile(FileName) then begin
1404 Msg:='Line {#__LINE__}: Unable to delete file "'+FileName+'". Please do it manually after logging off and on again.';
1406 // This is not a critical error, so just notify the user and continue.
1407 SuppressibleMsgBox(Msg,mbError,MB_OK,IDOK);
1408 Log(Msg);
1409 end;
1410 end;
1411 end;