chore: add check and install for dotnet8 on preinstall
This commit is contained in:
343
src-tauri/nsis/dotnet.nsh
Normal file
343
src-tauri/nsis/dotnet.nsh
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
; A set of NSIS macros to check whether a dotnet core runtime is installed and, if not, offer to
|
||||||
|
; download and install it. Supports dotnet versions 3.1 and newer - latest tested version is 7.0.
|
||||||
|
;
|
||||||
|
; Inspired by & initially based on NsisDotNetChecker, which does the same thing for .NET framework
|
||||||
|
; https://github.com/alex-sitnikov/NsisDotNetChecker
|
||||||
|
|
||||||
|
!include "WordFunc.nsh"
|
||||||
|
!include "TextFunc.nsh"
|
||||||
|
!include "X64.nsh"
|
||||||
|
|
||||||
|
!ifndef DOTNETCORE_INCLUDED
|
||||||
|
!define DOTNETCORE_INCLUDED
|
||||||
|
|
||||||
|
; Check that a specific version of the dotnet core runtime is installed and, if not, attempts to
|
||||||
|
; install it
|
||||||
|
;
|
||||||
|
; \param Version The desired dotnet core runtime version as a 2 digit version. e.g. 3.1, 6.0, 7.0
|
||||||
|
!macro CheckDotNetCore Version
|
||||||
|
|
||||||
|
; Save registers
|
||||||
|
Push $R0
|
||||||
|
Push $R1
|
||||||
|
Push $R2
|
||||||
|
|
||||||
|
; Push and pop parameters so we don't have conflicts if parameters are $R#
|
||||||
|
Push ${Version}
|
||||||
|
Pop $R0 ; Version
|
||||||
|
|
||||||
|
!define ID ${__LINE__}
|
||||||
|
|
||||||
|
; Check current installed version
|
||||||
|
!insertmacro DotNetCoreGetInstalledVersion $R0 $R1
|
||||||
|
|
||||||
|
; If $R1 is blank then there is no version installed, otherwise it is installed
|
||||||
|
; todo in future we might want to support "must be at least 6.0.7", for now we only deal with "yes/no" for a major version (e.g. 6.0)
|
||||||
|
StrCmp $R1 "" notinstalled_${ID}
|
||||||
|
DetailPrint "dotnet version $R1 already installed"
|
||||||
|
Goto end_${ID}
|
||||||
|
|
||||||
|
notinstalled_${ID}:
|
||||||
|
DetailPrint "dotnet $R0 is not installed"
|
||||||
|
|
||||||
|
!insertmacro DotNetCoreGetLatestVersion $R0 $R1
|
||||||
|
DetailPrint "Latest Version of $R0 is $R1"
|
||||||
|
|
||||||
|
|
||||||
|
; Get number of input digits
|
||||||
|
; ${WordFind} $R1 "." "#" $R2
|
||||||
|
; DetailPrint "version parts count is $R2"
|
||||||
|
|
||||||
|
; ${WordFind} $R1 "." "+1" $R2
|
||||||
|
; DetailPrint "version part 1 is $R2"
|
||||||
|
|
||||||
|
; ${WordFind} $R1 "." "+2" $R2
|
||||||
|
; DetailPrint "version part 2 is $R2"
|
||||||
|
|
||||||
|
; ${WordFind} $R1 "." "+3" $R2
|
||||||
|
; DetailPrint "version part 3 is $R2"
|
||||||
|
|
||||||
|
!insertmacro DotNetCoreInstallVersion $R1
|
||||||
|
|
||||||
|
end_${ID}:
|
||||||
|
!undef ID
|
||||||
|
|
||||||
|
; Restore registers
|
||||||
|
Pop $R2
|
||||||
|
Pop $R1
|
||||||
|
Pop $R0
|
||||||
|
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; Gets the latest version of the runtime for a specified dotnet version. This uses the same endpoint
|
||||||
|
; as the dotnet-install scripts to determine the latest full version of a dotnet version
|
||||||
|
;
|
||||||
|
; \param[in] Version The desired dotnet core runtime version as a 2 digit version. e.g. 3.1, 6.0, 7.0
|
||||||
|
; \param[out] Result The full version number of the latest version - e.g. 6.0.7
|
||||||
|
!macro DotNetCoreGetLatestVersion Version Result
|
||||||
|
|
||||||
|
; Save registers
|
||||||
|
Push $R0
|
||||||
|
Push $R1
|
||||||
|
Push $R2
|
||||||
|
|
||||||
|
; Push and pop parameters so we don't have conflicts if parameters are $R#
|
||||||
|
Push ${Version}
|
||||||
|
Pop $R0 ; Version
|
||||||
|
|
||||||
|
StrCpy $R1 https://dotnetcli.azureedge.net/dotnet/WindowsDesktop/$R0/latest.version
|
||||||
|
DetailPrint "Querying latest version of dotnet $R0 from $R1"
|
||||||
|
|
||||||
|
; Fetch latest version of the desired dotnet version
|
||||||
|
; todo error handling in the PS script? so we can check for errors here
|
||||||
|
StrCpy $R2 "Write-Host (Invoke-WebRequest -UseBasicParsing -URI $\"$R1$\").Content;"
|
||||||
|
!insertmacro DotNetCorePSExec $R2 $R2
|
||||||
|
; $R2 contains latest version, e.g. 6.0.7
|
||||||
|
|
||||||
|
; todo error handling here
|
||||||
|
|
||||||
|
; Push the result onto the stack
|
||||||
|
${TrimNewLines} $R2 $R2
|
||||||
|
Push $R2
|
||||||
|
|
||||||
|
; Restore registers
|
||||||
|
Exch
|
||||||
|
Pop $R2
|
||||||
|
Exch
|
||||||
|
Pop $R1
|
||||||
|
Exch
|
||||||
|
Pop $R0
|
||||||
|
|
||||||
|
; Set result
|
||||||
|
Pop ${Result}
|
||||||
|
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro DotNetCoreGetInstalledVersion Version Result
|
||||||
|
!define DNC_INS_ID ${__LINE__}
|
||||||
|
|
||||||
|
; Save registers
|
||||||
|
Push $R0
|
||||||
|
Push $R1
|
||||||
|
Push $R2
|
||||||
|
|
||||||
|
; Push and pop parameters so we don't have conflicts if parameters are $R#
|
||||||
|
Push ${Version}
|
||||||
|
Pop $R0 ; Version
|
||||||
|
|
||||||
|
DetailPrint "Checking installed version of dotnet $R0"
|
||||||
|
|
||||||
|
StrCpy $R1 "dotnet --list-runtimes | % { if($$_ -match $\".*WindowsDesktop.*($R0.\d+).*$\") { $$matches[1] } } | Sort-Object {[int]($$_ -replace '\d.\d.(\d+)', '$$1')} -Descending | Select-Object -first 1"
|
||||||
|
!insertmacro DotNetCorePSExec $R1 $R1
|
||||||
|
; $R1 contains highest installed version, e.g. 6.0.7
|
||||||
|
|
||||||
|
${TrimNewLines} $R1 $R1
|
||||||
|
|
||||||
|
; If there is an installed version it should start with the same two "words" as the input version,
|
||||||
|
; otherwise assume we got an error response
|
||||||
|
|
||||||
|
; todo improve this simple test which checks there are at least 3 "words" separated by periods
|
||||||
|
${WordFind} $R1 "." "E#" $R2
|
||||||
|
IfErrors error_${DNC_INS_ID}
|
||||||
|
; $R2 contains number of version parts in R1 (dot separated words = version parts)
|
||||||
|
|
||||||
|
; If less than 3 parts, or more than 4 parts, error
|
||||||
|
IntCmp $R2 3 0 error_${DNC_INS_ID}
|
||||||
|
IntCmp $R2 4 0 0 error_${DNC_INS_ID}
|
||||||
|
|
||||||
|
; todo more error handling here / validation
|
||||||
|
|
||||||
|
; Seems to be OK, skip the "set to blank string" error handler
|
||||||
|
Goto end_${DNC_INS_ID}
|
||||||
|
|
||||||
|
error_${DNC_INS_ID}:
|
||||||
|
StrCpy $R1 "" ; Set result to blank string if any error occurs (means not installed)
|
||||||
|
|
||||||
|
end_${DNC_INS_ID}:
|
||||||
|
!undef DNC_INS_ID
|
||||||
|
|
||||||
|
; Push the result onto the stack
|
||||||
|
Push $R1
|
||||||
|
|
||||||
|
; Restore registers
|
||||||
|
Exch
|
||||||
|
Pop $R2
|
||||||
|
Exch
|
||||||
|
Pop $R1
|
||||||
|
Exch
|
||||||
|
Pop $R0
|
||||||
|
|
||||||
|
; Set result
|
||||||
|
Pop ${Result}
|
||||||
|
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro DotNetCoreInstallVersion Version
|
||||||
|
|
||||||
|
; Save registers
|
||||||
|
Push $R0
|
||||||
|
Push $R1
|
||||||
|
Push $R2
|
||||||
|
Push $R3
|
||||||
|
|
||||||
|
; Push and pop parameters so we don't have conflicts if parameters are $R#
|
||||||
|
Push ${Version}
|
||||||
|
Pop $R0 ; Version
|
||||||
|
|
||||||
|
${If} ${IsNativeAMD64}
|
||||||
|
StrCpy $R3 "x64"
|
||||||
|
${ElseIf} ${IsNativeARM64}
|
||||||
|
StrCpy $R3 "arm64"
|
||||||
|
${ElseIf} ${IsNativeIA32}
|
||||||
|
StrCpy $R3 "x86"
|
||||||
|
${Else}
|
||||||
|
StrCpy $R3 "unknown"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
; todo can download as a .zip, which is smaller, then we'd need to unzip it before running it...
|
||||||
|
StrCpy $R1 https://dotnetcli.azureedge.net/dotnet/WindowsDesktop/$R0/windowsdesktop-runtime-$R0-win-$R3.exe
|
||||||
|
|
||||||
|
; For dotnet versions less than 5 the WindowsDesktop runtime has a different path
|
||||||
|
${WordFind} $R0 "." "+1" $R2
|
||||||
|
IntCmp $R2 5 +2 0 +2
|
||||||
|
StrCpy $R1 https://dotnetcli.azureedge.net/dotnet/Runtime/$R0/windowsdesktop-runtime-$R0-win-$R3.exe
|
||||||
|
|
||||||
|
DetailPrint "Downloading dotnet $R0 from $R1"
|
||||||
|
|
||||||
|
; Create destination file
|
||||||
|
GetTempFileName $R2
|
||||||
|
nsExec::Exec 'cmd.exe /c rename "$R2" "$R2.exe"' ; Not using Rename to avoid spam in details log
|
||||||
|
Pop $R3 ; Pop exit code
|
||||||
|
StrCpy $R2 "$R2.exe"
|
||||||
|
|
||||||
|
; Fetch runtime installer
|
||||||
|
; todo error handling in the PS script? so we can check for errors here
|
||||||
|
StrCpy $R1 "Invoke-WebRequest -UseBasicParsing -URI $\"$R1$\" -OutFile $\"$R2$\""
|
||||||
|
!insertmacro DotNetCorePSExec $R1 $R1
|
||||||
|
; $R1 contains powershell script result
|
||||||
|
|
||||||
|
${WordFind} $R1 "BlobNotFound" "E+1{" $R3
|
||||||
|
ifErrors +3 0
|
||||||
|
DetailPrint "Dotnet installer $R0 not found."
|
||||||
|
Goto +10
|
||||||
|
|
||||||
|
; todo error handling for PS result, verify download result
|
||||||
|
|
||||||
|
|
||||||
|
IfFileExists $R2 +3, 0
|
||||||
|
DetailPrint "Dotnet installer did not download."
|
||||||
|
Goto +7
|
||||||
|
|
||||||
|
DetailPrint "Download complete"
|
||||||
|
|
||||||
|
DetailPrint "Installing dotnet $R0"
|
||||||
|
ExecWait "$\"$R2$\" /install /quiet /norestart" $R1
|
||||||
|
DetailPrint "Installer completed (Result: $R1)"
|
||||||
|
|
||||||
|
nsExec::Exec 'cmd.exe /c del "$R2"' ; Not using Delete to avoid spam in details log
|
||||||
|
Pop $R3 ; Pop exit code
|
||||||
|
|
||||||
|
; Error checking? Verify install result?
|
||||||
|
|
||||||
|
; Restore registers
|
||||||
|
Pop $R3
|
||||||
|
Pop $R2
|
||||||
|
Pop $R1
|
||||||
|
Pop $R0
|
||||||
|
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
; below is adapted from https://nsis.sourceforge.io/PowerShell_support but avoids using the plugin
|
||||||
|
; directory in favour of a temp file and providing a return variable rather than returning on the
|
||||||
|
; stack. Methods renamed to avoid conflicting with use of the original macros
|
||||||
|
|
||||||
|
; DotNetCorePSExec
|
||||||
|
; Executes a powershell script
|
||||||
|
;
|
||||||
|
; \param[in] PSCommand The powershell command or script to execute
|
||||||
|
; \param[out] Result The output from the powershell script
|
||||||
|
!macro DotNetCorePSExec PSCommand Result
|
||||||
|
|
||||||
|
Push ${PSCommand}
|
||||||
|
Call DotNetCorePSExecFn
|
||||||
|
Pop ${Result}
|
||||||
|
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
; DotNetCorePSExecFile
|
||||||
|
; Executes a powershell file
|
||||||
|
;
|
||||||
|
; \param[in] FilePath The path to the powershell script file to execute
|
||||||
|
; \param[out] Result The output from the powershell script
|
||||||
|
!macro DotNetCorePSExecFile FilePath Result
|
||||||
|
|
||||||
|
Push ${FilePath}
|
||||||
|
Call DotNetCorePSExecFileFn
|
||||||
|
Pop ${Result}
|
||||||
|
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
Function DotNetCorePSExecFn
|
||||||
|
|
||||||
|
; Read parameters and save registers
|
||||||
|
Exch $R0 ; Script
|
||||||
|
Push $R1
|
||||||
|
Push $R2
|
||||||
|
|
||||||
|
; Write the command into a temp file
|
||||||
|
; Note: Using GetTempFileName to get a temp file name, but since we need to have a .ps1 extension
|
||||||
|
; on the end we rename it with an extra file extension
|
||||||
|
GetTempFileName $R1
|
||||||
|
nsExec::Exec 'cmd.exe /c rename "$R1" "$R1.ps1"' ; Not using Rename to avoid spam in details log
|
||||||
|
Pop $R2 ; Pop exit code
|
||||||
|
StrCpy $R1 "$R1.ps1"
|
||||||
|
|
||||||
|
FileOpen $R2 $R1 w
|
||||||
|
FileWrite $R2 $R0
|
||||||
|
FileClose $R2
|
||||||
|
|
||||||
|
; Execute the powershell script and delete the temp file
|
||||||
|
Push $R1
|
||||||
|
Call DotNetCorePSExecFileFn
|
||||||
|
nsExec::Exec 'cmd.exe /c del "$R1"' ; Not using Delete to avoid spam in details log
|
||||||
|
Pop $R0 ; Pop exit code
|
||||||
|
|
||||||
|
; Restore registers
|
||||||
|
Exch
|
||||||
|
Pop $R2
|
||||||
|
Exch
|
||||||
|
Pop $R1
|
||||||
|
Exch
|
||||||
|
Pop $R0
|
||||||
|
|
||||||
|
; Stack contains script output only, which we leave as the function result
|
||||||
|
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
Function DotNetCorePSExecFileFn
|
||||||
|
|
||||||
|
; Read parameters and save registers
|
||||||
|
Exch $R0 ; FilePath
|
||||||
|
Push $R1
|
||||||
|
|
||||||
|
nsExec::ExecToStack 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$R0" '
|
||||||
|
; Stack contain exitCode, scriptOutput, registers
|
||||||
|
|
||||||
|
; Pop exit code & validate
|
||||||
|
Pop $R1
|
||||||
|
IntCmp $R1 0 +2
|
||||||
|
SetErrorLevel 2
|
||||||
|
|
||||||
|
; Restore registers
|
||||||
|
Exch
|
||||||
|
Pop $R1
|
||||||
|
Exch
|
||||||
|
Pop $R0
|
||||||
|
|
||||||
|
; Stack contains script output only, which we leave as the function result
|
||||||
|
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
!endif
|
@@ -1,3 +1,9 @@
|
|||||||
|
!incluide "dotnet.nsh"
|
||||||
|
|
||||||
|
!macro NSIS_HOOK_PREINSTALL
|
||||||
|
!insertmacro CheckDotNetCore 8.0
|
||||||
|
!macroend
|
||||||
|
|
||||||
!macro NSIS_HOOK_POSTINSTALL
|
!macro NSIS_HOOK_POSTINSTALL
|
||||||
${If} $PassiveMode = 1
|
${If} $PassiveMode = 1
|
||||||
${OrIf} ${Silent}
|
${OrIf} ${Silent}
|
@@ -44,7 +44,7 @@
|
|||||||
"nsis": {
|
"nsis": {
|
||||||
"installMode": "currentUser",
|
"installMode": "currentUser",
|
||||||
"compression": "lzma",
|
"compression": "lzma",
|
||||||
"installerHooks": "./nsis-hooks.nsh"
|
"installerHooks": "./nsis/nsis-hooks.nsh"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"icon": [
|
"icon": [
|
||||||
|
Reference in New Issue
Block a user