mirror of
https://github.com/ollama/ollama.git
synced 2025-11-10 11:27:15 +01:00
* app: add code for macOS and Windows apps under 'app' * app: add readme * app: windows and linux only for now * ci: fix ui CI validation --------- Co-authored-by: jmorganca <jmorganca@gmail.com>
375 lines
14 KiB
Plaintext
375 lines
14 KiB
Plaintext
; Inno Setup Installer for Ollama
|
|
;
|
|
; To build the installer use the build script invoked from the top of the source tree
|
|
;
|
|
; powershell -ExecutionPolicy Bypass -File .\scripts\build_windows.ps
|
|
|
|
|
|
#define MyAppName "Ollama"
|
|
#if GetEnv("PKG_VERSION") != ""
|
|
#define MyAppVersion GetEnv("PKG_VERSION")
|
|
#else
|
|
#define MyAppVersion "0.0.0"
|
|
#endif
|
|
#define MyAppPublisher "Ollama"
|
|
#define MyAppURL "https://ollama.com/"
|
|
#define MyAppExeName "ollama app.exe"
|
|
#define MyIcon ".\assets\app.ico"
|
|
|
|
[Setup]
|
|
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
|
|
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
|
AppId={{44E83376-CE68-45EB-8FC1-393500EB558C}
|
|
AppName={#MyAppName}
|
|
AppVersion={#MyAppVersion}
|
|
VersionInfoVersion={#MyAppVersion}
|
|
;AppVerName={#MyAppName} {#MyAppVersion}
|
|
AppPublisher={#MyAppPublisher}
|
|
AppPublisherURL={#MyAppURL}
|
|
AppSupportURL={#MyAppURL}
|
|
AppUpdatesURL={#MyAppURL}
|
|
ArchitecturesAllowed=x64compatible arm64
|
|
ArchitecturesInstallIn64BitMode=x64compatible arm64
|
|
DefaultDirName={localappdata}\Programs\{#MyAppName}
|
|
DefaultGroupName={#MyAppName}
|
|
DisableProgramGroupPage=yes
|
|
PrivilegesRequired=lowest
|
|
OutputBaseFilename="OllamaSetup"
|
|
SetupIconFile={#MyIcon}
|
|
UninstallDisplayIcon={uninstallexe}
|
|
Compression=lzma2/ultra64
|
|
LZMAUseSeparateProcess=yes
|
|
LZMANumBlockThreads=8
|
|
SolidCompression=yes
|
|
WizardStyle=modern
|
|
ChangesEnvironment=yes
|
|
OutputDir=..\dist\
|
|
|
|
; Disable logging once everything's battle tested
|
|
; Filename will be %TEMP%\Setup Log*.txt
|
|
SetupLogging=yes
|
|
CloseApplications=no
|
|
RestartApplications=no
|
|
RestartIfNeededByRun=no
|
|
|
|
; https://jrsoftware.org/ishelp/index.php?topic=setup_wizardimagefile
|
|
WizardSmallImageFile=.\assets\setup.bmp
|
|
|
|
; Ollama requires Windows 10 22H2 or newer for proper unicode rendering
|
|
; TODO: consider setting this to 10.0.19045
|
|
MinVersion=10.0.10240
|
|
|
|
; First release that supports WinRT UI Composition for win32 apps
|
|
; MinVersion=10.0.17134
|
|
; First release with XAML Islands - possible UI path forward
|
|
; MinVersion=10.0.18362
|
|
|
|
; quiet...
|
|
DisableDirPage=yes
|
|
DisableFinishedPage=yes
|
|
DisableReadyMemo=yes
|
|
DisableReadyPage=yes
|
|
DisableStartupPrompt=yes
|
|
|
|
; TODO - percentage can't be set less than 100, so how to make it shorter?
|
|
; WizardSizePercent=100,80
|
|
|
|
#if GetEnv("KEY_CONTAINER")
|
|
SignTool=MySignTool
|
|
SignedUninstaller=yes
|
|
#endif
|
|
|
|
SetupMutex=OllamaSetupMutex
|
|
|
|
[Languages]
|
|
Name: "english"; MessagesFile: "compiler:Default.isl"
|
|
|
|
[LangOptions]
|
|
DialogFontSize=12
|
|
|
|
[Files]
|
|
#if FileExists("..\dist\windows-ollama-app-amd64.exe")
|
|
Source: "..\dist\windows-ollama-app-amd64.exe"; DestDir: "{app}"; DestName: "{#MyAppExeName}" ;Check: not IsArm64(); Flags: ignoreversion 64bit; BeforeInstall: TaskKill('{#MyAppExeName}')
|
|
Source: "..\dist\windows-amd64\vc_redist.x64.exe"; DestDir: "{tmp}"; Check: not IsArm64() and vc_redist_needed(); Flags: deleteafterinstall
|
|
Source: "..\dist\windows-amd64\ollama.exe"; DestDir: "{app}"; Check: not IsArm64(); Flags: ignoreversion 64bit; BeforeInstall: TaskKill('ollama.exe')
|
|
Source: "..\dist\windows-amd64\lib\ollama\*"; DestDir: "{app}\lib\ollama\"; Check: not IsArm64(); Flags: ignoreversion 64bit recursesubdirs
|
|
#endif
|
|
|
|
; For local development, rely on binary compatibility at runtime since we can't cross compile
|
|
#if FileExists("..\dist\windows-ollama-app-arm64.exe")
|
|
Source: "..\dist\windows-ollama-app-arm64.exe"; DestDir: "{app}"; DestName: "{#MyAppExeName}" ;Check: IsArm64(); Flags: ignoreversion 64bit; BeforeInstall: TaskKill('{#MyAppExeName}')
|
|
#else
|
|
Source: "..\dist\windows-ollama-app-amd64.exe"; DestDir: "{app}"; DestName: "{#MyAppExeName}" ;Check: IsArm64(); Flags: ignoreversion 64bit; BeforeInstall: TaskKill('{#MyAppExeName}')
|
|
#endif
|
|
|
|
#if FileExists("..\dist\windows-arm64\ollama.exe")
|
|
Source: "..\dist\windows-arm64\vc_redist.arm64.exe"; DestDir: "{tmp}"; Check: IsArm64() and vc_redist_needed(); Flags: deleteafterinstall
|
|
Source: "..\dist\windows-arm64\ollama.exe"; DestDir: "{app}"; Check: IsArm64(); Flags: ignoreversion 64bit; BeforeInstall: TaskKill('ollama.exe')
|
|
#endif
|
|
|
|
Source: ".\assets\app.ico"; DestDir: "{app}"; Flags: ignoreversion
|
|
|
|
[Icons]
|
|
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\app.ico"
|
|
Name: "{app}\lib\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\app.ico"
|
|
Name: "{userprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\app.ico"
|
|
|
|
[InstallDelete]
|
|
Type: files; Name: "{%LOCALAPPDATA}\Ollama\updates"
|
|
|
|
[Run]
|
|
#if DirExists("..\dist\windows-arm64")
|
|
Filename: "{tmp}\vc_redist.arm64.exe"; Parameters: "/install /passive /norestart"; Check: IsArm64() and vc_redist_needed(); StatusMsg: "Installing VC++ Redistributables..."; Flags: waituntilterminated
|
|
#endif
|
|
#if DirExists("..\dist\windows-amd64")
|
|
Filename: "{tmp}\vc_redist.x64.exe"; Parameters: "/install /passive /norestart"; Check: not IsArm64() and vc_redist_needed(); StatusMsg: "Installing VC++ Redistributables..."; Flags: waituntilterminated
|
|
#endif
|
|
Filename: "{cmd}"; Parameters: "/C set PATH={app};%PATH% & ""{app}\{#MyAppExeName}"""; Flags: postinstall nowait runhidden
|
|
|
|
[UninstallRun]
|
|
; Filename: "{cmd}"; Parameters: "/C ""taskkill /im ''{#MyAppExeName}'' /f /t"; Flags: runhidden
|
|
; Filename: "{cmd}"; Parameters: "/C ""taskkill /im ollama.exe /f /t"; Flags: runhidden
|
|
Filename: "taskkill"; Parameters: "/im ""{#MyAppExeName}"" /f /t"; Flags: runhidden
|
|
Filename: "taskkill"; Parameters: "/im ""ollama.exe"" /f /t"; Flags: runhidden
|
|
; HACK! need to give the server and app enough time to exit
|
|
; TODO - convert this to a Pascal code script so it waits until they're no longer running, then completes
|
|
Filename: "{cmd}"; Parameters: "/c timeout 5"; Flags: runhidden
|
|
|
|
[UninstallDelete]
|
|
Type: filesandordirs; Name: "{%TEMP}\ollama*"
|
|
Type: filesandordirs; Name: "{%LOCALAPPDATA}\Ollama"
|
|
Type: filesandordirs; Name: "{%LOCALAPPDATA}\Programs\Ollama"
|
|
Type: filesandordirs; Name: "{%USERPROFILE}\.ollama\history"
|
|
Type: filesandordirs; Name: "{userstartup}\{#MyAppName}.lnk"
|
|
; NOTE: if the user has a custom OLLAMA_MODELS it will be preserved
|
|
|
|
[InstallDelete]
|
|
Type: filesandordirs; Name: "{%TEMP}\ollama*"
|
|
Type: filesandordirs; Name: "{app}\lib\ollama"
|
|
|
|
[Messages]
|
|
WizardReady=Ollama
|
|
ReadyLabel1=%nLet's get you up and running with your own large language models.
|
|
SetupAppRunningError=Another Ollama installer is running.%n%nPlease cancel or finish the other installer, then click OK to continue with this install, or Cancel to exit.
|
|
|
|
|
|
;FinishedHeadingLabel=Run your first model
|
|
;FinishedLabel=%nRun this command in a PowerShell or cmd terminal.%n%n%n ollama run llama3.2
|
|
;ClickFinish=%n
|
|
|
|
[Registry]
|
|
Root: HKCU; Subkey: "Environment"; \
|
|
ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"; \
|
|
Check: NeedsAddPath('{app}')
|
|
; Register ollama:// URL protocol
|
|
Root: HKCU; Subkey: "Software\Classes\ollama"; ValueType: string; ValueName: ""; ValueData: "URL:Ollama Protocol"; Flags: uninsdeletekey
|
|
Root: HKCU; Subkey: "Software\Classes\ollama"; ValueType: string; ValueName: "URL Protocol"; ValueData: ""; Flags: uninsdeletekey
|
|
Root: HKCU; Subkey: "Software\Classes\ollama\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""%1"""; Flags: uninsdeletekey
|
|
|
|
[Code]
|
|
|
|
function NeedsAddPath(Param: string): boolean;
|
|
var
|
|
OrigPath: string;
|
|
begin
|
|
if not RegQueryStringValue(HKEY_CURRENT_USER,
|
|
'Environment',
|
|
'Path', OrigPath)
|
|
then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
{ look for the path with leading and trailing semicolon }
|
|
{ Pos() returns 0 if not found }
|
|
Result := Pos(';' + ExpandConstant(Param) + ';', ';' + OrigPath + ';') = 0;
|
|
end;
|
|
|
|
{ --- VC Runtime libraries discovery code - Only install vc_redist if it isn't already installed ----- }
|
|
const VCRTL_MIN_V1 = 14;
|
|
const VCRTL_MIN_V2 = 40;
|
|
const VCRTL_MIN_V3 = 33807;
|
|
const VCRTL_MIN_V4 = 0;
|
|
|
|
// check if the minimum required vc redist is installed (by looking the registry)
|
|
function vc_redist_needed (): Boolean;
|
|
var
|
|
sRegKey: string;
|
|
v1: Cardinal;
|
|
v2: Cardinal;
|
|
v3: Cardinal;
|
|
v4: Cardinal;
|
|
begin
|
|
if (IsArm64()) then begin
|
|
sRegKey := 'SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\arm64';
|
|
end else begin
|
|
sRegKey := 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64';
|
|
end;
|
|
if (RegQueryDWordValue (HKEY_LOCAL_MACHINE, sRegKey, 'Major', v1) and
|
|
RegQueryDWordValue (HKEY_LOCAL_MACHINE, sRegKey, 'Minor', v2) and
|
|
RegQueryDWordValue (HKEY_LOCAL_MACHINE, sRegKey, 'Bld', v3) and
|
|
RegQueryDWordValue (HKEY_LOCAL_MACHINE, sRegKey, 'RBld', v4)) then
|
|
begin
|
|
Log ('VC Redist version: ' + IntToStr (v1) +
|
|
'.' + IntToStr (v2) + '.' + IntToStr (v3) +
|
|
'.' + IntToStr (v4));
|
|
{ Version info was found. Return true if later or equal to our
|
|
minimal required version RTL_MIN_Vx }
|
|
Result := not (
|
|
(v1 > VCRTL_MIN_V1) or ((v1 = VCRTL_MIN_V1) and
|
|
((v2 > VCRTL_MIN_V2) or ((v2 = VCRTL_MIN_V2) and
|
|
((v3 > VCRTL_MIN_V3) or ((v3 = VCRTL_MIN_V3) and
|
|
(v4 >= VCRTL_MIN_V4)))))));
|
|
end
|
|
else
|
|
Result := TRUE;
|
|
end;
|
|
|
|
function GetDirSize(Path: String): Int64;
|
|
var
|
|
FindRec: TFindRec;
|
|
FilePath: string;
|
|
Size: Int64;
|
|
begin
|
|
if FindFirst(Path + '\*', FindRec) then begin
|
|
Result := 0;
|
|
try
|
|
repeat
|
|
if (FindRec.Name <> '.') and (FindRec.Name <> '..') then begin
|
|
FilePath := Path + '\' + FindRec.Name;
|
|
if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then begin
|
|
Size := GetDirSize(FilePath);
|
|
end else begin
|
|
Size := Int64(FindRec.SizeHigh) shl 32 + FindRec.SizeLow;
|
|
end;
|
|
Result := Result + Size;
|
|
end;
|
|
until not FindNext(FindRec);
|
|
finally
|
|
FindClose(FindRec);
|
|
end;
|
|
end else begin
|
|
Log(Format('Failed to list %s', [Path]));
|
|
Result := -1;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
DeleteModelsChecked: Boolean;
|
|
ModelsDir: string;
|
|
|
|
procedure InitializeUninstallProgressForm();
|
|
var
|
|
UninstallPage: TNewNotebookPage;
|
|
UninstallButton: TNewButton;
|
|
DeleteModelsCheckbox: TNewCheckBox;
|
|
OriginalPageNameLabel: string;
|
|
OriginalPageDescriptionLabel: string;
|
|
OriginalCancelButtonEnabled: Boolean;
|
|
OriginalCancelButtonModalResult: Integer;
|
|
ctrl: TWinControl;
|
|
ModelDirA: AnsiString;
|
|
ModelsSize: Int64;
|
|
begin
|
|
if not UninstallSilent then begin
|
|
ctrl := UninstallProgressForm.CancelButton;
|
|
UninstallButton := TNewButton.Create(UninstallProgressForm);
|
|
UninstallButton.Parent := UninstallProgressForm;
|
|
UninstallButton.Left := ctrl.Left - ctrl.Width - ScaleX(10);
|
|
UninstallButton.Top := ctrl.Top;
|
|
UninstallButton.Width := ctrl.Width;
|
|
UninstallButton.Height := ctrl.Height;
|
|
UninstallButton.TabOrder := ctrl.TabOrder;
|
|
UninstallButton.Caption := 'Uninstall';
|
|
UninstallButton.ModalResult := mrOK;
|
|
UninstallProgressForm.CancelButton.TabOrder := UninstallButton.TabOrder + 1;
|
|
UninstallPage := TNewNotebookPage.Create(UninstallProgressForm);
|
|
UninstallPage.Notebook := UninstallProgressForm.InnerNotebook;
|
|
UninstallPage.Parent := UninstallProgressForm.InnerNotebook;
|
|
UninstallPage.Align := alClient;
|
|
UninstallProgressForm.InnerNotebook.ActivePage := UninstallPage;
|
|
|
|
ctrl := UninstallProgressForm.StatusLabel;
|
|
with TNewStaticText.Create(UninstallProgressForm) do begin
|
|
Parent := UninstallPage;
|
|
Top := ctrl.Top;
|
|
Left := ctrl.Left;
|
|
Width := ctrl.Width;
|
|
Height := ctrl.Height;
|
|
AutoSize := False;
|
|
ShowAccelChar := False;
|
|
Caption := '';
|
|
end;
|
|
|
|
if (DirExists(GetEnv('USERPROFILE') + '\.ollama\models\blobs')) then begin
|
|
ModelsDir := GetEnv('USERPROFILE') + '\.ollama\models';
|
|
ModelsSize := GetDirSize(ModelsDir);
|
|
end;
|
|
|
|
DeleteModelsCheckbox := TNewCheckBox.Create(UninstallProgressForm);
|
|
DeleteModelsCheckbox.Parent := UninstallPage;
|
|
DeleteModelsCheckbox.Top := ctrl.Top + ScaleY(30);
|
|
DeleteModelsCheckbox.Left := ctrl.Left;
|
|
DeleteModelsCheckbox.Width := ScaleX(300);
|
|
if ModelsSize > 1024*1024*1024 then begin
|
|
DeleteModelsCheckbox.Caption := 'Remove models (' + IntToStr(ModelsSize/(1024*1024*1024)) + ' GB) ' + ModelsDir;
|
|
end else if ModelsSize > 1024*1024 then begin
|
|
DeleteModelsCheckbox.Caption := 'Remove models (' + IntToStr(ModelsSize/(1024*1024)) + ' MB) ' + ModelsDir;
|
|
end else begin
|
|
DeleteModelsCheckbox.Caption := 'Remove models ' + ModelsDir;
|
|
end;
|
|
DeleteModelsCheckbox.Checked := True;
|
|
|
|
OriginalPageNameLabel := UninstallProgressForm.PageNameLabel.Caption;
|
|
OriginalPageDescriptionLabel := UninstallProgressForm.PageDescriptionLabel.Caption;
|
|
OriginalCancelButtonEnabled := UninstallProgressForm.CancelButton.Enabled;
|
|
OriginalCancelButtonModalResult := UninstallProgressForm.CancelButton.ModalResult;
|
|
|
|
UninstallProgressForm.PageNameLabel.Caption := '';
|
|
UninstallProgressForm.PageDescriptionLabel.Caption := '';
|
|
UninstallProgressForm.CancelButton.Enabled := True;
|
|
UninstallProgressForm.CancelButton.ModalResult := mrCancel;
|
|
|
|
if UninstallProgressForm.ShowModal = mrCancel then Abort;
|
|
|
|
UninstallButton.Visible := False;
|
|
UninstallProgressForm.PageNameLabel.Caption := OriginalPageNameLabel;
|
|
UninstallProgressForm.PageDescriptionLabel.Caption := OriginalPageDescriptionLabel;
|
|
UninstallProgressForm.CancelButton.Enabled := OriginalCancelButtonEnabled;
|
|
UninstallProgressForm.CancelButton.ModalResult := OriginalCancelButtonModalResult;
|
|
|
|
UninstallProgressForm.InnerNotebook.ActivePage := UninstallProgressForm.InstallingPage;
|
|
|
|
if DeleteModelsCheckbox.Checked then begin
|
|
DeleteModelsChecked:=True;
|
|
end else begin
|
|
DeleteModelsChecked:=False;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
|
|
begin
|
|
if CurUninstallStep = usDone then begin
|
|
if DeleteModelsChecked then begin
|
|
Log('user requested model cleanup');
|
|
if (VarIsEmpty(ModelsDir)) then begin
|
|
Log('cleaning up home directory models')
|
|
DelTree(GetEnv('USERPROFILE') + '\.ollama\models', True, True, True);
|
|
end else begin
|
|
Log('cleaning up custom directory models ' + ModelsDir)
|
|
DelTree(ModelsDir + '\blobs', True, True, True);
|
|
DelTree(ModelsDir + '\manifests', True, True, True);
|
|
end;
|
|
end else begin
|
|
Log('user requested to preserve model dir');
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TaskKill(FileName: String);
|
|
var
|
|
ResultCode: Integer;
|
|
begin
|
|
Exec('taskkill.exe', '/f /im ' + '"' + FileName + '"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
|
end;
|