refactor: eliminate `cola.prefs` package
[git-cola.git] / win32 / install.iss
bloba49984a88d02d47926d7076ad14d8063b369e741
1 #define APP_NAME 'git-cola'
2 #define APP_LONGNAME 'git-cola - The highly caffeinated git GUI'
3 #define APP_VERSION '%APPVERSION%'
4 #define APP_URL 'http://git-cola.github.io/'
6 [Setup]
7 ; Compiler-related
8 InternalCompressLevel=max
9 OutputBaseFilename={#emit APP_NAME+'-'+APP_VERSION}
10 OutputDir=%OUTPUTDIR%
11 SolidCompression=yes
13 ; Installer-related
14 AllowNoIcons=yes
15 AppName={#emit APP_LONGNAME}
16 AppPublisherURL={#emit APP_URL}
17 AppVersion={#emit APP_VERSION}
18 AppVerName={#emit APP_LONGNAME+' '+APP_VERSION}
19 ChangesEnvironment=yes
20 DefaultDirName={pf}\{#emit APP_NAME}
21 DefaultGroupName={#emit APP_LONGNAME}
22 DisableReadyPage=yes
23 InfoBeforeFile=etc\gpl-2.0.rtf
24 PrivilegesRequired=none
25 UninstallDisplayIcon=etc\git.ico
27 ; Cosmetic
28 SetupIconFile=etc\git.ico
29 WizardSmallImageFile=etc\git.bmp
31 [Tasks]
32 Name: quicklaunchicon; Description: "Create a &Quick Launch icon"; GroupDescription: "Additional icons:"; Flags: checkedonce
33 Name: desktopicon; Description: "Create a &Desktop icon"; GroupDescription: "Additional icons:"; Flags: checkedonce
34 Name: guiextension; Description: "Add ""Git &Cola Here"""; GroupDescription: "Windows Explorer integration:"; Flags: checkedonce
36 [Files]
37 Source: "*"; DestDir: "{app}"; Excludes: "\*.bmp, \install.*, \tmp.*, \bin\*install*"; Flags: recursesubdirs
38 Source: "etc\ReleaseNotes.txt"; DestDir: "{app}\etc"; Flags: isreadme
40 [Icons]
41 Name: "{group}\git-cola"; Filename: "{code:GetPythonExe}"; Parameters: """{app}\bin\git-cola.pyw"" --prompt --git-path ""{code:GetGitExe}"""; WorkingDir: "%USERPROFILE%"; IconFilename: "{app}\etc\git.ico"
42 Name: "{group}\git-dag"; Filename: "{code:GetPythonExe}"; Parameters: """{app}\bin\git-dag.pyw"" --prompt --git-path ""{code:GetGitExe}"""; WorkingDir: "%USERPROFILE%"; IconFilename: "{app}\etc\git.ico"
43 Name: "{group}\git-cola Homepage"; Filename: "{#emit APP_URL}"; WorkingDir: "%USERPROFILE%";
44 Name: "{group}\Release Notes"; Filename: "{app}\etc\ReleaseNotes.txt"; WorkingDir: "%USERPROFILE%";
45 Name: "{group}\License"; Filename: "{app}\etc\gpl-2.0.rtf"; WorkingDir: "%USERPROFILE%";
46 Name: "{group}\Uninstall git-cola"; Filename: "{uninstallexe}"
47 Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\git-cola"; Filename: "{code:GetPythonExe}"; Parameters: """{app}\bin\git-cola.pyw"" --prompt --git-path ""{code:GetGitExe}"""; WorkingDir: "%USERPROFILE%"; IconFilename: "{app}\etc\git.ico"; Tasks: quicklaunchicon
48 Name: "{code:GetShellFolder|desktop}\git-cola"; Filename: "{code:GetPythonExe}"; Parameters: """{app}\bin\git-cola.pyw"" --prompt --git-path ""{code:GetGitExe}"""; WorkingDir: "%USERPROFILE%"; IconFilename: "{app}\etc\git.ico"; Tasks: desktopicon
49 Name: "{code:GetShellFolder|desktop}\git-dag"; Filename: "{code:GetPythonExe}"; Parameters: """{app}\bin\git-dag.pyw"" --prompt --git-path ""{code:GetGitExe}"""; WorkingDir: "%USERPROFILE%"; IconFilename: "{app}\etc\git.ico"; Tasks: desktopicon
51 [Messages]
52 BeveledLabel={#emit APP_URL}
53 SetupAppTitle={#emit APP_NAME} Setup
54 SetupWindowTitle={#emit APP_NAME} Setup
56 [UninstallDelete]
57 Type: files; Name: "{app}\bin\*"
58 Type: files; Name: "{app}\etc\*"
59 Type: files; Name: "{app}\share\git-cola\bin\*"
60 Type: files; Name: "{app}\share\git-cola\icons\*"
61 Type: files; Name: "{app}\share\git-cola\lib\cola\*"
62 Type: files; Name: "{app}\share\git-cola\lib\cola\classic\*"
63 Type: files; Name: "{app}\share\git-cola\lib\cola\main\*"
64 Type: files; Name: "{app}\share\git-cola\lib\cola\merge\*"
65 Type: files; Name: "{app}\share\git-cola\lib\cola\models\*"
66 Type: files; Name: "{app}\share\git-cola\lib\cola\stash\*"
67 Type: files; Name: "{app}\share\git-cola\lib\cola\widgets\*"
68 Type: dirifempty; Name: "{app}\share\git-cola\lib\cola\classic"
69 Type: dirifempty; Name: "{app}\share\git-cola\lib\cola\main"
70 Type: dirifempty; Name: "{app}\share\git-cola\lib\cola\merge"
71 Type: dirifempty; Name: "{app}\share\git-cola\lib\cola\models"
72 Type: dirifempty; Name: "{app}\share\git-cola\lib\cola\stash"
73 Type: dirifempty; Name: "{app}\share\git-cola\lib\cola\widgets"
74 Type: dirifempty; Name: "{app}\share\git-cola\lib\cola"
75 Type: dirifempty; Name: "{app}\share\git-cola\lib"
76 Type: dirifempty; Name: "{app}\share\git-cola\icons"
77 Type: dirifempty; Name: "{app}\share\git-cola\bin"
78 Type: dirifempty; Name: "{app}\etc"
79 Type: dirifempty; Name: "{app}\bin"
81 [Code]
83 Helper methods
86 var
87 PythonPage:TWizardPage;
88 GitPage:TWizardPage;
89 EdtPython:TEdit;
90 EdtGit:TEdit;
92 function GetEnvStrings(VarName:string;AllUsers:Boolean):TArrayOfString;
93 var
94 Path:string;
95 i:Longint;
96 p:Integer;
97 begin
98 Path:='';
100 // See http://www.jrsoftware.org/isfaq.php#env
101 if AllUsers then begin
102 // We ignore errors here. The resulting array of strings will be empty.
103 RegQueryStringValue(HKEY_LOCAL_MACHINE,'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',VarName,Path);
104 end else begin
105 // We ignore errors here. The resulting array of strings will be empty.
106 RegQueryStringValue(HKEY_CURRENT_USER,'Environment',VarName,Path);
107 end;
109 // Make sure we have at least one semicolon.
110 Path:=Path+';';
112 // Split the directories in PATH into an array of strings.
113 i:=0;
114 SetArrayLength(Result,0);
116 p:=Pos(';',Path);
117 while p>0 do begin
118 SetArrayLength(Result,i+1);
119 if p>1 then begin
120 Result[i]:=Copy(Path,1,p-1);
121 i:=i+1;
122 end;
123 Path:=Copy(Path,p+1,Length(Path));
124 p:=Pos(';',Path);
125 end;
126 end;
128 function SetEnvStrings(VarName:string;AllUsers,DeleteIfEmpty:Boolean;DirStrings:TArrayOfString):Boolean;
130 Path,KeyName:string;
131 i:Longint;
132 begin
133 // Merge all non-empty directory strings into a PATH variable.
134 Path:='';
135 for i:=0 to GetArrayLength(DirStrings)-1 do begin
136 if Length(DirStrings[i])>0 then begin
137 if Length(Path)>0 then begin
138 Path:=Path+';'+DirStrings[i];
139 end else begin
140 Path:=DirStrings[i];
141 end;
142 end;
143 end;
145 // See http://www.jrsoftware.org/isfaq.php#env
146 if AllUsers then begin
147 KeyName:='SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
148 if DeleteIfEmpty and (Length(Path)=0) then begin
149 Result:=(not RegValueExists(HKEY_LOCAL_MACHINE,KeyName,VarName))
150 or RegDeleteValue(HKEY_LOCAL_MACHINE,KeyName,VarName);
151 end else begin
152 Result:=RegWriteStringValue(HKEY_LOCAL_MACHINE,KeyName,VarName,Path);
153 end;
154 end else begin
155 KeyName:='Environment';
156 if DeleteIfEmpty and (Length(Path)=0) then begin
157 Result:=(not RegValueExists(HKEY_CURRENT_USER,KeyName,VarName))
158 or RegDeleteValue(HKEY_CURRENT_USER,KeyName,VarName);
159 end else begin
160 Result:=RegWriteStringValue(HKEY_CURRENT_USER,KeyName,VarName,Path);
161 end;
162 end;
163 end;
166 procedure BrowseForPythonFolder(Sender:TObject);
168 Path:string;
169 begin
170 Path:=GetPreviousData('PythonPath', 'C:\Python27');
171 EdtPython.Text:=Path;
172 Path:=ExtractFilePath(EdtPython.Text);
173 BrowseForFolder('Please select the Python folder:',Path,False);
174 Path:=Path+'\pythonw.exe';
175 if FileExists(Path) then begin
176 EdtPython.Text:=Path;
177 end else begin
178 MsgBox('Please enter a valid path to pythonw.exe.',mbError,MB_OK);
179 end;
180 end;
182 function GetPythonExe(Param: String): String;
183 begin
184 Result:=EdtPython.Text;
185 end;
187 procedure BrowseForGitFolder(Sender:TObject);
189 Path:string;
190 OldPath:string;
191 begin
192 Path:=GetPreviousData('GitPath', 'C:\Program Files\Git');
193 EdtGit.Text:=Path;
194 Path:=ExtractFilePath(EdtGit.Text);
195 BrowseForFolder('Please select the Git folder:',Path,False);
196 OldPath:=Path;
199 Check for both $DIR\git.exe and $DIR\bin\git.exe
202 Path:=OldPath+'\bin\git.exe';
203 if FileExists(Path) then begin
204 EdtGit.Text:=Path;
205 Exit;
206 end;
208 Path:=OldPath+'\git.exe';
209 if FileExists(Path) then begin
210 EdtGit.Text:=Path;
211 end else begin
212 MsgBox('Please enter a valid path to git.exe.',mbError,MB_OK);
213 end;
214 end;
217 function GetGitExe(Param: String): String;
218 begin
219 Result:=EdtGit.Text;
220 end;
222 procedure RegisterPreviousData(PreviousDataKey:Integer);
224 Path:string;
225 begin
226 Path:=ExtractFilePath(EdtPython.Text);
227 SetPreviousData(PreviousDataKey, 'PythonPath', Path);
229 Path:=ExtractFilePath(ExtractFilePath(EdtGit.Text));
230 SetPreviousData(PreviousDataKey, 'GitPath', Path);
231 end;
234 Installer code
238 procedure InitializeWizard;
240 BtnPython:TButton;
241 BtnGit:TButton;
242 LblPython:TLabel;
243 LblGit:TLabel;
244 begin
245 // Create a custom page for finding Python
246 PythonPage:=CreateCustomPage(
247 wpSelectTasks,
248 'Setup Python',
249 'Where is your Python folder?'
252 LblPython:=TLabel.Create(PythonPage);
253 with LblPython do begin
254 Parent:=PythonPage.Surface;
255 Caption:= 'Please provide the path to pythonw.exe.';
256 Left:=ScaleX(28);
257 Top:=ScaleY(100);
258 Width:=ScaleX(316);
259 Height:=ScaleY(39);
260 end;
262 EdtPython:=TEdit.Create(PythonPage);
263 with EdtPython do begin
264 Parent:=PythonPage.Surface;
265 Text:=GetPreviousData('PythonPath', 'C:\Python26');
266 Text:=Text+'pythonw.exe';
267 if not FileExists(Text) then begin
268 Text:='';
269 end;
270 Left:=ScaleX(28);
271 Top:=ScaleY(148);
272 Width:=ScaleX(316);
273 Height:=ScaleY(13);
274 end;
276 BtnPython:=TButton.Create(PythonPage);
277 with BtnPython do begin
278 Parent:=PythonPage.Surface;
279 Caption:='...';
280 OnClick:=@BrowseForPythonFolder;
281 Left:=ScaleX(348);
282 Top:=ScaleY(148);
283 Width:=ScaleX(21);
284 Height:=ScaleY(21);
285 end;
287 // Create a custom page for finding Git
288 GitPage:=CreateCustomPage(
289 wpSelectTasks,
290 'Setup Git',
291 'Where is your Git folder?'
294 LblGit:=TLabel.Create(GitPage);
295 with LblGit do begin
296 Parent:=GitPage.Surface;
297 Caption:= 'Please provide the path to git.exe.';
298 Left:=ScaleX(28);
299 Top:=ScaleY(100);
300 Width:=ScaleX(316);
301 Height:=ScaleY(39);
302 end;
304 EdtGit:=TEdit.Create(GitPage);
305 with EdtGit do begin
306 Parent:=GitPage.Surface;
307 Text:=GetPreviousData('GitPath', 'C:\Program Files\Git');
308 Text:=Text+'git.exe';
309 if not FileExists(Text) then begin
310 Text:='';
311 end;
312 Left:=ScaleX(28);
313 Top:=ScaleY(148);
314 Width:=ScaleX(316);
315 Height:=ScaleY(13);
316 end;
318 BtnGit:=TButton.Create(GitPage);
319 with BtnGit do begin
320 Parent:=GitPage.Surface;
321 Caption:='...';
322 OnClick:=@BrowseForGitFolder;
323 Left:=ScaleX(348);
324 Top:=ScaleY(148);
325 Width:=ScaleX(21);
326 Height:=ScaleY(21);
327 end;
328 end;
330 function NextButtonClick(CurPageID:Integer):Boolean;
331 begin
332 if CurPageID = PythonPage.ID then begin
333 Result:=FileExists(EdtPython.Text);
335 if not Result then begin
336 MsgBox('Please enter a valid path to pythonw.exe.',mbError,MB_OK);
337 end;
338 Exit;
339 end;
341 if CurPageID = GitPage.ID then begin
342 Result:=FileExists(EdtGit.Text);
344 if not Result then begin
345 MsgBox('Please enter a valid path to git.exe.',mbError,MB_OK);
346 end;
347 Exit;
348 end;
350 Result:=True;
351 end;
353 procedure CurStepChanged(CurStep:TSetupStep);
355 AppDir,BinDir,Msg:string;
356 EnvPath:TArrayOfString;
357 i:Longint;
358 RootKey:Integer;
359 begin
360 if CurStep<>ssPostInstall then begin
361 Exit;
362 end;
364 AppDir:=ExpandConstant('{app}');
365 BinDir:=ExpandConstant('{app}\bin');
368 Modify the environment
370 This must happen no later than ssPostInstall to make
371 "ChangesEnvironment=yes" not happend before the change!
374 // Get the current user's directories in PATH.
375 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
377 // First, remove the installation directory from PATH in any case.
378 for i:=0 to GetArrayLength(EnvPath)-1 do begin
379 if Pos(AppDir,EnvPath[i])=1 then begin
380 EnvPath[i]:='';
381 end;
382 end;
384 // Modify the PATH variable as requested by the user.
385 i:=GetArrayLength(EnvPath);
386 SetArrayLength(EnvPath,i+1);
388 // Add \bin to the path
389 EnvPath[i]:=BinDir
391 // Set the current user's PATH directories.
392 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
393 Msg:='Line {#emit __LINE__}: Unable to set the PATH environment variable.';
394 MsgBox(Msg,mbError,MB_OK);
395 Log(Msg);
396 // This is not a critical error, the user can probably fix it manually,
397 // so we continue.
398 end;
401 Create the Windows Explorer shell extensions
404 if IsAdminLoggedOn then begin
405 RootKey:=HKEY_LOCAL_MACHINE;
406 end else begin
407 RootKey:=HKEY_CURRENT_USER;
408 end;
410 if IsTaskSelected('guiextension') then begin
411 if (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_cola','','Git &Cola Here'))
412 or (not RegWriteStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_cola\command','','"'+EdtPython.Text+'" "'+AppDir+'\bin\git-cola.pyw" "--repo" "%1" "--git-path" "'+EdtGit.Text+'"')) then begin
413 Msg:='Line {#emit __LINE__}: Unable to create "Git Cola Here" shell extension.';
414 MsgBox(Msg,mbError,MB_OK);
415 Log(Msg);
416 // This is not a critical error, the user can probably fix it manually,
417 // so we continue.
418 end;
419 end;
420 end;
423 Uninstaller code
426 function InitializeUninstall:Boolean;
427 begin
428 Result:=True;
429 end;
431 procedure CurUninstallStepChanged(CurUninstallStep:TUninstallStep);
433 AppDir,Command,Msg:string;
434 EnvPath:TArrayOfString;
435 i:Longint;
436 RootKey:Integer;
437 begin
438 if CurUninstallStep<>usUninstall then begin
439 Exit;
440 end;
443 Modify the environment
445 This must happen no later than usUninstall to make
446 "ChangesEnvironment=yes" not happend before the change!
449 AppDir:=ExpandConstant('{app}');
450 Command:=AppDir+'\setup.ini';
452 // Get the current user's directories in PATH.
453 EnvPath:=GetEnvStrings('PATH',IsAdminLoggedOn);
455 // Remove the installation directory from PATH in any case, even if it
456 // was not added by the installer.
457 for i:=0 to GetArrayLength(EnvPath)-1 do begin
458 if Pos(AppDir,EnvPath[i])=1 then begin
459 EnvPath[i]:='';
460 end;
461 end;
463 // Reset the current user's directories in PATH.
464 if not SetEnvStrings('PATH',IsAdminLoggedOn,True,EnvPath) then begin
465 Msg:='Line {#emit __LINE__}: Unable to revert any possible changes to PATH.';
466 MsgBox(Msg,mbError,MB_OK);
467 Log(Msg);
468 // This is not a critical error, the user can probably fix it manually,
469 // so we continue.
470 end;
472 if (FileExists(Command) and (not DeleteFile(Command))) then begin
473 Msg:='Line {#emit __LINE__}: Unable to delete file "'+Command+'".';
474 MsgBox(Msg,mbError,MB_OK);
475 Log(Msg);
476 // This is not a critical error, the user can probably fix it manually,
477 // so we continue.
478 end;
481 Delete the Windows Explorer shell extensions
484 if IsAdminLoggedOn then begin
485 RootKey:=HKEY_LOCAL_MACHINE;
486 end else begin
487 RootKey:=HKEY_CURRENT_USER;
488 end;
490 Command:='';
491 RegQueryStringValue(RootKey,'SOFTWARE\Classes\Directory\shell\git_cola\command','',Command);
492 if Pos(AppDir,Command)>0 then begin
493 if not RegDeleteKeyIncludingSubkeys(RootKey,'SOFTWARE\Classes\Directory\shell\git_cola') then begin
494 Msg:='Line {#emit __LINE__}: Unable to remove "Git Cola Here" shell extension.';
495 MsgBox(Msg,mbError,MB_OK);
496 Log(Msg);
497 // This is not a critical error, the user can probably fix it manually,
498 // so we continue.
499 end;
500 end;
501 end;
503 function GetShellFolder(Param:string):string;
504 begin
505 if IsAdminLoggedOn then begin
506 Param:='{common'+Param+'}';
507 end else begin
508 Param:='{user'+Param+'}';
509 end;
510 Result:=ExpandConstant(Param);
511 end;