Installer: Use BindImageEx to potentially speed-up loading of DLLs
[msysgit.git] / share / WinGit / install.iss
blob965c6766ada5cb2984a6a5132441f92f9a56ebc6
1 #define APP_NAME 'Git'
2 #define APP_VERSION '%APPVERSION%'
3 #define APP_URL 'http://msysgit.googlecode.com/'
4 #define APP_BUILTINS 'etc\fileList-builtins.txt'
5 #define APP_BINDIMAGE 'etc\fileList-bindimage.txt'
7 #define COMP_CONSOLE_FONT 'Use a TrueType font in all console windows (not only for Git Bash)'
9 #define DROP_HANDLER_GUID '{{86C86720-42A0-1069-A2E8-08002B30309D}}'
11 [Setup]
12 ; Compiler-related
13 Compression=lzma2/ultra
14 LZMAUseSeparateProcess=yes
15 OutputBaseFilename={#APP_NAME+'-'+APP_VERSION}
16 OutputDir={#GetEnv('USERPROFILE')}
17 SolidCompression=yes
19 ; Uncomment the line below to be able to compile the script from within the IDE.
20 ;SourceDir={#GetEnv('TEMP')}\WinGit
22 ; Installer-related
23 AllowNoIcons=yes
24 AppName={#APP_NAME}
25 AppPublisherURL={#APP_URL}
26 AppVersion={#APP_VERSION}
27 ChangesEnvironment=yes
28 DefaultDirName={pf}\{#APP_NAME}
29 DefaultGroupName={#APP_NAME}
30 DisableReadyPage=yes
31 InfoBeforeFile=gpl-2.0.rtf
32 PrivilegesRequired=none
33 UninstallDisplayIcon=etc\git.ico
35 ; Cosmetic
36 SetupIconFile=etc\git.ico
37 WizardImageBackColor=clWhite
38 WizardImageStretch=no
39 WizardImageFile=git.bmp
40 WizardSmallImageFile=gitsmall.bmp
42 [Types]
43 ; Define a dummy type to avoid getting the default ones.
44 Name: custom; Description: Custom installation; Flags: iscustom
46 [Components]
47 Name: icons; Description: Additional icons; Types: custom
48 Name: icons\quicklaunch; Description: In the Quick Launch; Types: custom
49 Name: icons\desktop; Description: On the Desktop; Types: custom
50 Name: ext; Description: Windows Explorer integration; Types: custom
51 Name: ext\reg; Description: Context menu entries; Flags: exclusive; Types: custom
52 Name: ext\reg\shellhere; Description: Git Bash Here; Types: custom
53 Name: ext\reg\guihere; Description: Git GUI Here; Types: custom
54 Name: ext\cheetah; Description: git-cheetah shell extension (32-bit only); Flags: exclusive; Types: custom
55 Name: assoc; Description: Associate .git* configuration files with the default text editor; Types: custom
56 Name: assoc_sh; Description: Associate .sh files to be run with Bash; Types: custom
57 Name: consolefont; Description: {#COMP_CONSOLE_FONT}; Types: custom
59 [Files]
60 ; Install files that might be in use during setup under a different name.
61 Source: git-cheetah\git_shell_ext.dll; DestDir: {app}\git-cheetah; DestName: git_shell_ext.dll.new; Flags: replacesameversion
63 Source: *; DestDir: {app}; Excludes: \*.bmp, gpl-2.0.rtf, \*.iss, \tmp.*, \bin\*install*, \git-cheetah\git_shell_ext.dll; Flags: recursesubdirs replacesameversion
64 Source: ReleaseNotes.rtf; DestDir: {app}; Flags: isreadme replacesameversion
66 [Icons]
67 Name: {group}\Git GUI; Filename: {app}\bin\wish.exe; Parameters: """{app}\libexec\git-core\git-gui"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico
68 Name: {group}\Git Bash; Filename: {syswow64}\cmd.exe; Parameters: "/c """"{app}\bin\sh.exe"" --login -i"""; WorkingDir: %HOMEDRIVE%%HOMEPATH%; IconFilename: {app}\etc\git.ico
70 [Messages]
71 BeveledLabel={#APP_URL}
72 SetupAppTitle={#APP_NAME} Setup
73 SetupWindowTitle={#APP_NAME} Setup
75 [Registry]
76 ; There is no "Console" key in HKLM.
77 Root: HKCU; Subkey: Console; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: uninsclearvalue; Components: consolefont
78 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Components: consolefont
79 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Components: consolefont
80 Root: HKCU; Subkey: Console; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Components: consolefont
82 Root: HKCU; Subkey: Console\Git Bash; ValueType: string; ValueName: FaceName; ValueData: Lucida Console; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
83 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontFamily; ValueData: $00000036; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
84 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontSize; ValueData: $000e0000; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
85 Root: HKCU; Subkey: Console\Git Bash; ValueType: dword; ValueName: FontWeight; ValueData: $00000190; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty
87 ; Note that we write the Registry values below either to HKLM or to HKCU depending on whether the user running the installer
88 ; is a member of the local Administrators group or not (see the "Check" argument).
90 ; File associations for configuration files that may be contained in a repository (so this does not include ".gitconfig").
91 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
92 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
93 Root: HKLM; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
94 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
95 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
96 Root: HKCU; Subkey: Software\Classes\.gitattributes; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
98 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
99 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
100 Root: HKLM; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
101 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
102 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
103 Root: HKCU; Subkey: Software\Classes\.gitignore; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
105 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
106 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
107 Root: HKLM; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: IsAdminLoggedOn; Components: assoc
108 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueData: txtfile; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
109 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: Content Type; ValueData: text/plain; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
110 Root: HKCU; Subkey: Software\Classes\.gitmodules; ValueType: string; ValueName: PerceivedType; ValueData: text; Flags: createvalueifdoesntexist uninsdeletevalue uninsdeletekeyifempty; Check: not IsAdminLoggedOn; Components: assoc
112 ; Associate .sh extension with sh.exe so those files are double-clickable,
113 ; startable from cmd.exe, and when files are dropped on them they are passed
114 ; as arguments to the script.
116 ; Install under HKEY_LOCAL_MACHINE if an administrator is installing.
117 Root: HKLM; Subkey: Software\Classes\.sh; ValueType: string; ValueData: sh_auto_file; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
118 Root: HKLM; Subkey: Software\Classes\sh_auto_file; ValueType: string; ValueData: "Shell Script"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
119 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
120 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
121 Root: HKLM; Subkey: Software\Classes\sh_auto_file\ShellEx\DropHandler; ValueType: string; ValueData: {#DROP_HANDLER_GUID}; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: IsAdminLoggedOn; Components: assoc_sh
123 ; Install under HKEY_CURRENT_USER if a non-administrator is installing.
124 Root: HKCU; Subkey: Software\Classes\.sh; ValueType: string; ValueData: sh_auto_file; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
125 Root: HKCU; Subkey: Software\Classes\sh_auto_file; ValueType: string; ValueData: "Shell Script"; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
126 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
127 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
128 Root: HKCU; Subkey: Software\Classes\sh_auto_file\ShellEx\DropHandler; ValueType: string; ValueData: {#DROP_HANDLER_GUID}; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue; Check: not IsAdminLoggedOn; Components: assoc_sh
130 [UninstallDelete]
131 ; Delete the built-ins.
132 Type: files; Name: {app}\bin\git-*.exe
133 Type: files; Name: {app}\libexec\git-core\git-*.exe
134 Type: files; Name: {app}\libexec\git-core\git.exe
136 ; Delete any (temporary) git-cheetah files.
137 Type: files; Name: {app}\git-cheetah\*.*
139 ; Delete any manually created shortcuts.
140 Type: files; Name: {userappdata}\Microsoft\Internet Explorer\Quick Launch\Git Bash.lnk
141 Type: files; Name: {code:GetShellFolder|desktop}\Git Bash.lnk
142 Type: files; Name: {app}\Git Bash.lnk
144 ; Delete a home directory inside the msysGit directory.
145 Type: dirifempty; Name: {app}\home\{username}
146 Type: dirifempty; Name: {app}\home
148 [Code]
149 #include "helpers.inc.iss"
150 #include "putty.inc.iss"
151 #include "modules.inc.iss"
153 function CreateHardLink(lpFileName,lpExistingFileName:String;lpSecurityAttributes:Integer):Boolean;
154 #ifdef UNICODE
155 external 'CreateHardLinkW@Kernel32.dll stdcall delayload setuponly';
156 #else
157 external 'CreateHardLinkA@Kernel32.dll stdcall delayload setuponly';
158 #endif
160 function CreateSymbolicLink(lpSymlinkFileName,lpTargetFileName:String;dwFlags:DWORD):Boolean;
161 #ifdef UNICODE
162 external 'CreateSymbolicLinkW@Kernel32.dll stdcall delayload setuponly';
163 #else
164 external 'CreateSymbolicLinkA@Kernel32.dll stdcall delayload setuponly';
165 #endif
167 function BindImageEx(Flags:DWORD;ImageName,DllPath,SymbolPath:AnsiString;StatusRoutine:Integer):Boolean;
168 external 'BindImageEx@Imagehlp.dll stdcall delayload setuponly';
170 const
171 // Git Path options.
172 GP_BashOnly = 1;
173 GP_Cmd = 2;
174 GP_CmdTools = 3;
176 // Git SSH options.
177 GS_OpenSSH = 1;
178 GS_Plink = 2;
180 // Git line ending conversion options.
181 GC_LFOnly = 1;
182 GC_CRLFAlways = 2;
183 GC_CRLFCommitAsIs = 3;
185 // BindImageEx API constants.
186 BIND_NO_BOUND_IMPORTS = $00000001;
187 BIND_NO_UPDATE = $00000002;
188 BIND_ALL_IMAGES = $00000004;
189 BIND_CACHE_IMPORT_DLLS = $00000008;
192 // Wizard page and variables for the Path options.
193 PathPage:TWizardPage;
194 RdbPath:array[GP_BashOnly..GP_CmdTools] of TRadioButton;
196 // Wizard page and variables for the SSH options.
197 PuTTYPage:TWizardPage;
198 RdbSSH:array[GS_OpenSSH..GS_Plink] of TRadioButton;
199 EdtPlink:TEdit;
201 // Wizard page and variables for the line ending conversion options.
202 CRLFPage:TWizardPage;
203 RdbCRLF:array[GC_LFOnly..GC_CRLFCommitAsIs] of TRadioButton;
205 // Wizard page and variables for the processes page.
206 Processes:ProcessList;
207 ProcessesPage:TWizardPage;
208 ProcessesListBox:TListBox;
209 ProcessesRefresh,ContinueButton:TButton;
211 procedure BrowseForPuTTYFolder(Sender:TObject);
213 Path:String;
214 begin
215 Path:=ExtractFilePath(EdtPlink.Text);
216 BrowseForFolder('Please select the PuTTY folder:',Path,False);
217 if FileExists(Path+'\TortoisePlink.exe') then begin
218 EdtPlink.Text:=Path+'\TortoisePlink.exe';
219 RdbSSH[GS_Plink].Checked:=True;
220 end else if FileExists(Path+'\plink.exe') then begin
221 EdtPlink.Text:=Path+'\plink.exe';
222 RdbSSH[GS_Plink].Checked:=True;
223 end else begin
224 MsgBox('Please enter a valid path to "TortoisePlink.exe" or "plink.exe".',mbError,MB_OK);
225 end;
226 end;
228 procedure DeleteContextMenuEntries;
230 AppDir,Command,Msg:String;
231 RootKey:Integer;
232 begin
233 AppDir:=ExpandConstant('{app}');
235 if IsAdminLoggedOn then begin
236 RootKey:=HKEY_LOCAL_MACHINE;
237 end else begin
238 RootKey:=HKEY_CURRENT_USER;
239 end;
241 Command:='';
242 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','',Command);
243 if Pos(AppDir,Command)>0 then begin
244 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell') then begin
245 Msg:='Line {#__LINE__}: Unable to remove "Git Bash Here" shell extension.';
246 MsgBox(Msg,mbError,MB_OK);
247 Log(Msg);
248 // This is not a critical error, the user can probably fix it manually,
249 // so we continue.
250 end;
251 end;
253 Command:='';
254 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui\command','',Command);
255 if Pos(AppDir,Command)>0 then begin
256 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui') then begin
257 Msg:='Line {#__LINE__}: Unable to remove "Git GUI Here" shell extension.';
258 MsgBox(Msg,mbError,MB_OK);
259 Log(Msg);
260 // This is not a critical error, the user can probably fix it manually,
261 // so we continue.
262 end;
263 end;
264 end;
266 procedure RefreshProcessList(Sender:TObject);
268 Modules:TArrayOfString;
269 ProcsCloseRequired,ProcsCloseRestart:ProcessList;
270 Found:Boolean;
271 i:Longint;
272 Caption:String;
273 begin
274 SetArrayLength(Modules,2);
275 Modules[0]:=ExpandConstant('{app}\bin\msys-1.0.dll');
276 Modules[1]:=ExpandConstant('{app}\bin\tcl85.dll');
277 Found:=FindProcessesUsingModules(Modules,ProcsCloseRequired);
279 Found:=FindProcessesUsingModule(ExpandConstant('{app}\git-cheetah\git_shell_ext.dll'),ProcsCloseRestart) or Found;
281 // Misuse the "Restartable" flag to indicate which processes are required
282 // to be closed before setup can continue, and which just should be closed
283 // in order to make changes take effect immediately.
284 SetArrayLength(Processes,GetArrayLength(ProcsCloseRequired)+GetArrayLength(ProcsCloseRestart));
285 for i:=0 to GetArrayLength(ProcsCloseRequired)-1 do begin
286 Processes[i]:=ProcsCloseRequired[i];
287 Processes[i].Restartable:=False;
288 end;
289 for i:=0 to GetArrayLength(ProcsCloseRestart)-1 do begin
290 Processes[GetArrayLength(ProcsCloseRequired)+i]:=ProcsCloseRestart[i];
291 Processes[GetArrayLength(ProcsCloseRequired)+i].Restartable:=True;
292 end;
294 ProcessesListBox.Items.Clear;
295 if (Sender=NIL) or Found then begin
296 for i:=0 to GetArrayLength(Processes)-1 do begin
297 Caption:=Processes[i].Name+' (PID '+IntToStr(Processes[i].ID);
298 if not Processes[i].Restartable then begin
299 Caption:=Caption+', closing is required';
300 end;
301 Caption:=Caption+')';
302 ProcessesListBox.Items.Append(Caption);
303 end;
304 end;
306 if ContinueButton<>NIL then begin
307 ContinueButton.Enabled:=(GetArrayLength(ProcsCloseRequired)=0);
308 end;
309 end;
312 Installer code
315 procedure InitializeWizard;
317 i,PrevPageID:Integer;
318 LblGitBash,LblGitCmd,LblGitCmdTools,LblGitCmdToolsWarn:TLabel;
319 LblOpenSSH,LblPlink:TLabel;
320 PuTTYSessions:TArrayOfString;
321 LblLFOnly,LblCRLFAlways,LblCRLFCommitAsIs:TLabel;
322 BtnPlink:TButton;
323 Data:String;
324 begin
325 // Until we have a 64-bit version of git-cheetah, disable it on 64-bit Windows.
326 if isWin64 then begin
327 for i:=0 to WizardForm.ComponentsList.Items.Count-1 do begin
328 Data:=LowerCase(WizardForm.ComponentsList.ItemCaption[i]);
329 if Pos('context',Data)>0 then begin
330 // Select the Registry-based context menu entries.
331 WizardForm.ComponentsList.Checked[i]:=True;
332 end else if Pos('cheetah',Data)>0 then begin
333 // Disable the git-cheetah shell extension.
334 WizardForm.ComponentsList.ItemEnabled[i]:=False;
335 end;
336 end;
337 end;
339 PrevPageID:=wpSelectProgramGroup;
342 * Create a custom page for modifying the environment.
345 PathPage:=CreateCustomPage(
346 PrevPageID,
347 'Adjusting your PATH environment',
348 'How would you like to use Git from the command line?'
350 PrevPageID:=PathPage.ID;
352 // 1st choice
353 RdbPath[GP_BashOnly]:=TRadioButton.Create(PathPage);
354 with RdbPath[GP_BashOnly] do begin
355 Parent:=PathPage.Surface;
356 Caption:='Use Git Bash only';
357 Left:=ScaleX(4);
358 Top:=ScaleY(8);
359 Width:=ScaleX(129);
360 Height:=ScaleY(17);
361 Font.Style:=[fsBold];
362 TabOrder:=0;
363 Checked:=True;
364 end;
365 LblGitBash:=TLabel.Create(PathPage);
366 with LblGitBash do begin
367 Parent:=PathPage.Surface;
368 Caption:=
369 'This is the most conservative choice if you are concerned about the stability' + #13 +
370 'of your system. Your PATH will not be modified.';
371 Left:=ScaleX(28);
372 Top:=ScaleY(32);
373 Width:=ScaleX(405);
374 Height:=ScaleY(26);
375 end;
377 // 2nd choice
378 RdbPath[GP_Cmd]:=TRadioButton.Create(PathPage);
379 with RdbPath[GP_Cmd] do begin
380 Parent:=PathPage.Surface;
381 Caption:='Run Git from the Windows Command Prompt';
382 Left:=ScaleX(4);
383 Top:=ScaleY(76);
384 Width:=ScaleX(281);
385 Height:=ScaleY(17);
386 Font.Style:=[fsBold];
387 TabOrder:=1;
388 end;
389 LblGitCmd:=TLabel.Create(PathPage);
390 with LblGitCmd do begin
391 Parent:=PathPage.Surface;
392 Caption:=
393 'This option is considered safe and no conflicts with other tools are known.' + #13 +
394 'Only Git will be added to your PATH. Use this option if you want to use Git' + #13 +
395 'from a Cygwin Prompt (make sure to not have Cygwin''s Git installed).';
396 Left:=ScaleX(28);
397 Top:=ScaleY(100);
398 Width:=ScaleX(405);
399 Height:=ScaleY(39);
400 end;
402 // 3rd choice
403 RdbPath[GP_CmdTools]:=TRadioButton.Create(PathPage);
404 with RdbPath[GP_CmdTools] do begin
405 Parent:=PathPage.Surface;
406 Caption:='Run Git and included Unix tools from the Windows Command Prompt';
407 Left:=ScaleX(4);
408 Top:=ScaleY(152);
409 Width:=ScaleX(405);
410 Height:=ScaleY(17);
411 Font.Style:=[fsBold];
412 TabOrder:=2;
413 end;
414 LblGitCmdTools:=TLabel.Create(PathPage);
415 with LblGitCmdTools do begin
416 Parent:=PathPage.Surface;
417 Caption:='Both Git and its accompanying Unix tools will be added to your PATH.';
418 Left:=ScaleX(28);
419 Top:=ScaleY(176);
420 Width:=ScaleX(405);
421 Height:=ScaleY(13);
422 end;
423 LblGitCmdToolsWarn:=TLabel.Create(PathPage);
424 with LblGitCmdToolsWarn do begin
425 Parent:=PathPage.Surface;
426 Caption:=
427 'Warning: This will override Windows tools like find.exe and' + #13 +
428 'sort.exe. Select this option only if you understand the implications.';
429 Left:=ScaleX(28);
430 Top:=ScaleY(192);
431 Width:=ScaleX(376);
432 Height:=ScaleY(26);
433 Font.Color:=255;
434 Font.Style:=[fsBold];
435 end;
437 // Restore the setting chosen during a previous install.
438 Data:=GetPreviousData('Path Option','BashOnly');
439 if Data='BashOnly' then begin
440 RdbPath[GP_BashOnly].Checked:=True;
441 end else if Data='Cmd' then begin
442 RdbPath[GP_Cmd].Checked:=True;
443 end else if Data='CmdTools' then begin
444 RdbPath[GP_CmdTools].Checked:=True;
445 end;
448 * Create a custom page for using (Tortoise)Plink instead of OpenSSH
449 * if at least one PuTTY session is found in the Registry.
452 if RegGetSubkeyNames(HKEY_CURRENT_USER,'Software\SimonTatham\PuTTY\Sessions',PuTTYSessions) and (GetArrayLength(PuTTYSessions)>0) then begin
453 PuTTYPage:=CreateCustomPage(
454 PrevPageID,
455 'Choosing the SSH executable',
456 'Which Secure Shell client program would you like Git to use?'
458 PrevPageID:=PuTTYPage.ID;
460 // 1st choice
461 RdbSSH[GS_OpenSSH]:=TRadioButton.Create(PuTTYPage);
462 with RdbSSH[GS_OpenSSH] do begin
463 Parent:=PuTTYPage.Surface;
464 Caption:='Use OpenSSH';
465 Left:=ScaleX(4);
466 Top:=ScaleY(8);
467 Width:=ScaleX(129);
468 Height:=ScaleY(17);
469 Font.Style:=[fsBold];
470 TabOrder:=0;
471 Checked:=True;
472 end;
473 LblOpenSSH:=TLabel.Create(PuTTYPage);
474 with LblOpenSSH do begin
475 Parent:=PuTTYPage.Surface;
476 Caption:=
477 'This uses ssh.exe that comes with Git. The GIT_SSH and SVN_SSH' + #13 +
478 'environment variables will not be modified.';
479 Left:=ScaleX(28);
480 Top:=ScaleY(32);
481 Width:=ScaleX(324);
482 Height:=ScaleY(26);
483 end;
485 // 2nd choice
486 RdbSSH[GS_Plink]:=TRadioButton.Create(PuTTYPage);
487 with RdbSSH[GS_Plink] do begin
488 Parent:=PuTTYPage.Surface;
489 Caption:='Use (Tortoise)Plink';
490 Left:=ScaleX(4);
491 Top:=ScaleY(76);
492 Width:=ScaleX(281);
493 Height:=ScaleY(17);
494 Font.Style:=[fsBold];
495 TabOrder:=1;
496 end;
497 LblPlink:=TLabel.Create(PuTTYPage);
498 with LblPlink do begin
499 Parent:=PuTTYPage.Surface;
500 Caption:=
501 'PuTTY sessions were found in your Registry. You may specify the path' + #13 +
502 'to an existing copy of (Tortoise)Plink.exe from the TortoiseSVN/CVS' + #13 +
503 'or PuTTY applications. The GIT_SSH and SVN_SSH environment' + #13 +
504 'variables will be adjusted to point to the following executable:';
505 Left:=ScaleX(28);
506 Top:=ScaleY(100);
507 Width:=ScaleX(340);
508 Height:=ScaleY(52);
509 end;
510 EdtPlink:=TEdit.Create(PuTTYPage);
511 with EdtPlink do begin
512 Parent:=PuTTYPage.Surface;
513 Text:=GetPuTTYLocation;
514 if not FileExists(Text) then begin
515 Text:='';
516 end;
517 Left:=ScaleX(28);
518 Top:=ScaleY(161);
519 Width:=ScaleX(316);
520 Height:=ScaleY(13);
521 end;
522 BtnPlink:=TButton.Create(PuTTYPage);
523 with BtnPlink do begin
524 Parent:=PuTTYPage.Surface;
525 Caption:='...';
526 OnClick:=@BrowseForPuTTYFolder;
527 Left:=ScaleX(348);
528 Top:=ScaleY(161);
529 Width:=ScaleX(21);
530 Height:=ScaleY(21);
531 end;
533 // Restore the setting chosen during a previous install.
534 Data:=GetPreviousData('SSH Option','OpenSSH');
535 if Data='OpenSSH' then begin
536 RdbSSH[GS_OpenSSH].Checked:=True;
537 end else if Data='Plink' then begin
538 RdbSSH[GS_Plink].Checked:=True;
539 end;
540 end else begin
541 PuTTYPage:=NIL;
542 end;
545 * Create a custom page for the core.autocrlf setting.
548 CRLFPage:=CreateCustomPage(
549 PrevPageID,
550 'Configuring the line ending conversions',
551 'How should Git treat line endings in text files?'
553 PrevPageID:=CRLFPage.ID;
555 // 1st choice
556 RdbCRLF[GC_CRLFAlways]:=TRadioButton.Create(CRLFPage);
557 with RdbCRLF[GC_CRLFAlways] do begin
558 Parent:=CRLFPage.Surface;
559 Caption:='Checkout Windows-style, commit Unix-style line endings';
560 Left:=ScaleX(4);
561 Top:=ScaleY(8);
562 Width:=ScaleX(340);
563 Height:=ScaleY(17);
564 Font.Style:=[fsBold];
565 TabOrder:=0;
566 Checked:=True;
567 end;
568 LblCRLFAlways:=TLabel.Create(CRLFPage);
569 with LblCRLFAlways do begin
570 Parent:=CRLFPage.Surface;
571 Caption:=
572 'Git will convert LF to CRLF when checking out text files. When committing' + #13 +
573 'text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
574 'this is the recommended setting on Windows ("core.autocrlf" is set to "true").';
575 Left:=ScaleX(28);
576 Top:=ScaleY(32);
577 Width:=ScaleX(372);
578 Height:=ScaleY(47);
579 end;
581 // 2nd choice
582 RdbCRLF[GC_LFOnly]:=TRadioButton.Create(CRLFPage);
583 with RdbCRLF[GC_LFOnly] do begin
584 Parent:=CRLFPage.Surface;
585 Caption:='Checkout as-is, commit Unix-style line endings';
586 Left:=ScaleX(4);
587 Top:=ScaleY(80);
588 Width:=ScaleX(340);
589 Height:=ScaleY(17);
590 Font.Style:=[fsBold];
591 TabOrder:=1;
592 Checked:=False;
593 end;
594 LblLFOnly:=TLabel.Create(CRLFPage);
595 with LblLFOnly do begin
596 Parent:=CRLFPage.Surface;
597 Caption:=
598 'Git will not perform any conversion when checking out text files. When' + #13 +
599 'committing text files, CRLF will be converted to LF. For cross-platform projects,' + #13 +
600 'this is the recommended setting on Unix ("core.autocrlf" is set to "input").';
601 Left:=ScaleX(28);
602 Top:=ScaleY(104);
603 Width:=ScaleX(372);
604 Height:=ScaleY(47);
605 end;
607 // 3rd choice
608 RdbCRLF[GC_CRLFCommitAsIs]:=TRadioButton.Create(CRLFPage);
609 with RdbCRLF[GC_CRLFCommitAsIs] do begin
610 Parent:=CRLFPage.Surface;
611 Caption:='Checkout as-is, commit as-is';
612 Left:=ScaleX(4);
613 Top:=ScaleY(152);
614 Width:=ScaleX(340);
615 Height:=ScaleY(17);
616 Font.Style:=[fsBold];
617 TabOrder:=2;
618 Checked:=False;
619 end;
620 LblCRLFCommitAsIs:=TLabel.Create(CRLFPage);
621 with LblCRLFCommitAsIs do begin
622 Parent:=CRLFPage.Surface;
623 Caption:=
624 'Git will not perform any conversions when checking out or committing' + #13 +
625 'text files. Choosing this option is not recommended for cross-platform' + #13 +
626 'projects ("core.autocrlf" is set to "false").';
627 Left:=ScaleX(28);
628 Top:=ScaleY(176);
629 Width:=ScaleX(372);
630 Height:=ScaleY(47);
631 end;
633 // Restore the setting chosen during a previous install.
634 Data:=GetPreviousData('CRLF Option','CRLFAlways');
635 if Data='LFOnly' then begin
636 RdbCRLF[GC_LFOnly].Checked:=True;
637 end else if Data='CRLFAlways' then begin
638 RdbCRLF[GC_CRLFAlways].Checked:=True;
639 end else if Data='CRLFCommitAsIs' then begin
640 RdbCRLF[GC_CRLFCommitAsIs].Checked:=True;
641 end;
644 * Create a custom page for finding the processes that lock a module.
647 ProcessesPage:=CreateCustomPage(
648 wpPreparing,
649 'Replacing in-use files',
650 'The following applications use files that need to be replaced, please close them.'
653 ProcessesListBox:=TListBox.Create(ProcessesPage);
654 with ProcessesListBox do begin
655 Parent:=ProcessesPage.Surface;
656 Width:=ProcessesPage.SurfaceWidth;
657 Height:=ProcessesPage.SurfaceHeight-ScaleY(8);
658 end;
660 ProcessesRefresh:=TNewButton.Create(WizardForm);
661 with ProcessesRefresh do begin
662 Parent:=WizardForm;
663 Width:=WizardForm.NextButton.Width;
664 Height:=WizardForm.NextButton.Height;
665 Top:=WizardForm.NextButton.Top;
666 Left:=WizardForm.ClientWidth-WizardForm.NextButton.Left-WizardForm.NextButton.Width;
667 Caption:='&Refresh';
668 OnClick:=@RefreshProcessList;
669 end;
671 // This button is only used by the uninstaller.
672 ContinueButton:=NIL;
674 // Initially hide the Refresh button, show it when the process page becomes current.
675 ProcessesRefresh.Hide;
676 end;
678 function ShouldSkipPage(PageID:Integer):Boolean;
679 begin
680 if (ProcessesPage<>NIL) and (PageID=ProcessesPage.ID) then begin
681 // This page is only reached forward (by pressing "Next", never by pressing "Back").
682 RefreshProcessList(NIL);
683 Result:=(GetArrayLength(Processes)=0);
684 end else begin
685 Result:=False;
686 end;
687 end;
689 procedure CurPageChanged(CurPageID:Integer);
691 i:Integer;
692 begin
693 // Uncheck the console font option by default.
694 if CurPageID=wpSelectComponents then begin
695 for i:=0 to WizardForm.ComponentsList.Items.Count-1 do begin
696 if WizardForm.ComponentsList.ItemCaption[i]='{#COMP_CONSOLE_FONT}' then begin
697 WizardForm.ComponentsList.Checked[i]:=False;
698 Break;
699 end;
700 end;
701 end;
703 // Show the "Refresh" button only on the processes page.
704 if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin
705 ProcessesRefresh.Show;
706 end else begin
707 ProcessesRefresh.Hide;
708 end;
709 end;
711 function NextButtonClick(CurPageID:Integer):Boolean;
713 i:Integer;
714 begin
715 Result:=True;
717 if (PuTTYPage<>NIL) and (CurPageID=PuTTYPage.ID) then begin
718 Result:=RdbSSH[GS_OpenSSH].Checked or
719 (RdbSSH[GS_Plink].Checked and FileExists(EdtPlink.Text));
720 if not Result then begin
721 MsgBox('Please enter a valid path to (Tortoise)Plink.exe.',mbError,MB_OK);
722 end;
723 end else if (ProcessesPage<>NIL) and (CurPageID=ProcessesPage.ID) then begin
724 // It would have been nicer to just disable the "Next" button, but the
725 // WizardForm exports the button just read-only.
726 for i:=0 to GetArrayLength(Processes)-1 do begin
727 if not Processes[i].Restartable then begin
728 MsgBox('Setup cannot continue until you close at least those applications in the list that are marked as "closing is required".',mbCriticalError,MB_OK);
729 Result:=False;
730 Exit;
731 end;
732 end;
734 Result:=(GetArrayLength(Processes)=0);
736 if not Result then begin
737 Result:=(MsgBox(
738 'If you continue without closing the listed applications, you will need to log off and on again before changes take effect.' + #13 + #13 +
739 'Are you sure you want to continue anyway?',
740 mbConfirmation,
741 MB_YESNO
742 )=IDYES);
743 end;
744 end;
745 end;
747 // AfterInstall
749 // Even though the name of this procedure suggests otherwise most of the
750 // code below is only executed once after the regular installation code
751 // is finished. This happens because of the if-guard right in the
752 // beginning of this procedure.
753 procedure CurStepChanged(CurStep:TSetupStep);
755 AppDir,DllPath,FileName,TempName,Cmd,Msg:String;
756 BuiltIns,ImageNames,EnvPath,EnvHome,EnvSSH:TArrayOfString;
757 Count,i:Longint;
758 LinkCreated:Boolean;
759 FindRec:TFindRec;
760 RootKey:Integer;
761 begin
762 if CurStep<>ssPostInstall then begin
763 Exit;
764 end;
766 AppDir:=ExpandConstant('{app}');
769 Bind the imported function addresses
773 DllPath:=ExpandConstant('{app}\bin;{sys}');
775 // Load the list of images from a text file.
776 FileName:=AppDir+'\{#APP_BINDIMAGE}';
777 if LoadStringsFromFile(FileName,ImageNames) then begin
778 Count:=GetArrayLength(ImageNames)-1;
779 for i:=0 to Count do begin
780 FileName:=AppDir+'\'+ImageNames[i];
781 if not BindImageEx(BIND_NO_BOUND_IMPORTS or BIND_CACHE_IMPORT_DLLS,FileName,DllPath,'',0) then begin
782 Log('Line {#__LINE__}: Error calling BindImageEx for "'+FileName+'".');
783 end;
784 end;
785 end;
786 except
787 Log('Line {#__LINE__}: An exception occurred while calling BindImageEx.');
788 end;
791 Create the built-ins
794 // Load the built-ins from a text file.
795 FileName:=AppDir+'\{#APP_BUILTINS}';
796 if LoadStringsFromFile(FileName,BuiltIns) then begin
797 Count:=GetArrayLength(BuiltIns)-1;
799 // Delete those scripts from "bin" which have been replaced by built-ins in "libexec\git-core".
800 for i:=0 to Count do begin
801 FileName:=AppDir+'\bin\'+ChangeFileExt(ExtractFileName(BuiltIns[i]),'');
802 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
803 Log('Line {#__LINE__}: Unable to delete script "'+FileName+'", ignoring.');
804 end;
805 end;
807 // Create built-ins as aliases for git.exe.
808 for i:=0 to Count do begin
809 FileName:=AppDir+'\'+BuiltIns[i];
811 // Delete any existing built-in.
812 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
813 Log('Line {#__LINE__}: Unable to delete existing built-in "'+FileName+'", skipping.');
814 continue;
815 end;
818 // This will throw an exception on pre-WinVista systems.
819 LinkCreated:=CreateSymbolicLink(FileName,AppDir+'\bin\git.exe',0);
820 except
821 LinkCreated:=False;
822 Log('Line {#__LINE__}: Creating symbolic link "'+FileName+'" failed, will try a hard link.');
823 end;
825 if not LinkCreated then begin
827 // This will throw an exception on pre-Win2k systems.
828 LinkCreated:=CreateHardLink(FileName,AppDir+'\bin\git.exe',0);
829 except
830 LinkCreated:=False;
831 Log('Line {#__LINE__}: Creating hardlink "'+FileName+'" failed, will try a copy.');
832 end;
833 end;
835 if not LinkCreated then begin
836 if not FileCopy(AppDir+'\bin\git.exe',FileName,False) then begin
837 Log('Line {#__LINE__}: Creating copy "'+FileName+'" failed, giving up.');
838 // This is not a critical error, Git could basically be used without the
839 // aliases for built-ins, so we continue.
840 end;
841 end;
842 end;
844 // Delete any duplicate files in case we are updating from a non-libexec to a libexec directory layout.
845 if FindFirst(AppDir+'\libexec\git-core\*',FindRec) then begin
846 repeat
847 if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY)=0 then begin
848 FileName:=AppDir+'\bin\'+FindRec.name;
849 if (Pos(FindRec.name,'git.exe')<>1) and FileExists(FileName) and (not DeleteFile(FileName)) then begin
850 Log('Line {#__LINE__}: Unable to delete dupe "'+FileName+'", ignoring.');
851 end;
852 end;
853 until not FindNext(FindRec);
854 FindClose(FindRec);
855 end;
856 end else begin
857 Msg:='Line {#__LINE__}: Unable to read file "{#APP_BUILTINS}".';
858 MsgBox(Msg,mbError,MB_OK);
859 Log(Msg);
860 // This is not a critical error, Git could basically be used without the
861 // aliases for built-ins, so we continue.
862 end;
865 Adapt core.autocrlf
868 if RdbCRLF[GC_LFOnly].checked then begin
869 Cmd:='core.autocrlf input';
870 end else if RdbCRLF[GC_CRLFAlways].checked then begin
871 Cmd:='core.autocrlf true';
872 end else begin
873 Cmd:='core.autocrlf false';
874 end;
875 if not Exec(AppDir + '\bin\git.exe', 'config -f gitconfig ' + Cmd,
876 AppDir + '\etc', SW_HIDE, ewWaitUntilTerminated, i) then begin
877 Msg:='Unable to configure the line ending conversion: ' + Cmd;
878 MsgBox(Msg,mbError,MB_OK);
879 Log(Msg);
880 // This is not a critical error, the user can probably fix it manually,
881 // so we continue.
882 end;
885 Modify the environment
887 This must happen no later than ssPostInstall to make
888 "ChangesEnvironment=yes" not happend before the change!
891 FileName:=AppDir+'\setup.ini';
893 // Delete GIT_SSH and SVN_SSH if a previous installation set them (this is required for the GS_OpenSSH case).
894 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
895 if (GetArrayLength(EnvSSH)=1) and
896 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
897 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
898 Msg:='Line {#__LINE__}: Unable to reset GIT_SSH prior to install.';
899 MsgBox(Msg,mbError,MB_OK);
900 Log(Msg);
901 // This is not a critical error, the user can probably fix it manually,
902 // so we continue.
903 end;
904 end;
906 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
907 if (GetArrayLength(EnvSSH)=1) and
908 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','SVN_SSH','',FileName))=0) then begin
909 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
910 Msg:='Line {#__LINE__}: Unable to reset SVN_SSH prior to install.';
911 MsgBox(Msg,mbError,MB_OK);
912 Log(Msg);
913 // This is not a critical error, the user can probably fix it manually,
914 // so we continue.
915 end;
916 end;
918 if (PuTTYPage<>NIL) and RdbSSH[GS_Plink].Checked then begin
919 SetArrayLength(EnvSSH,1);
920 EnvSSH[0]:=EdtPlink.Text;
922 // Set GIT_SSH as specified by the user.
923 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
924 Msg:='Line {#__LINE__}: Unable to set the GIT_SSH environment variable.';
925 MsgBox(Msg,mbError,MB_OK);
926 Log(Msg);
927 // This is not a critical error, the user can probably fix it manually,
928 // so we continue.
929 end;
931 // Mark that we have changed GIT_SSH by writing its value to a file.
932 if not SetIniString('Environment','GIT_SSH',EnvSSH[0],FileName) then begin
933 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
934 MsgBox(Msg,mbError,MB_OK);
935 Log(Msg);
936 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
937 // so we continue.
938 end;
940 // Set SVN_SSH as specified by the user, but with escaped backslashes and quotes.
941 StringChangeEx(EnvSSH[0],'\','\\',True);
942 EnvSSH[0]:=AddQuotes(EnvSSH[0]);
944 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,EnvSSH) then begin
945 Msg:='Line {#__LINE__}: Unable to set the SVN_SSH environment variable.';
946 MsgBox(Msg,mbError,MB_OK);
947 Log(Msg);
948 // This is not a critical error, the user can probably fix it manually,
949 // so we continue.
950 end;
952 // Mark that we have changed SVN_SSH by writing its value to a file.
953 if not SetIniString('Environment','SVN_SSH',EnvSSH[0],FileName) then begin
954 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
955 MsgBox(Msg,mbError,MB_OK);
956 Log(Msg);
957 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
958 // so we continue.
959 end;
960 end;
962 // Get the current user's directories in PATH.
963 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
965 // First, remove the installation directory from PATH in any case.
966 for i:=0 to GetArrayLength(EnvPath)-1 do begin
967 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
968 EnvPath[i]:='';
969 end;
970 end;
972 // Delete HOME if a previous installation modified it.
973 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
974 if (GetArrayLength(EnvHome)=1) and
975 (CompareStr(RemoveQuotes(EnvHome[0]),GetIniString('Environment','HOME','',FileName))=0) then begin
976 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
977 Msg:='Line {#__LINE__}: Unable to reset HOME prior to install.';
978 MsgBox(Msg,mbError,MB_OK);
979 Log(Msg);
980 // This is not a critical error, the user can probably fix it manually,
981 // so we continue.
982 end;
983 end;
985 // Modify the PATH variable as requested by the user.
986 if RdbPath[GP_Cmd].Checked or RdbPath[GP_CmdTools].Checked then begin
987 i:=GetArrayLength(EnvPath);
988 SetArrayLength(EnvPath,i+1);
990 // List \cmd before \bin so \cmd has higher priority and programs in
991 // there will be called in favor of those in \bin.
992 EnvPath[i]:=AppDir+'\cmd';
994 if RdbPath[GP_CmdTools].Checked then begin
995 SetArrayLength(EnvPath,i+2);
996 EnvPath[i+1]:=AppDir+'\bin';
998 // Set HOME for the Windows Command Prompt, but only if it has not been set manually before.
999 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
1000 i:=GetArrayLength(EnvHome);
1001 if (i=0) or ((i=1) and (Length(EnvHome[0])=0)) then begin
1002 SetArrayLength(EnvHome,1);
1003 EnvHome[0]:=ExpandConstant('{%HOMEDRIVE}{%HOMEPATH}');
1004 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,EnvHome) then begin
1005 Msg:='Line {#__LINE__}: Unable to set the HOME environment variable.';
1006 MsgBox(Msg,mbError,MB_OK);
1007 Log(Msg);
1008 // This is not a critical error, the user can probably fix it manually,
1009 // so we continue.
1010 end;
1012 // Mark that we have changed HOME.
1013 if not SetIniString('Environment','HOME',EnvHome[0],FileName) then begin
1014 Msg:='Line {#__LINE__}: Unable to write to file "'+FileName+'".';
1015 MsgBox(Msg,mbError,MB_OK);
1016 Log(Msg);
1017 // This is not a critical error, though uninstall / reinstall will probably not run cleanly,
1018 // so we continue.
1019 end;
1020 end;
1021 end;
1022 end;
1024 // Set the current user's PATH directories.
1025 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
1026 Msg:='Line {#__LINE__}: Unable to set the PATH environment variable.';
1027 MsgBox(Msg,mbError,MB_OK);
1028 Log(Msg);
1029 // This is not a critical error, the user can probably fix it manually,
1030 // so we continue.
1031 end;
1034 Create shortcuts that need to be created regardless of the "Don't create a Start Menu folder" toggle
1037 Cmd:=ExpandConstant('{syswow64}\cmd.exe');
1038 TempName:='/c ""'+AppDir+'\bin\sh.exe" --login -i"';
1039 FileName:=AppDir+'\etc\git.ico';
1041 if IsComponentSelected('icons\quicklaunch') then begin
1042 CreateShellLink(
1043 ExpandConstant('{userappdata}\Microsoft\Internet Explorer\Quick Launch\Git Bash.lnk')
1044 , 'Git Bash'
1045 , Cmd
1046 , TempName
1047 , '%HOMEDRIVE%%HOMEPATH%'
1048 , FileName
1050 , SW_SHOWNORMAL
1052 end;
1053 if IsComponentSelected('icons\desktop') then begin
1054 CreateShellLink(
1055 GetShellFolder('desktop')+'\Git Bash.lnk'
1056 , 'Git Bash'
1057 , Cmd
1058 , TempName
1059 , '%HOMEDRIVE%%HOMEPATH%'
1060 , FileName
1062 , SW_SHOWNORMAL
1064 end;
1066 // Create a special shortcut that does not set a working directory (Note: Since Inno Setup 5.3.11,
1067 // passing an empty WorkingDir gets replaced with {sys}, so use '.' instead).
1068 // This shortcut is used by "Git Bash.vbs", which in turn is run by the "Git Bash Here" shell extension.
1069 CreateShellLink(
1070 AppDir+'\Git Bash.lnk'
1071 , 'Git Bash'
1072 , Cmd
1073 , TempName
1074 , '.'
1075 , FileName
1077 , SW_SHOWNORMAL
1081 Create the Windows Explorer integrations
1084 if IsAdminLoggedOn then begin
1085 RootKey:=HKEY_LOCAL_MACHINE;
1086 end else begin
1087 RootKey:=HKEY_CURRENT_USER;
1088 end;
1090 if IsComponentSelected('ext\reg\shellhere') then begin
1091 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell','','Git Ba&sh Here')) or
1092 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_shell\command','','wscript "'+AppDir+'\Git Bash.vbs" "%1"')) then begin
1093 Msg:='Line {#__LINE__}: Unable to create "Git Bash Here" shell extension.';
1094 MsgBox(Msg,mbError,MB_OK);
1095 Log(Msg);
1096 // This is not a critical error, the user can probably fix it manually,
1097 // so we continue.
1098 end;
1099 end;
1101 if IsComponentSelected('ext\reg\guihere') then begin
1102 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui','','Git &GUI Here')) or
1103 (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_gui\command','','"'+AppDir+'\bin\wish.exe" "'+AppDir+'\libexec\git-core\git-gui" "--working-dir" "%1"')) then begin
1104 Msg:='Line {#__LINE__}: Unable to create "Git GUI Here" shell extension.';
1105 MsgBox(Msg,mbError,MB_OK);
1106 Log(Msg);
1107 // This is not a critical error, the user can probably fix it manually,
1108 // so we continue.
1109 end;
1110 end;
1112 // It is either the Registry-based context menu entries, or the shell extension.
1113 if IsComponentSelected('ext\cheetah') then begin
1114 DeleteContextMenuEntries;
1116 FileName:=AppDir+'\git-cheetah\git_shell_ext.dll';
1117 if not ReplaceInUseFile(FileName,FileName+'.new',True) then begin
1118 Log('Line {#__LINE__}: Replacing file "'+FileName+'" failed.');
1119 end;
1120 end;
1121 end;
1123 procedure RegisterPreviousData(PreviousDataKey:Integer);
1125 Data:String;
1126 begin
1127 // Git Path options.
1128 Data:='';
1129 if RdbPath[GP_BashOnly].Checked then begin
1130 Data:='BashOnly';
1131 end else if RdbPath[GP_Cmd].Checked then begin
1132 Data:='Cmd';
1133 end else if RdbPath[GP_CmdTools].Checked then begin
1134 Data:='CmdTools';
1135 end;
1136 SetPreviousData(PreviousDataKey,'Path Option',Data);
1138 // Git SSH options.
1139 Data:='';
1140 if (PuTTYPage=NIL) or RdbSSH[GS_OpenSSH].Checked then begin
1141 Data:='OpenSSH';
1142 end else if RdbSSH[GS_Plink].Checked then begin
1143 Data:='Plink';
1144 end;
1145 SetPreviousData(PreviousDataKey,'SSH Option',Data);
1147 // Line ending conversion options.
1148 Data:='';
1149 if RdbCRLF[GC_LFOnly].Checked then begin
1150 Data:='LFOnly';
1151 end else if RdbCRLF[GC_CRLFAlways].Checked then begin
1152 Data:='CRLFAlways';
1153 end else if RdbCRLF[GC_CRLFCommitAsIs].Checked then begin
1154 Data:='CRLFCommitAsIs';
1155 end;
1156 SetPreviousData(PreviousDataKey,'CRLF Option',Data);
1157 end;
1160 Uninstaller code
1163 function InitializeUninstall:Boolean;
1165 Form:TSetupForm;
1166 Info:TLabel;
1167 ExitButton,RefreshButton:TButton;
1168 begin
1169 Result:=True;
1171 Form:=CreateCustomForm;
1173 Form.Caption:='Git Uninstall: Removing in-use files';
1174 Form.ClientWidth:=ScaleX(500);
1175 Form.ClientHeight:=ScaleY(256);
1176 Form.Center;
1178 Info:=TLabel.Create(Form);
1179 with Info do begin
1180 Parent:=Form;
1181 Left:=ScaleX(11);
1182 Top:=ScaleY(11);
1183 AutoSize:=True;
1184 Caption:='The following applications use files that need to be removed, please close them.';
1185 end;
1187 ContinueButton:=TButton.Create(Form);
1188 with ContinueButton do begin
1189 Parent:=Form;
1190 Left:=Form.ClientWidth-ScaleX(75+10);
1191 Top:=Form.ClientHeight-ScaleY(23+10);
1192 Width:=ScaleX(75);
1193 Height:=ScaleY(23);
1194 Caption:='Continue';
1195 ModalResult:=mrOk;
1196 end;
1198 ExitButton:=TButton.Create(Form);
1199 with ExitButton do begin
1200 Parent:=Form;
1201 Left:=ContinueButton.Left-ScaleX(75+6);
1202 Top:=ContinueButton.Top;
1203 Width:=ScaleX(75);
1204 Height:=ScaleY(23);
1205 Caption:='Exit';
1206 ModalResult:=mrCancel;
1207 Cancel:=True;
1208 end;
1210 RefreshButton:=TButton.Create(Form);
1211 with RefreshButton do begin
1212 Parent:=Form;
1213 Left:=ScaleX(10);
1214 Top:=ExitButton.Top;
1215 Width:=ScaleX(75);
1216 Height:=ScaleY(23);
1217 Caption:='Refresh';
1218 OnClick:=@RefreshProcessList;
1219 end;
1221 ProcessesListBox:=TListBox.Create(Form);
1222 with ProcessesListBox do begin
1223 Parent:=Form;
1224 Left:=ScaleX(11);
1225 Top:=Info.Top+Info.Height+11;
1226 Width:=Form.ClientWidth-ScaleX(11*2);
1227 Height:=ContinueButton.Top-ScaleY(11*4);
1228 end;
1230 Form.ActiveControl:=ContinueButton;
1232 RefreshProcessList(NIL);
1233 if GetArrayLength(Processes)>0 then begin
1234 // Now that these dialogs are going to be shown, we should probably
1235 // disable the "Are you sure to remove Git?" confirmation dialog, but
1236 // unfortunately that is not possible with Inno Setup currently.
1237 Result:=(Form.ShowModal()=mrOk);
1239 // Note: The number of processes might have changed during a refresh.
1240 if Result and (GetArrayLength(Processes)>0) then begin
1241 Result:=(MsgBox(
1242 'If you continue without closing the listed applications, you will need to log off and on again to remove some files manually.' + #13 + #13 +
1243 'Are you sure you want to continue anyway?',
1244 mbConfirmation,
1245 MB_YESNO
1246 )=IDYES);
1247 end;
1248 end;
1249 finally
1250 Form.free;
1251 end;
1252 end;
1254 // PreUninstall
1256 // Even though the name of this function suggests otherwise most of the
1257 // code below is only executed right before the actual uninstallation.
1258 // This happens because of the if-guard right in the beginning of this
1259 // function.
1260 procedure CurUninstallStepChanged(CurUninstallStep:TUninstallStep);
1262 AppDir,FileName,Msg:String;
1263 EnvPath,EnvHome,EnvSSH:TArrayOfString;
1264 i:Longint;
1265 begin
1266 if CurUninstallStep<>usUninstall then begin
1267 Exit;
1268 end;
1270 // Reset the console font (the FontType is reset in the Registry section).
1271 if IsComponentInstalled('consolefont') then begin
1272 if MsgBox('Do you want to revert the TrueType font setting for all console windows?',mbConfirmation,MB_YESNO)=IDYES then begin
1273 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontFamily',0);
1274 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontSize',0);
1275 RegWriteDWordValue(HKEY_CURRENT_USER,'Console','FontWeight',0);
1276 end;
1277 end;
1280 Modify the environment
1282 This must happen no later than usUninstall to make
1283 "ChangesEnvironment=yes" not happend before the change!
1286 AppDir:=ExpandConstant('{app}');
1287 FileName:=AppDir+'\setup.ini';
1289 // Delete the current user's GIT_SSH and SVN_SSH if we set it.
1290 EnvSSH:=GetEnvStrings('GIT_SSH',IsAdminLoggedOn);
1291 if (GetArrayLength(EnvSSH)=1) and
1292 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','GIT_SSH','',FileName))=0) then begin
1293 if not SetEnvStrings('GIT_SSH',IsAdminLoggedOn,True,[]) then begin
1294 Msg:='Line {#__LINE__}: Unable to revert any possible changes to GIT_SSH.';
1295 MsgBox(Msg,mbError,MB_OK);
1296 Log(Msg);
1297 // This is not a critical error, the user can probably fix it manually,
1298 // so we continue.
1299 end;
1300 end;
1302 EnvSSH:=GetEnvStrings('SVN_SSH',IsAdminLoggedOn);
1303 if (GetArrayLength(EnvSSH)=1) and
1304 (CompareStr(RemoveQuotes(EnvSSH[0]),GetIniString('Environment','SVN_SSH','',FileName))=0) then begin
1305 if not SetEnvStrings('SVN_SSH',IsAdminLoggedOn,True,[]) then begin
1306 Msg:='Line {#__LINE__}: Unable to revert any possible changes to SVN_SSH.';
1307 MsgBox(Msg,mbError,MB_OK);
1308 Log(Msg);
1309 // This is not a critical error, the user can probably fix it manually,
1310 // so we continue.
1311 end;
1312 end;
1314 // Get the current user's directories in PATH.
1315 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
1317 // Remove the installation directory from PATH in any case, even if it
1318 // was not added by the installer.
1319 for i:=0 to GetArrayLength(EnvPath)-1 do begin
1320 if Pos(AppDir+'\',EnvPath[i]+'\')=1 then begin
1321 EnvPath[i]:='';
1322 end;
1323 end;
1325 // Reset the current user's directories in PATH.
1326 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
1327 Msg:='Line {#__LINE__}: Unable to revert any possible changes to PATH.';
1328 MsgBox(Msg,mbError,MB_OK);
1329 Log(Msg);
1330 // This is not a critical error, the user can probably fix it manually,
1331 // so we continue.
1332 end;
1334 // Reset the current user's HOME if we modified it.
1335 EnvHome:=GetEnvStrings('HOME',IsAdminLoggedOn);
1336 if (GetArrayLength(EnvHome)=1) and
1337 (CompareStr(RemoveQuotes(EnvHome[0]),GetIniString('Environment','HOME','',FileName))=0) then begin
1338 if not SetEnvStrings('HOME',IsAdminLoggedOn,True,[]) then begin
1339 Msg:='Line {#__LINE__}: Unable to revert any possible changes to HOME.';
1340 MsgBox(Msg,mbError,MB_OK);
1341 Log(Msg);
1342 // This is not a critical error, the user can probably fix it manually,
1343 // so we continue.
1344 end;
1345 end;
1347 if FileExists(FileName) and (not DeleteFile(FileName)) then begin
1348 Msg:='Line {#__LINE__}: Unable to delete file "'+FileName+'".';
1349 MsgBox(Msg,mbError,MB_OK);
1350 Log(Msg);
1351 // This is not a critical error, the user can probably fix it manually,
1352 // so we continue.
1353 end;
1356 Delete the Windows Explorer integrations
1359 DeleteContextMenuEntries;
1361 FileName:=AppDir+'\git-cheetah\git_shell_ext.dll';
1362 if FileExists(FileName) then begin
1363 if not UnregisterServer(Is64BitInstallMode,FileName,False) then begin
1364 Msg:='Line {#__LINE__}: Unable to unregister file "'+FileName+'". Please do it manually by running "regsvr32 /u '+ExtractFileName(FileName)+'".';
1365 MsgBox(Msg,mbError,MB_OK);
1366 Log(Msg);
1367 end;
1369 if not DeleteFile(FileName) then begin
1370 Msg:='Line {#__LINE__}: Unable to delete file "'+FileName+'". Please do it manually after logging off and on again.';
1371 MsgBox(Msg,mbError,MB_OK);
1372 Log(Msg);
1373 end;
1374 end;
1375 end;