@echo off setlocal chcp 65001 >nul cd /d "%~dp0" echo ============================================================ echo INV_WEB CLIENT AGENT INSTALLER WITH RUSTDESK echo ============================================================ echo. set "SPEC_FILE=client_agent_v2_windows_onefile.spec" set "CLIENT_DIR=dist\Inv_web Client" set "CLIENT_EXE=%CLIENT_DIR%\Inv_web Client.exe" set "INSTALLER_SCRIPT=client_agent_v2_installer.iss" set "RUSTDESK_INSTALLER=installer\prerequisites\rustdesk.exe" set "ISCC=%ProgramFiles(x86)%\Inno Setup 6\ISCC.exe" set "PYTHON_INSTALLER_VERSION=3.13.15" set "PYTHON_INSTALLER_URL=https://www.python.org/ftp/python/3.13.15/python-3.13.15-amd64.exe" set "PYTHON_INSTALLER_SHA256=edec09c4853aeae9ac36efb8c9f95b6b8e2fee65eee56d9767a8b7c69c574403" set "PYTHON_INSTALLER_FILE=%TEMP%\inventera-python-3.13.15-amd64.exe" set "PYTHON_INSTALLER_TARGET=%LocalAppData%\Programs\Python\Python313" set "PYTHON_INSTALL_LOG=%TEMP%\inventera-python-3.13.15-install.log" if not exist "%SPEC_FILE%" ( echo Spec file not found: %SPEC_FILE% goto :fail ) if not exist "%INSTALLER_SCRIPT%" ( echo Installer script not found: %INSTALLER_SCRIPT% goto :fail ) if not exist "%RUSTDESK_INSTALLER%" ( echo. echo ERROR: RustDesk installer was not found. echo Download the Windows x64 RustDesk installer, rename it to rustdesk.exe and place it here: echo %~dp0%RUSTDESK_INSTALLER% goto :fail ) if not exist "installer\configure_rustdesk.ps1" ( echo RustDesk configuration script not found: installer\configure_rustdesk.ps1 goto :fail ) echo Checking RustDesk configuration script... powershell.exe -NoProfile -NonInteractive -Command "$parseTokens = $null; $parseErrors = $null; [void][System.Management.Automation.Language.Parser]::ParseFile((Resolve-Path 'installer\configure_rustdesk.ps1'), [ref]$parseTokens, [ref]$parseErrors); if ($parseErrors.Count -gt 0) { foreach ($parseError in $parseErrors) { Write-Host $parseError }; exit 1 }" if errorlevel 1 goto :fail if not exist "%ISCC%" set "ISCC=%ProgramFiles%\Inno Setup 6\ISCC.exe" if not exist "%ISCC%" set "ISCC=%LocalAppData%\Programs\Inno Setup 6\ISCC.exe" if not exist "%ISCC%" for /f "delims=" %%I in ('where ISCC.exe 2^>nul') do set "ISCC=%%I" if not exist "%ISCC%" ( where winget >nul 2>nul if not errorlevel 1 ( echo Inno Setup 6 was not found. Installing it with winget... winget install --id JRSoftware.InnoSetup -e --silent --accept-package-agreements --accept-source-agreements ) ) if not exist "%ISCC%" set "ISCC=%ProgramFiles(x86)%\Inno Setup 6\ISCC.exe" if not exist "%ISCC%" set "ISCC=%ProgramFiles%\Inno Setup 6\ISCC.exe" if not exist "%ISCC%" set "ISCC=%LocalAppData%\Programs\Inno Setup 6\ISCC.exe" if not exist "%ISCC%" for /f "delims=" %%I in ('where ISCC.exe 2^>nul') do set "ISCC=%%I" if not exist "%ISCC%" ( echo. echo ERROR: Inno Setup 6 is not installed. echo Install it from: https://jrsoftware.org/isdl.php echo Then run this BAT file again. goto :fail ) echo Inno Setup compiler: %ISCC% rem The Python Install Manager runtime does not include Tcl/Tk. Use the rem classic CPython 3.13 distribution, which includes the complete Tk runtime. call :find_build_python if not defined BUILD_PYTHON ( where winget >nul 2>nul if not errorlevel 1 ( echo Classic Python 3.13 with Tcl/Tk was not found. Trying winget... winget install --id Python.Python.3.13 -e --scope user --silent --accept-package-agreements --accept-source-agreements ) ) call :find_build_python if not defined BUILD_PYTHON ( echo winget is unavailable or did not provide a usable Python with Tcl/Tk. call :install_classic_python if errorlevel 1 goto :fail ) call :find_build_python if not defined BUILD_PYTHON ( echo. echo ERROR: Python 3.13 with a working Tcl/Tk runtime was not found after installation. echo Python installer log: %PYTHON_INSTALL_LOG% goto :fail ) "%BUILD_PYTHON%" -c "import tkinter; t = tkinter.Tcl(); print(t.eval('info library'))" if errorlevel 1 ( echo The detected Python does not have a working Tcl/Tk installation: echo %BUILD_PYTHON% echo Reinstall classic Python 3.13 and enable the Tcl/Tk component. goto :fail ) for %%I in ("%BUILD_PYTHON%") do set "PYTHON_BASE=%%~dpI" set "TCL_LIBRARY=" for /d %%D in ("%PYTHON_BASE%tcl\tcl*") do if exist "%%~fD\init.tcl" set "TCL_LIBRARY=%%~fD" set "TK_LIBRARY=" for /d %%D in ("%PYTHON_BASE%tcl\tk*") do if exist "%%~fD\tk.tcl" set "TK_LIBRARY=%%~fD" if not defined TCL_LIBRARY ( echo Tcl runtime was not found under: %PYTHON_BASE%tcl echo Reinstall classic Python 3.13 and enable the Tcl/Tk component. goto :fail ) if not defined TK_LIBRARY ( echo Tk runtime was not found under: %PYTHON_BASE%tcl echo Reinstall classic Python 3.13 and enable the Tcl/Tk component. goto :fail ) echo Build Python: %BUILD_PYTHON% echo Tcl runtime: %TCL_LIBRARY% echo Tk runtime: %TK_LIBRARY% echo Installing Python build dependencies... "%BUILD_PYTHON%" -m pip install --upgrade "pyinstaller>=6.18" pystray pillow if errorlevel 1 goto :fail echo Building client executable... "%BUILD_PYTHON%" -m PyInstaller --clean --noconfirm "%SPEC_FILE%" if errorlevel 1 goto :fail if not exist "%CLIENT_EXE%" ( echo Client executable was not created: %CLIENT_EXE% goto :fail ) if not exist "%CLIENT_DIR%\_internal\_tcl_data\init.tcl" ( echo Tcl data was not included in the client build. goto :fail ) if not exist "%CLIENT_DIR%\_internal\_tk_data\tk.tcl" ( echo Tk data was not included in the client build. goto :fail ) echo Testing the packaged client runtime... start "" /wait "%CLIENT_EXE%" --help if errorlevel 1 ( echo The packaged client failed its startup test. goto :fail ) echo Building Windows installer... "%ISCC%" "%INSTALLER_SCRIPT%" if errorlevel 1 goto :fail echo. echo Done: %~dp0dist\Inv_web_Client_Setup.exe exit /b 0 :fail echo. echo ============================================================ echo BUILD FAILED. Review the error messages above. echo The window will remain open so the error can be copied. echo ============================================================ echo. pause exit /b 1 :find_build_python set "BUILD_PYTHON=" call :try_build_python "%LocalAppData%\Programs\Python\Python313\python.exe" call :try_build_python "%ProgramFiles%\Python313\python.exe" if not defined BUILD_PYTHON ( for /d %%D in ("%LocalAppData%\Programs\Python\Python313*") do call :try_build_python "%%~fD\python.exe" ) if not defined BUILD_PYTHON ( for /d %%D in ("%ProgramFiles%\Python313*") do call :try_build_python "%%~fD\python.exe" ) if not defined BUILD_PYTHON ( for /f "tokens=2,*" %%A in ('reg query "HKCU\Software\Python\PythonCore\3.13\InstallPath" /v ExecutablePath 2^>nul') do if /I "%%A"=="REG_SZ" call :try_build_python "%%B" ) if not defined BUILD_PYTHON ( for /f "tokens=2,*" %%A in ('reg query "HKCU\Software\Python\PythonCore\3.13-64\InstallPath" /v ExecutablePath 2^>nul') do if /I "%%A"=="REG_SZ" call :try_build_python "%%B" ) if not defined BUILD_PYTHON ( for /f "tokens=2,*" %%A in ('reg query "HKLM\Software\Python\PythonCore\3.13\InstallPath" /v ExecutablePath 2^>nul') do if /I "%%A"=="REG_SZ" call :try_build_python "%%B" ) if not defined BUILD_PYTHON ( for /f "tokens=2,*" %%A in ('reg query "HKLM\Software\Python\PythonCore\3.13-64\InstallPath" /v ExecutablePath 2^>nul') do if /I "%%A"=="REG_SZ" call :try_build_python "%%B" ) if not defined BUILD_PYTHON ( for /f "delims=" %%I in ('py -3.13 -c "import sys; print(sys.executable)" 2^>nul') do call :try_build_python "%%I" ) exit /b 0 :try_build_python if defined BUILD_PYTHON exit /b 0 if "%~1"=="" exit /b 0 if not exist "%~1" exit /b 0 "%~1" -c "import pathlib, sys, tkinter; assert sys.version_info[:2] == (3, 13); tcl = pathlib.Path(sys.base_prefix) / 'tcl'; assert list(tcl.glob('tcl*/init.tcl')); assert list(tcl.glob('tk*/tk.tcl')); print(tkinter.Tcl().eval('info patchlevel'))" >nul 2>nul if not errorlevel 1 set "BUILD_PYTHON=%~1" exit /b 0 :install_classic_python echo. echo Downloading official classic Python %PYTHON_INSTALLER_VERSION% x64 with Tcl/Tk... if exist "%PYTHON_INSTALLER_FILE%" del /q "%PYTHON_INSTALLER_FILE%" >nul 2>nul where curl.exe >nul 2>nul if errorlevel 1 goto :download_python_with_powershell curl.exe --fail --location --retry 3 --output "%PYTHON_INSTALLER_FILE%" "%PYTHON_INSTALLER_URL%" if not errorlevel 1 goto :verify_python_installer echo curl failed. Trying PowerShell download... :download_python_with_powershell powershell.exe -NoProfile -NonInteractive -Command "$ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -UseBasicParsing -Uri $env:PYTHON_INSTALLER_URL -OutFile $env:PYTHON_INSTALLER_FILE" if errorlevel 1 goto :python_download_failed :verify_python_installer if not exist "%PYTHON_INSTALLER_FILE%" goto :python_download_failed set "DOWNLOADED_PYTHON_SHA256=" for /f "delims=" %%H in ('powershell.exe -NoProfile -NonInteractive -Command "(Get-FileHash -LiteralPath $env:PYTHON_INSTALLER_FILE -Algorithm SHA256).Hash" 2^>nul') do set "DOWNLOADED_PYTHON_SHA256=%%H" if /I not "%DOWNLOADED_PYTHON_SHA256%"=="%PYTHON_INSTALLER_SHA256%" ( echo. echo ERROR: The downloaded Python installer checksum is invalid. echo Expected: %PYTHON_INSTALLER_SHA256% echo Received: %DOWNLOADED_PYTHON_SHA256% if exist "%PYTHON_INSTALLER_FILE%" del /q "%PYTHON_INSTALLER_FILE%" >nul 2>nul exit /b 1 ) echo Installing classic Python %PYTHON_INSTALLER_VERSION% for the current user... "%PYTHON_INSTALLER_FILE%" /quiet InstallAllUsers=0 TargetDir="%PYTHON_INSTALLER_TARGET%" AssociateFiles=0 PrependPath=0 Shortcuts=0 Include_launcher=0 Include_pip=1 Include_tcltk=1 Include_test=0 /log "%PYTHON_INSTALL_LOG%" if errorlevel 1 goto :classic_python_install_failed if exist "%PYTHON_INSTALLER_FILE%" del /q "%PYTHON_INSTALLER_FILE%" >nul 2>nul exit /b 0 :python_download_failed echo. echo ERROR: Failed to download the official Python installer: echo %PYTHON_INSTALLER_URL% echo Check the Internet connection and run this BAT file again. if exist "%PYTHON_INSTALLER_FILE%" del /q "%PYTHON_INSTALLER_FILE%" >nul 2>nul exit /b 1 :classic_python_install_failed echo. echo ERROR: Classic Python installation failed. echo Installer log: %PYTHON_INSTALL_LOG% if exist "%PYTHON_INSTALLER_FILE%" del /q "%PYTHON_INSTALLER_FILE%" >nul 2>nul exit /b 1