From c247c7fe2ff327173d82e0d141d53d054b234299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Mich=C3=A1lek?= Date: Thu, 26 Nov 2020 16:00:55 +0100 Subject: [PATCH] tools: add system check to verify Python versions and Windows Defender status --- .../tool_setup/Languages/idf_tool_en-US.islu | 40 + tools/windows/tool_setup/README.md | 8 + tools/windows/tool_setup/choice_page.iss.inc | 5 +- tools/windows/tool_setup/cmdline_page.iss.inc | 2 +- .../tool_setup/git_find_installed.iss.inc | 2 +- tools/windows/tool_setup/git_page.iss.inc | 2 +- .../tool_setup/idf_download_page.iss.inc | 2 +- tools/windows/tool_setup/idf_page.iss.inc | 2 +- tools/windows/tool_setup/idf_setup.iss.inc | 129 +++- tools/windows/tool_setup/idf_tool_setup.iss | 10 +- tools/windows/tool_setup/main.iss.inc | 2 +- .../tool_setup/python_find_installed.iss.inc | 59 +- tools/windows/tool_setup/python_page.iss.inc | 8 +- tools/windows/tool_setup/summary.iss.inc | 2 +- .../system_check/system_check_download.py | 12 + .../system_check/system_check_subprocess.py | 5 + .../system_check/system_check_virtualenv.py | 10 + .../tool_setup/system_check_page.iss.inc | 688 ++++++++++++++++++ tools/windows/tool_setup/utils.iss.inc | 2 +- 19 files changed, 890 insertions(+), 100 deletions(-) create mode 100644 tools/windows/tool_setup/Languages/idf_tool_en-US.islu create mode 100644 tools/windows/tool_setup/system_check/system_check_download.py create mode 100644 tools/windows/tool_setup/system_check/system_check_subprocess.py create mode 100644 tools/windows/tool_setup/system_check/system_check_virtualenv.py create mode 100644 tools/windows/tool_setup/system_check_page.iss.inc diff --git a/tools/windows/tool_setup/Languages/idf_tool_en-US.islu b/tools/windows/tool_setup/Languages/idf_tool_en-US.islu new file mode 100644 index 0000000000..53c95db00e --- /dev/null +++ b/tools/windows/tool_setup/Languages/idf_tool_en-US.islu @@ -0,0 +1,40 @@ +; Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD +; SPDX-License-Identifier: Apache-2.0 + +[LangOptions] +LanguageName=English +LanguageID=$0409 + +[CustomMessages] +PreInstallationCheckTitle=Pre-installation system check +PreInstallationCheckSubtitle=Verification of environment +SystemCheckStart=Starting system check ... +SystemCheckForDefender=Checking Windows Defender +SystemCheckHint=Hint +SystemCheckResultFound=FOUND +SystemCheckResultNotFound=NOT FOUND +SystemCheckResultOk=OK +SystemCheckResultFail=FAIL +SystemCheckResultError=ERR +SystemCheckResultWarn=WARN +SystemCheckStopped=Check stopped. +SystemCheckStopButtonCaption=Stop +SystemCheckComplete=Check complete. +SystemCheckForComponent=Checking installed +SystemCheckUnableToExecute=Unable to execute +SystemCheckUnableToFindFile=Unable to find file +SystemCheckRemedyMissingPip=Please use a supported version of Python available on the next screen. +SystemCheckRemedyMissingVirtualenv=Please install virtualenv and retry the installation. Suggested commands: +SystemCheckRemedyCreateVirtualenv=Please use the supported Python version that is available on the next screen. +SystemCheckRemedyPythonInVirtualenv=Please use the supported Python version that is available on the next screen. +SystemCheckRemedyBinaryPythonWheel=Please use the supported Python version that is available on the next screen. +SystemCheckRemedyFailedHttpsDownload=Please use the supported Python version that is available on the next screen. +SystemCheckRemedyFailedSubmoduleRun=Python contains a subprocess.run module intended for Python 2. Please uninstall the module. Suggested command: +SystemCheckApplyFixesButtonCaption=Apply Fixes +SystemCheckFullLogButtonCaption=Full log +SystemCheckApplyFixesConsent=Do you want to apply the commands with the suggested fixes to update your Windows environment and start a new System Check? +SystemCheckFixesSuccessful=Successful application of Fixes. +SystemCheckFixesFailed=Failed application of Fixes. Please refer to the Full log. +SystemCheckNotCompleteConsent=System check is not complete. Do you want to proceed by skipping checks? +SystemCheckRootCertificates=Checking certificates +SystemCheckRootCertificateWarning=Unable to load data from server dl.espressif.com. diff --git a/tools/windows/tool_setup/README.md b/tools/windows/tool_setup/README.md index d7118f695b..56739bfd92 100644 --- a/tools/windows/tool_setup/README.md +++ b/tools/windows/tool_setup/README.md @@ -14,6 +14,14 @@ Some functionality of the installer depends on additional programs: * [cmdlinerunner](cmdlinerunner/cmdlinerunner.c) — a helper DLL used to run external command line programs from the installer, capture live console output, and get the exit code. +## Instalation of dependencies via Chocolatey + +Run with Administrator privileges: + +``` +choco install inno-download-plugin +``` + ## Building the installer ### In Docker diff --git a/tools/windows/tool_setup/choice_page.iss.inc b/tools/windows/tool_setup/choice_page.iss.inc index 8291edc2fa..db1f25db43 100644 --- a/tools/windows/tool_setup/choice_page.iss.inc +++ b/tools/windows/tool_setup/choice_page.iss.inc @@ -1,9 +1,12 @@ +{ Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD + SPDX-License-Identifier: Apache-2.0 } + var ChoicePagePrepare: array of TNotifyEvent; ChoicePageSelectionChange: array of TNotifyEvent; ChoicePageValidate: array of TWizardPageButtonEvent; ChoicePageMaxTag: Integer; - ChoicePages: array of TInputOptionWizardPage; + ChoicePages: array of TWizardPage; procedure ChoicePageOnClickCheck(Sender: TObject); var diff --git a/tools/windows/tool_setup/cmdline_page.iss.inc b/tools/windows/tool_setup/cmdline_page.iss.inc index f8035027e1..099a4ebbf6 100644 --- a/tools/windows/tool_setup/cmdline_page.iss.inc +++ b/tools/windows/tool_setup/cmdline_page.iss.inc @@ -1,4 +1,4 @@ -{ Copyright 2019 Espressif Systems (Shanghai) PTE LTD +{ Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD SPDX-License-Identifier: Apache-2.0 } { ------------------------------ Progress & log page for command line tools ------------------------------ } diff --git a/tools/windows/tool_setup/git_find_installed.iss.inc b/tools/windows/tool_setup/git_find_installed.iss.inc index 328294cdd0..3ef24f2e4f 100644 --- a/tools/windows/tool_setup/git_find_installed.iss.inc +++ b/tools/windows/tool_setup/git_find_installed.iss.inc @@ -1,4 +1,4 @@ -{ Copyright 2019 Espressif Systems (Shanghai) PTE LTD +{ Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD SPDX-License-Identifier: Apache-2.0 } { ------------------------------ Find installed copies of Git ------------------------------ } diff --git a/tools/windows/tool_setup/git_page.iss.inc b/tools/windows/tool_setup/git_page.iss.inc index b9c1ef7d0f..1072260176 100644 --- a/tools/windows/tool_setup/git_page.iss.inc +++ b/tools/windows/tool_setup/git_page.iss.inc @@ -1,4 +1,4 @@ -{ Copyright 2019 Espressif Systems (Shanghai) PTE LTD +{ Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD SPDX-License-Identifier: Apache-2.0 } { ------------------------------ Page to select Git ------------------------------ } diff --git a/tools/windows/tool_setup/idf_download_page.iss.inc b/tools/windows/tool_setup/idf_download_page.iss.inc index 6f501c059f..d5b8bd0163 100644 --- a/tools/windows/tool_setup/idf_download_page.iss.inc +++ b/tools/windows/tool_setup/idf_download_page.iss.inc @@ -1,4 +1,4 @@ -{ Copyright 2019 Espressif Systems (Shanghai) PTE LTD +{ Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD SPDX-License-Identifier: Apache-2.0 } { ------------------------------ Page to select the version of ESP-IDF to download ------------------------------ } diff --git a/tools/windows/tool_setup/idf_page.iss.inc b/tools/windows/tool_setup/idf_page.iss.inc index e6baef9c8f..cec63da03f 100644 --- a/tools/windows/tool_setup/idf_page.iss.inc +++ b/tools/windows/tool_setup/idf_page.iss.inc @@ -1,4 +1,4 @@ -{ Copyright 2019 Espressif Systems (Shanghai) PTE LTD +{ Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD SPDX-License-Identifier: Apache-2.0 } { ------------------------------ Page to select whether to download ESP-IDF, or use an existing copy ------------------------------ } diff --git a/tools/windows/tool_setup/idf_setup.iss.inc b/tools/windows/tool_setup/idf_setup.iss.inc index 303c0f04e5..c8e301dbd7 100644 --- a/tools/windows/tool_setup/idf_setup.iss.inc +++ b/tools/windows/tool_setup/idf_setup.iss.inc @@ -1,4 +1,4 @@ -{ Copyright 2019 Espressif Systems (Shanghai) PTE LTD +{ Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD SPDX-License-Identifier: Apache-2.0 } { ------------------------------ Downloading ESP-IDF ------------------------------ } @@ -90,6 +90,24 @@ begin FindFileRecursive(Path + '\.git', 'alternates', @RemoveAlternatesFile); end; +{ + Run git config fileMode is repairing problem when git repo was zipped on Linux and extracted on Windows. + The repo and submodules are marked as dirty which confuses users that fresh installation already contains changes. + More information: https://mirrors.edge.kernel.org/pub/software/scm/git/docs/git-config.html +} +procedure GitRepoFixFileMode(Path: String); +var + CmdLine: String; +begin + CmdLine := GitExecutablePath + ' -C ' + Path + ' config --local core.fileMode false'; + Log('Setting core.fileMode on repository: ' + CmdLine); + DoCmdlineInstall('Finishing ESP-IDF installation', 'Updating fileMode', CmdLine); + + Log('Setting core.fileMode on repository for submodules: ' + CmdLine); + CmdLine := GitExecutablePath + ' -C ' + Path + ' submodule foreach --recursive git config --local core.fileMode false'; + DoCmdlineInstall('Finishing ESP-IDF installation', 'Updating fileMode in submodules', CmdLine); +end; + { Run git reset --hard in the repo and in the submodules, to fix the newlines. } procedure GitRepoFixNewlines(Path: String); var @@ -183,6 +201,7 @@ begin } CmdLine := ExpandConstant('cmd.exe /c ""xcopy" /s /e /i /h /q "' + IDFTempPath + '" "' + IDFPath + '""'); DoCmdlineInstall('Extracting ESP-IDF', 'Copying ESP-IDF into the destination directory', CmdLine); + GitRepoFixFileMode(IDFPath); GitRepoFixNewlines(IDFPath); DelTree(IDFTempPath, True, True, True); end; @@ -248,20 +267,95 @@ begin DoCmdlineInstall('Installing Python environment', '', CmdLine); end; +{ Find Major and Minor version in esp_idf_version.h file. } +function GetIDFVersionFromHeaderFile():String; +var + HeaderFileName: String; + HeaderLines: TArrayOfString; + LineIndex: Integer; + LineCount: Longint; + Line: String; + MajorVersion: String; + MinorVersion: String; +begin + HeaderFileName := GetIDFPath('') + '\components\esp_common\include\esp_idf_version.h'; + if (not FileExists(HeaderFileName)) then begin + Result := ''; + Exit; + end; + + LoadStringsFromFile(HeaderFileName, HeaderLines); + LineCount := GetArrayLength(HeaderLines); + for LineIndex := 0 to LineCount - 1 do begin + Line := HeaderLines[LineIndex]; + if (pos('define ESP_IDF_VERSION_MAJOR', Line) > 0) then begin + Delete(Line, 1, 29); + MajorVersion := Trim(Line); + end else if (pos('define ESP_IDF_VERSION_MINOR', Line) > 0) then begin + Delete(Line, 1, 29); + MinorVersion := Trim(Line); + Result := MajorVersion + '.' + MinorVersion; + Exit; + end + end; +end; + { ------------------------------ Start menu shortcut ------------------------------ } procedure CreateIDFCommandPromptShortcut(LnkString: String); var Destination: String; Description: String; + VersionIndex: Integer; + MajorString: String; + MinorString: String; + DotIndex: Integer; + IDFVersionString: String; + PythonVirtualEnvPath: String; Command: String; begin ForceDirectories(ExpandConstant(LnkString)); Destination := ExpandConstant(LnkString + '\{#IDFCmdExeShortcutFile}'); Description := '{#IDFCmdExeShortcutDescription}'; + + IDFVersionString := IDFDownloadVersion; + { Transform version vx.y or release/vx.y to x.y } + VersionIndex := pos('v', IDFVersionString); + if (VersionIndex > 0) then begin + Delete(IDFVersionString, 1, VersionIndex); + end; + + { Transform version x.y.z to x.y } + DotIndex := pos('.', IDFVersionString); + if (DotIndex > 0) then begin + MajorString := Copy(IDFVersionString, 1, DotIndex - 1); + Delete(IDFVersionString, 1, DotIndex); + { Trim trailing version numbers. } + DotIndex := pos('.', IDFVersionString); + if (DotIndex > 0) then begin + MinorString := Copy(IDFVersionString, 1, DotIndex - 1); + IDFVersionString := MajorString + '.' + MinorString; + end else begin + IDFVersionString := MajorString + '.' + IDFVersionString; + end; + end; + + { Transform master to x.y } + if (IDFVersionString = 'master') then begin + IDFVersionString := GetIDFVersionFromHeaderFile(); + end; + + { The links should contain reference to Python vitual env } + PythonVirtualEnvPath := ExpandConstant('{app}\python_env\idf') + IDFVersionString + '_py' + PythonVersion + '_env\Scripts'; + + { Fallback in case of not existing environment. } + if (not FileExists(PythonVirtualEnvPath + '\python.exe')) then begin + PythonVirtualEnvPath := PythonPath; + end; + { If cmd.exe command argument starts with a quote, the first and last quote chars in the command will be removed by cmd.exe; each argument needs to be surrounded by quotes as well. } - Command := ExpandConstant('/k ""{app}\idf_cmd_init.bat" "') + PythonPath + '" "' + GitPath + '""'; + Command := ExpandConstant('/k ""{app}\idf_cmd_init.bat" "') + PythonVirtualEnvPath + '" "' + GitPath + '""'; Log('CreateShellLink Destination=' + Destination + ' Description=' + Description + ' Command=' + Command) try CreateShellLink( @@ -290,47 +384,22 @@ begin end; + procedure CheckWinDefenderAvailable(CurPageID: Integer); var - bHasWD: Boolean; - szHasWD: String; - szWDPath: String; - listPSModulePath: TStringList; x: Integer; begin if CurPageID = wpSelectTasks then begin - listPSModulePath := TStringList.Create; - listPSModulePath.Delimiter := ';'; - listPSModulePath.StrictDelimiter := True; - listPSModulePath.DelimitedText := GetEnv('PsModulePath'); - - Log('Checking PSMODULEPATH for Windows Defender module...'); - - for x:=0 to (listPSModulePath.Count-1) do - begin - szWDPath := listPSModulePath[x] + '\Defender' - bHasWD := DirExists(szWDPath); - if bHasWD then - begin - szHasWD := 'YES (' + szWDPath + ')'; - Break; - end - else - szHasWD := 'NO'; - end; - - Log('CheckWinDefenderAvailable: ' + szHasWD); - { WD registration checkbox is identified by 'Windows Defender' substring anywhere in its caption. Please, keep this in mind when making changes } for x:=0 to (WizardForm.TasksList.Items.Count-1) do begin if Pos('Windows Defender', WizardForm.TasksList.ItemCaption[x]) > 0 then begin - WizardForm.TasksList.ItemEnabled[x] := bHasWD; - WizardForm.TasksList.Checked[x] := bHasWD; + WizardForm.TasksList.ItemEnabled[x] := isWindowsDefenderEnabled; + WizardForm.TasksList.Checked[x] := isWindowsDefenderEnabled; break; end; end; diff --git a/tools/windows/tool_setup/idf_tool_setup.iss b/tools/windows/tool_setup/idf_tool_setup.iss index 83eb25f2e2..8dc68fc46a 100644 --- a/tools/windows/tool_setup/idf_tool_setup.iss +++ b/tools/windows/tool_setup/idf_tool_setup.iss @@ -1,4 +1,4 @@ -; Copyright 2019 Espressif Systems (Shanghai) PTE LTD +; Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD ; SPDX-License-Identifier: Apache-2.0 #pragma include __INCLUDE__ + ";" + ReadReg(HKLM, "Software\Mitrich Software\Inno Download Plugin", "InstallDir") @@ -51,7 +51,7 @@ ChangesEnvironment=yes WizardStyle=modern [Languages] -Name: "english"; MessagesFile: "compiler:Default.isl" +Name: "english"; MessagesFile: "compiler:Default.isl,Languages/idf_tool_en-US.islu" [Dirs] Name: "{app}\dist" @@ -65,6 +65,11 @@ Source: "..\..\idf_tools.py"; DestDir: "{app}"; DestName: "idf_tools_fallback.py Source: "tools_fallback.json"; DestDir: "{app}"; DestName: "tools_fallback.json" Source: "idf_cmd_init.bat"; DestDir: "{app}" Source: "dist\*"; DestDir: "{app}\dist" +; Helper Python files for sanity check of Python environment - used by system_check_page +Source: "system_check\system_check_download.py"; Flags: dontcopy +Source: "system_check\system_check_subprocess.py"; Flags: dontcopy +Source: "system_check\system_check_virtualenv.py"; Flags: dontcopy +; Helper PowerShell scripts for managing exceptions in Windows Defender Source: "tools_WD_excl.ps1"; DestDir: "{app}\dist" Source: "tools_WD_clean.ps1"; DestDir: "{app}\dist" @@ -104,6 +109,7 @@ Root: HKCU; Subkey: "Environment"; ValueType: string; ValueName: "IDF_TOOLS_PATH #include "idf_page.iss.inc" #include "git_page.iss.inc" #include "python_page.iss.inc" +#include "system_check_page.iss.inc" #include "idf_download_page.iss.inc" #include "idf_setup.iss.inc" #include "summary.iss.inc" diff --git a/tools/windows/tool_setup/main.iss.inc b/tools/windows/tool_setup/main.iss.inc index d54d6d9828..54b371e4aa 100644 --- a/tools/windows/tool_setup/main.iss.inc +++ b/tools/windows/tool_setup/main.iss.inc @@ -1,4 +1,4 @@ -{ Copyright 2019 Espressif Systems (Shanghai) PTE LTD +{ Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD SPDX-License-Identifier: Apache-2.0 } { ------------------------------ Custom steps before the main installation flow ------------------------------ } diff --git a/tools/windows/tool_setup/python_find_installed.iss.inc b/tools/windows/tool_setup/python_find_installed.iss.inc index 5b786fca3f..43f7e1a468 100644 --- a/tools/windows/tool_setup/python_find_installed.iss.inc +++ b/tools/windows/tool_setup/python_find_installed.iss.inc @@ -1,4 +1,4 @@ -{ Copyright 2019 Espressif Systems (Shanghai) PTE LTD +{ Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD SPDX-License-Identifier: Apache-2.0 } { ------------------------------ Find installed Python interpreters in Windows Registry (see PEP 514) ------------------------------ } @@ -19,7 +19,8 @@ end; function GetPythonVersionInfoFromKey(RootKey: Integer; SubKeyName, CompanyName, TagName: String; var Version: String; var DisplayName: String; - var ExecutablePath: String): Boolean; + var ExecutablePath: String; + var BaseDir: String): Boolean; var TagKey, InstallPathKey, DefaultPath: String; begin @@ -39,6 +40,8 @@ begin ExecutablePath := DefaultPath + '\python.exe'; end; + BaseDir := DefaultPath; + if not RegQueryStringValue(RootKey, TagKey, 'SysVersion', Version) then begin if CompanyName = 'PythonCore' then @@ -59,55 +62,3 @@ begin Result := True; end; - -procedure FindPythonVersionsFromKey(RootKey: Integer; SubKeyName: String); -var - CompanyNames: TArrayOfString; - CompanyName, CompanySubKey, TagName, TagSubKey: String; - ExecutablePath, DisplayName, Version: String; - TagNames: TArrayOfString; - CompanyId, TagId: Integer; -begin - if not RegGetSubkeyNames(RootKey, SubKeyName, CompanyNames) then - begin - Log('Nothing found in ' + IntToStr(RootKey) + '\' + SubKeyName); - Exit; - end; - - for CompanyId := 0 to GetArrayLength(CompanyNames) - 1 do - begin - CompanyName := CompanyNames[CompanyId]; - - if CompanyName = 'PyLauncher' then - continue; - - CompanySubKey := SubKeyName + '\' + CompanyName; - Log('In ' + IntToStr(RootKey) + '\' + CompanySubKey); - - if not RegGetSubkeyNames(RootKey, CompanySubKey, TagNames) then - continue; - - for TagId := 0 to GetArrayLength(TagNames) - 1 do - begin - TagName := TagNames[TagId]; - TagSubKey := CompanySubKey + '\' + TagName; - Log('In ' + IntToStr(RootKey) + '\' + TagSubKey); - - if not GetPythonVersionInfoFromKey(RootKey, SubKeyName, CompanyName, TagName, Version, DisplayName, ExecutablePath) then - continue; - - PythonVersionAdd(Version, DisplayName, ExecutablePath); - end; - end; -end; - -procedure FindInstalledPythonVersions(); -begin - InstalledPythonVersions := TStringList.Create(); - InstalledPythonDisplayNames := TStringList.Create(); - InstalledPythonExecutables := TStringList.Create(); - - FindPythonVersionsFromKey(HKEY_CURRENT_USER, 'Software\Python'); - FindPythonVersionsFromKey(HKEY_LOCAL_MACHINE, 'Software\Python'); - FindPythonVersionsFromKey(HKEY_LOCAL_MACHINE, 'Software\Wow6432Node\Python'); -end; diff --git a/tools/windows/tool_setup/python_page.iss.inc b/tools/windows/tool_setup/python_page.iss.inc index a0325fc7a1..3a9da659ac 100644 --- a/tools/windows/tool_setup/python_page.iss.inc +++ b/tools/windows/tool_setup/python_page.iss.inc @@ -1,4 +1,4 @@ -{ Copyright 2019 Espressif Systems (Shanghai) PTE LTD +{ Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD SPDX-License-Identifier: Apache-2.0 } { ------------------------------ Page to select Python interpreter ------------------------------ } @@ -50,8 +50,6 @@ begin if Page.CheckListBox.Items.Count > 0 then exit; - FindInstalledPythonVersions(); - VersionToInstall := '{#PythonVersion}'; OfferToInstall := True; FirstEnabledIndex := -1; @@ -119,11 +117,11 @@ end; procedure PythonExecutablePathUpdateAfterInstall(); var - Version, DisplayName, ExecutablePath: String; + Version, DisplayName, ExecutablePath, BaseDir: String; begin if not GetPythonVersionInfoFromKey( HKEY_CURRENT_USER, 'Software\Python', 'PythonCore', '{#PythonVersion}', - Version, DisplayName, ExecutablePath) then + Version, DisplayName, ExecutablePath, BaseDir) then begin Log('Failed to find ExecutablePath for the installed copy of Python'); exit; diff --git a/tools/windows/tool_setup/summary.iss.inc b/tools/windows/tool_setup/summary.iss.inc index 6a5923601d..5b7baf79e8 100644 --- a/tools/windows/tool_setup/summary.iss.inc +++ b/tools/windows/tool_setup/summary.iss.inc @@ -1,4 +1,4 @@ -{ Copyright 2019 Espressif Systems (Shanghai) PTE LTD +{ Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD SPDX-License-Identifier: Apache-2.0 } { ------------------------------ Installation summary page ------------------------------ } diff --git a/tools/windows/tool_setup/system_check/system_check_download.py b/tools/windows/tool_setup/system_check/system_check_download.py new file mode 100644 index 0000000000..0dd76a2251 --- /dev/null +++ b/tools/windows/tool_setup/system_check/system_check_download.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +import sys +download_url = sys.argv[1] +output_filename = sys.argv[2] +if (sys.version_info > (3, 0)): + import urllib.request + urllib.request.urlretrieve(download_url, output_filename) +else: + import urllib2 + response = urllib2.urlopen(download_url) + with open(output_filename, "w") as output_file: + output_file.write(response.read()) diff --git a/tools/windows/tool_setup/system_check/system_check_subprocess.py b/tools/windows/tool_setup/system_check/system_check_subprocess.py new file mode 100644 index 0000000000..d7309a0984 --- /dev/null +++ b/tools/windows/tool_setup/system_check/system_check_subprocess.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +import sys +if (sys.version_info > (3, 0)): + import subprocess + subprocess.run("cmd /c echo hello") diff --git a/tools/windows/tool_setup/system_check/system_check_virtualenv.py b/tools/windows/tool_setup/system_check/system_check_virtualenv.py new file mode 100644 index 0000000000..669d8e77fe --- /dev/null +++ b/tools/windows/tool_setup/system_check/system_check_virtualenv.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +import sys +expected_executable = sys.argv[1] +active_executable = sys.executable +if expected_executable != active_executable: + print("Failure. Expected executable does not match current executable.") + print("Expected:", expected_executable) + print("Active: ", active_executable) + sys.exit(1) diff --git a/tools/windows/tool_setup/system_check_page.iss.inc b/tools/windows/tool_setup/system_check_page.iss.inc new file mode 100644 index 0000000000..05d1d5cb39 --- /dev/null +++ b/tools/windows/tool_setup/system_check_page.iss.inc @@ -0,0 +1,688 @@ +{ Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD + SPDX-License-Identifier: Apache-2.0 } + +{ SystemCheck states } +const + SYSTEM_CHECK_STATE_INIT = 0; { No check was executed yet. } + SYSTEM_CHECK_STATE_RUNNING = 1; { Check is in progress and can be cancelled. } + SYSTEM_CHECK_STATE_COMPLETE = 2; { Check is complete. } + SYSTEM_CHECK_STATE_STOPPED = 3; { User stopped the check. } + +var + { RTF View to display content of system check. } + SystemCheckViewer: TNewMemo; + { Indicate state of System Check. } + SystemCheckState:Integer; + { Text representation of log messages which are then converte to RTF. } + SystemLogText: TStringList; + { Message for user which gives a hint how to correct the problem. } + SystemCheckHint: String; + { Setup Page which displays progress/result of system check. } + SystemCheckPage: TOutputMsgWizardPage; + { TimeCounter for Spinner animation invoked during command execution. } + TimeCounter:Integer; + { Spinner is TStringList, because characters like backslash must be escaped and stored on two bytes. } + Spinner: TStringList; + { Button to request display of full log of system check/installation. } + FullLogButton: TNewButton; + { Button to request application of available fixtures. } + ApplyFixesButton: TNewButton; + { Commands which should be executed to fix problems discovered during system check. } + Fixes: TStringList; + { Button to request Stop of System Checks manually. } + StopSystemCheckButton: TNewButton; + { Count number of createde virtualenv to avoid collision with previous runs. } + VirtualEnvCounter: Integer; + +{ Indicates whether system check was able to find running Windows Defender. } +var IsWindowsDefenderEnabled: Boolean; + +{ Const values for user32.dll which allows scrolling of the text view. } +const + WM_VSCROLL = $0115; + SB_BOTTOM = 7; + +type + TMsg = record + hwnd: HWND; + message: UINT; + wParam: Longint; + lParam: Longint; + time: DWORD; + pt: TPoint; + end; + +const + PM_REMOVE = 1; + +{ Functions to communicate via Windows API. } +function PeekMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): BOOL; external 'PeekMessageW@user32.dll stdcall'; +function TranslateMessage(const lpMsg: TMsg): BOOL; external 'TranslateMessage@user32.dll stdcall'; +function DispatchMessage(const lpMsg: TMsg): Longint; external 'DispatchMessageW@user32.dll stdcall'; + +procedure AppProcessMessage; +var + Msg: TMsg; +begin + while PeekMessage(Msg, WizardForm.Handle, 0, 0, PM_REMOVE) do begin + TranslateMessage(Msg); + DispatchMessage(Msg); + end; +end; + +{ Render text message for view, add spinner if necessary and scroll the window. } +procedure SystemLogRefresh(); +begin + SystemCheckViewer.Lines := SystemLogText; + + { Add Spinner to message. } + if ((TimeCounter > 0) and (TimeCounter < 6)) then begin + SystemCheckViewer.Lines[SystemCheckViewer.Lines.Count - 1] := SystemCheckViewer.Lines[SystemCheckViewer.Lines.Count - 1] + ' [' + Spinner[TimeCounter - 1] + ']'; + end; + + { Scroll window to the bottom of the log - https://stackoverflow.com/questions/64587596/is-it-possible-to-display-the-install-actions-in-a-list-in-inno-setup } + SendMessage(SystemCheckViewer.Handle, WM_VSCROLL, SB_BOTTOM, 0); +end; + +{ Log message to file and display just a '.' to user so that user is not overloaded by details. } +procedure SystemLogProgress(message:String); +begin + Log(message); + if (SystemLogText.Count = 0) then begin + SystemLogText.Append(''); + end; + + SystemLogText[SystemLogText.Count - 1] := SystemLogText[SystemLogText.Count - 1] + '.'; + SystemLogRefresh(); +end; + +{ Log message to file and display it to user as title message with asterisk prefix. } +procedure SystemLogTitle(message:String); +begin + message := '* ' + message; + Log(message); + SystemLogText.Append(message); + SystemLogRefresh(); +end; + +{ Log message to file and display it to user. } +procedure SystemLog(message:String); +begin + Log(message); + if (SystemLogText.Count = 0) then begin + SystemLogText.Append(''); + end; + + SystemLogText[SystemLogText.Count - 1] := SystemLogText[SystemLogText.Count - 1] + message; + SystemLogRefresh(); +end; + +{ Process timer tick during command execution so that the app keeps communicating with user. } +procedure TimerTick(); +begin + { TimeCounter for animating Spinner. } + TimeCounter:=TimeCounter+1; + if (TimeCounter = 5) then begin + TimeCounter := 1; + end; + + { Redraw Log with Spinner animation. } + SystemLogRefresh(); + + { Give control back to UI so that it can be updated. https://gist.github.com/jakoch/33ac13800c17eddb2dd4 } + AppProcessMessage; +end; + +{ --- Command line nonblocking exec --- } +function NonBlockingExec(command, workdir: String): Integer; +var + Res: Integer; + Handle: Longword; + ExitCode: Integer; + LogTextAnsi: AnsiString; + LogText, LeftOver: String; + +begin + if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin + ExitCode := -3; + Exit; + end; + try + ExitCode := -1; + { SystemLog('Workdir: ' + workdir); } + SystemLogProgress(' $ ' + command); + Handle := ProcStart(command, workdir) + if Handle = 0 then + begin + SystemLog('[' + CustomMessage('SystemCheckResultError') + ']'); + Result := -2; + Exit; + end; + while (ExitCode = -1) and (SystemCheckState <> SYSTEM_CHECK_STATE_STOPPED) do + begin + ExitCode := ProcGetExitCode(Handle); + SetLength(LogTextAnsi, 4096); + Res := ProcGetOutput(Handle, LogTextAnsi, 4096) + if Res > 0 then + begin + SetLength(LogTextAnsi, Res); + LogText := LeftOver + String(LogTextAnsi); + SystemLogProgress(LogText); + end; + TimerTick(); + Sleep(200); + end; + ProcEnd(Handle); + finally + if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then + begin + Result := -1; + end else begin + Result := ExitCode; + end; + end; +end; + +{ Execute command for SystemCheck and reset timer so that Spinner will disappear after end of execution. } +function SystemCheckExec(command, workdir: String): Integer; +begin + TimeCounter := 0; + Result := NonBlockingExec(command, workdir); + TimeCounter := 0; +end; + +{ Get formated line from SystemCheck for user. } +function GetSystemCheckHint(Command: String; CustomCheckMessageKey:String):String; +begin + Result := CustomMessage('SystemCheckUnableToExecute') + ' ' + Command + #13#10 + CustomMessage(CustomCheckMessageKey); +end; + +{ Add command to list of fixes which can be executed by installer. } +procedure AddFix(Command:String); +begin + { Do not add possible fix command when check command was stopped by user. } + if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin + Exit; + end; + Fixes.Append(Command); +end; + +{ Execute checks to determine whether Python installation is valid so thet user can choose it to install IDF. } +function IsPythonInstallationValid(displayName: String; pythonPath:String): Boolean; +var + ResultCode: Integer; + ScriptFile: String; + TempDownloadFile: String; + Command: String; + VirtualEvnPath: String; + VirtualEnvPython: String; + RemedyCommand: String; +begin + SystemLogTitle(CustomMessage('SystemCheckForComponent') + ' ' + displayName + ' '); + SystemCheckHint := ''; + + pythonPath := pythonPath + ' '; + + Command := pythonPath + '-m pip --version'; + ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}')); + if (ResultCode <> 0) then begin + SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyMissingPip'); + Result := False; + Exit; + end; + + Command := pythonPath + '-m virtualenv --version'; + ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}')); + if (ResultCode <> 0) then begin + SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyMissingVirtualenv') + #13#10 + pythonPath + '-m pip install --upgrade pip' + #13#10 + pythonPath + '-m pip install virtualenv'; + AddFix(pythonPath + '-m pip install --upgrade pip'); + AddFix(pythonPath + '-m pip install virtualenv'); + Result := False; + Exit; + end; + + VirtualEnvCounter := VirtualEnvCounter + 1; + VirtualEvnPath := ExpandConstant('{tmp}\') + IntToStr(VirtualEnvCounter) + '-idf-test-venv\'; + VirtualEnvPython := VirtualEvnPath + 'Scripts\python.exe '; + Command := pythonPath + '-m virtualenv ' + VirtualEvnPath; + ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}')); + if (ResultCode <> 0) then begin + SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyCreateVirtualenv'); + Result := False; + Exit; + end; + + ScriptFile := ExpandConstant('{tmp}\system_check_virtualenv.py') + Command := VirtualEnvPython + ScriptFile + ' ' + VirtualEnvPython; + ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}')); + if (ResultCode <> 0) then begin + SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyPythonInVirtualenv'); + Result := False; + Exit; + end; + + Command := VirtualEnvPython + '-m pip install --only-binary ":all:" "cryptography>=2.1.4" --no-binary future'; + ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}')); + if (ResultCode <> 0) then begin + SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyBinaryPythonWheel'); + Result := False; + Exit; + end; + + TempDownloadFile := IntToStr(VirtualEnvCounter) + '-idf-exe-v1.0.1.zip'; + ScriptFile := ExpandConstant('{tmp}\system_check_download.py'); + Command := VirtualEnvPython + ScriptFile + ExpandConstant(' https://dl.espressif.com/dl/idf-exe-v1.0.1.zip ' + TempDownloadFile); + ResultCode := SystemCheckExec(Command , ExpandConstant('{tmp}')); + if (ResultCode <> 0) then begin + SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyFailedHttpsDownload'); + Result := False; + Exit; + end; + + if (not FileExists(ExpandConstant('{tmp}\') + TempDownloadFile)) then begin + SystemLog(' [' + CustomMessage('SystemCheckResultFail') + '] - ' + CustomMessage('SystemCheckUnableToFindFile') + ' ' + ExpandConstant('{tmp}\') + TempDownloadFile); + Result := False; + Exit; + end; + + ScriptFile := ExpandConstant('{tmp}\system_check_subprocess.py'); + Command := pythonPath + ScriptFile; + ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}')); + if (ResultCode <> 0) then begin + RemedyCommand := pythonPath + '-m pip uninstall subprocess.run'; + SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyFailedSubmoduleRun') + #13#10 + RemedyCommand; + AddFix(RemedyCommand); + Result := False; + Exit; + end; + + SystemLog(' [' + CustomMessage('SystemCheckResultOk') + ']'); + Result := True; +end; + +procedure FindPythonVersionsFromKey(RootKey: Integer; SubKeyName: String); +var + CompanyNames: TArrayOfString; + CompanyName, CompanySubKey, TagName, TagSubKey: String; + ExecutablePath, DisplayName, Version: String; + TagNames: TArrayOfString; + CompanyId, TagId: Integer; + BaseDir: String; +begin + if not RegGetSubkeyNames(RootKey, SubKeyName, CompanyNames) then + begin + Log('Nothing found in ' + IntToStr(RootKey) + '\' + SubKeyName); + Exit; + end; + + for CompanyId := 0 to GetArrayLength(CompanyNames) - 1 do + begin + CompanyName := CompanyNames[CompanyId]; + + if CompanyName = 'PyLauncher' then + continue; + + CompanySubKey := SubKeyName + '\' + CompanyName; + Log('In ' + IntToStr(RootKey) + '\' + CompanySubKey); + + if not RegGetSubkeyNames(RootKey, CompanySubKey, TagNames) then + continue; + + for TagId := 0 to GetArrayLength(TagNames) - 1 do + begin + TagName := TagNames[TagId]; + TagSubKey := CompanySubKey + '\' + TagName; + Log('In ' + IntToStr(RootKey) + '\' + TagSubKey); + + if not GetPythonVersionInfoFromKey(RootKey, SubKeyName, CompanyName, TagName, Version, DisplayName, ExecutablePath, BaseDir) then + continue; + + if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin + Exit; + end; + + { Verify Python installation and display hint in case of invalid version or env. } + if not IsPythonInstallationValid(DisplayName, ExecutablePath) then begin + if ((Length(SystemCheckHint) > 0) and (SystemCheckState <> SYSTEM_CHECK_STATE_STOPPED)) then begin + SystemLogTitle(CustomMessage('SystemCheckHint') + ': ' + SystemCheckHint); + end; + continue; + end; + + PythonVersionAdd(Version, DisplayName, ExecutablePath); + end; + end; +end; + +procedure FindInstalledPythonVersions(); +begin + FindPythonVersionsFromKey(HKEY_CURRENT_USER, 'Software\Python'); + FindPythonVersionsFromKey(HKEY_LOCAL_MACHINE, 'Software\Python'); + FindPythonVersionsFromKey(HKEY_LOCAL_MACHINE, 'Software\Wow6432Node\Python'); +end; + + +{ Get Boolean for UI to determine whether it make sense to register exceptions to Defender. } +function GetWindowsDefenderStatus(): Boolean; +var + bHasWD: Boolean; + szWDPath: String; + listPSModulePath: TStringList; + ResultCode: Integer; + x: Integer; +begin + Log('Checking PSMODULEPATH for Windows Defender module'); + + listPSModulePath := TStringList.Create; + listPSModulePath.Delimiter := ';'; + listPSModulePath.StrictDelimiter := True; + listPSModulePath.DelimitedText := GetEnv('PsModulePath'); + + for x:=0 to (listPSModulePath.Count-1) do + begin + szWDPath := listPSModulePath[x] + '\Defender' + bHasWD := DirExists(szWDPath); + if bHasWD then + begin + break; + end + end; + + if not bHasWD then begin + Result := False; + Exit; + end; + + Log('Checking Windows Services Defender is enabled: (Get-MpComputerStatus).AntivirusEnabled'); + ResultCode := SystemCheckExec('powershell -ExecutionPolicy Bypass "if((Get-MpComputerStatus).AntivirusEnabled) { Exit 0 } else { Exit 1 }"', ExpandConstant('{tmp}')); + if (ResultCode <> 0) then begin + Log('Result code: ' + IntToStr(ResultCode)); + Result := False; + Exit; + end; + + Result := True; +end; + +{ Process user request to stop system checks. } +function SystemCheckStopRequest():Boolean; +begin + { In case of stopped check by user, procees to next/previous step. } + if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin + Result := True; + Exit; + end; + + if (SystemCheckState = SYSTEM_CHECK_STATE_RUNNING) then begin + if (MsgBox(CustomMessage('SystemCheckNotCompleteConsent'), mbConfirmation, MB_YESNO) = IDYES) then begin + SystemCheckState := SYSTEM_CHECK_STATE_STOPPED; + Result := True; + Exit; + end; + end; + + if (SystemCheckState = SYSTEM_CHECK_STATE_COMPLETE) then begin + Result := True; + end else begin + Result := False; + end; +end; + +{ Process request to proceed to next page. If the scan is running ask user for confirmation. } +function OnSystemCheckValidate(Sender: TWizardPage): Boolean; +begin + Result := SystemCheckStopRequest(); +end; + +{ Process request to go to previous screen (license). Prompt user for confirmation when system check is running. } +function OnSystemCheckBackButton(Sender: TWizardPage): Boolean; +begin + Result := SystemCheckStopRequest(); +end; + +{ Process request to stop System Check directly on the screen with System Check by Stop button. } +procedure StopSystemCheckButtonClick(Sender: TObject); +begin + SystemCheckStopRequest(); +end; + +{ Check whether site is reachable and that system trust the certificate. } +procedure VerifyRootCertificates(); +var + ResultCode: Integer; + Command: String; + OutFile: String; +begin + SystemLogTitle(CustomMessage('SystemCheckRootCertificates') + ' '); + + { It's necessary to invoke PowerShell *BEFORE* Python. Invoke-Request will retrieve and add Root Certificate if necessary. } + { Without the certificate Python is failing to connect to https. } + { Windows command to list current certificates: certlm.msc } + OutFile := ExpandConstant('{tmp}\check'); + Command := 'powershell -ExecutionPolicy Bypass '; + Command := Command + 'Invoke-WebRequest -Uri "https://dl.espressif.com/dl/?system_check=win' + GetWindowsVersionString + '" -OutFile "' + OutFile + '-1.txt";'; + Command := Command + 'Invoke-WebRequest -Uri "https://github.com/espressif" -OutFile "' + OutFile + '-2.txt";'; + {Command := Command + 'Invoke-WebRequest -Uri "https://www.s3.amazonaws.com/" -OutFile "' + OutFile + '-3.txt";';} + ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}')); + if (ResultCode <> 0) then begin + SystemLog(' [' + CustomMessage('SystemCheckResultWarn') + ']'); + SystemLog(CustomMessage('SystemCheckRootCertificateWarning')); + end else begin + SystemLog(' [' + CustomMessage('SystemCheckResultOk') + ']'); + end; +end; + +{ Execute system check } +procedure ExecuteSystemCheck(); +begin + { Execute system check only once. Avoid execution in case of back button. } + if (SystemCheckState <> SYSTEM_CHECK_STATE_INIT) then begin + Exit; + end; + + SystemCheckState := SYSTEM_CHECK_STATE_RUNNING; + SystemLogTitle(CustomMessage('SystemCheckStart')); + StopSystemCheckButton.Enabled := True; + + VerifyRootCertificates(); + + FindInstalledPythonVersions(); + + if (SystemCheckState <> SYSTEM_CHECK_STATE_STOPPED) then begin + SystemLogTitle(CustomMessage('SystemCheckForDefender') + ' '); + IsWindowsDefenderEnabled := GetWindowsDefenderStatus(); + if (IsWindowsDefenderEnabled) then begin + SystemLog(' [' + CustomMessage('SystemCheckResultFound') + ']'); + end else begin + SystemLog(' [' + CustomMessage('SystemCheckResultNotFound') + ']'); + end; + end else begin + { User cancelled the check, let's enable Defender script so that use can decide to disable it. } + IsWindowsDefenderEnabled := True; + end; + + if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin + SystemLog(''); + SystemLogTitle(CustomMessage('SystemCheckStopped')); + end else begin + SystemLogTitle(CustomMessage('SystemCheckComplete')); + SystemCheckState := SYSTEM_CHECK_STATE_COMPLETE; + end; + + { Enable Apply Script button if some fixes are available. } + if (Fixes.Count > 0) then begin + ApplyFixesButton.Enabled := True; + end; + + StopSystemCheckButton.Enabled := False; +end; + +{ Invoke scan of system environment. } +procedure OnSystemCheckActivate(Sender: TWizardPage); +begin + { Display special controls. For some reason the first call of the page does not invoke SystemCheckOnCurPageChanged. } + FullLogButton.Visible := True; + ApplyFixesButton.Visible := True; + StopSystemCheckButton.Visible := True; + SystemCheckViewer.Visible := True; + + ExecuteSystemCheck(); +end; + +{ Handle request to display full log from the installation. Open the log in notepad. } +procedure FullLogButtonClick(Sender: TObject); +var + ResultCode: Integer; +begin + Exec(ExpandConstant('{win}\notepad.exe'), ExpandConstant('{log}'), '', SW_SHOW, ewNoWait, ResultCode); +end; + +{ Handle request to apply available fixes. } +procedure ApplyFixesButtonClick(Sender: TObject); +var + ResultCode: Integer; + FixIndex: Integer; + AreFixesApplied: Boolean; +begin + if (MsgBox(CustomMessage('SystemCheckApplyFixesConsent'), mbConfirmation, MB_YESNO) = IDNO) then begin + Exit; + end; + + ApplyFixesButton.Enabled := false; + SystemCheckState := SYSTEM_CHECK_STATE_INIT; + SystemLog(''); + SystemLogTitle('Starting application of fixes'); + + AreFixesApplied := True; + for FixIndex := 0 to Fixes.Count - 1 do + begin + ResultCode := SystemCheckExec(Fixes[FixIndex], ExpandConstant('{tmp}')); + if (ResultCode <> 0) then begin + AreFixesApplied := False; + break; + end; + end; + + SystemLog(''); + if (AreFixesApplied) then begin + SystemLogTitle(CustomMessage('SystemCheckFixesSuccessful')); + end else begin + SystemLogTitle(CustomMessage('SystemCheckFixesFailed')); + end; + + SystemLog(''); + Fixes.Clear(); + + { Restart system check. } + ExecuteSystemCheck(); +end; + +{ Add Page for System Check so that user is informed about readiness of the system. } + +procedure CreateSystemCheckPage(); +begin + { Initialize data structure for Python } + InstalledPythonVersions := TStringList.Create(); + InstalledPythonDisplayNames := TStringList.Create(); + InstalledPythonExecutables := TStringList.Create(); + + { Create Spinner animation. } + Spinner := TStringList.Create(); + Spinner.Append('-'); + Spinner.Append('\'); + Spinner.Append('|'); + Spinner.Append('/'); + + VirtualEnvCounter := 0; + Fixes := TStringList.Create(); + SystemCheckState := SYSTEM_CHECK_STATE_INIT; + SystemCheckPage := CreateOutputMsgPage(wpLicense, CustomMessage('PreInstallationCheckTitle'), CustomMessage('PreInstallationCheckSubtitle'), ''); + + with SystemCheckPage do + begin + OnActivate := @OnSystemCheckActivate; + OnBackButtonClick := @OnSystemCheckBackButton; + OnNextButtonClick := @OnSystemCheckValidate; + end; + + SystemCheckViewer := TNewMemo.Create(WizardForm); + with SystemCheckViewer do + begin + Parent := WizardForm; + Left := ScaleX(10); + Top := ScaleY(60); + ReadOnly := True; + Font.Name := 'Courier New'; + Height := WizardForm.CancelButton.Top - ScaleY(40); + Width := WizardForm.ClientWidth + ScaleX(80); + WordWrap := True; + Visible := False; + end; + + SystemLogText := TStringList.Create; + + FullLogButton := TNewButton.Create(WizardForm); + with FullLogButton do + begin + Parent := WizardForm; + Left := WizardForm.ClientWidth; + Top := SystemCheckViewer.Top + SystemCheckViewer.Height + ScaleY(5); + Width := WizardForm.CancelButton.Width; + Height := WizardForm.CancelButton.Height; + Caption := CustomMessage('SystemCheckFullLogButtonCaption'); + OnClick := @FullLogButtonClick; + Visible := False; + end; + + ApplyFixesButton := TNewButton.Create(WizardForm); + with ApplyFixesButton do + begin + Parent := WizardForm; + Left := WizardForm.ClientWidth - FullLogButton.Width; + Top := FullLogButton.Top; + Width := WizardForm.CancelButton.Width; + Height := WizardForm.CancelButton.Height; + Caption := CustomMessage('SystemCheckApplyFixesButtonCaption'); + OnClick := @ApplyFixesButtonClick; + Visible := False; + Enabled := False; + end; + + StopSystemCheckButton := TNewButton.Create(WizardForm); + with StopSystemCheckButton do + begin + Parent := WizardForm; + Left := ApplyFixesButton.Left - ApplyFixesButton.Width; + Top := FullLogButton.Top; + Width := WizardForm.CancelButton.Width; + Height := WizardForm.CancelButton.Height; + Caption := CustomMessage('SystemCheckStopButtonCaption'); + OnClick := @StopSystemCheckButtonClick; + Visible := False; + Enabled := False; + end; + + { Extract helper files for sanity check of Python environment. } + ExtractTemporaryFile('system_check_download.py') + ExtractTemporaryFile('system_check_subprocess.py') + ExtractTemporaryFile('system_check_virtualenv.py') +end; + +{ Process Cancel Button Click event. Prompt user to confirm Cancellation of System check. } +{ Then continue with normal cancel window. } +procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean); +begin + if ((CurPageId = SystemCheckPage.ID) and (SystemCheckState = SYSTEM_CHECK_STATE_RUNNING)) then begin + SystemCheckStopRequest(); + end; +end; + +{ Display control specific for System Check page. } + +procedure SystemCheckOnCurPageChanged(CurPageID: Integer); +begin + FullLogButton.Visible := CurPageID = SystemCheckPage.ID; + ApplyFixesButton.Visible := CurPageID = SystemCheckPage.ID; + StopSystemCheckButton.Visible := CurPageID = SystemCheckPage.ID; + SystemCheckViewer.Visible := CurPageID = SystemCheckPage.ID; +end; diff --git a/tools/windows/tool_setup/utils.iss.inc b/tools/windows/tool_setup/utils.iss.inc index 2dee527ec5..6e90a37e50 100644 --- a/tools/windows/tool_setup/utils.iss.inc +++ b/tools/windows/tool_setup/utils.iss.inc @@ -1,4 +1,4 @@ -{ Copyright 2019 Espressif Systems (Shanghai) PTE LTD +{ Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD SPDX-License-Identifier: Apache-2.0 } { ------------------------------ Helper functions from libcmdlinerunner.dll ------------------------------ }