idf_setup.iss.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. { Copyright 2019-2020 Espressif Systems (Shanghai) CO LTD
  2. SPDX-License-Identifier: Apache-2.0 }
  3. { ------------------------------ Downloading ESP-IDF ------------------------------ }
  4. var
  5. IDFZIPFileVersion, IDFZIPFileName: String;
  6. function GetIDFPath(Unused: String): String;
  7. begin
  8. if IDFUseExisting then
  9. Result := IDFExistingPath
  10. else
  11. Result := IDFDownloadPath;
  12. end;
  13. function GetIDFZIPFileVersion(Version: String): String;
  14. var
  15. ReleaseVerPart: String;
  16. i: Integer;
  17. Found: Boolean;
  18. begin
  19. if WildCardMatch(Version, 'v*') or WildCardMatch(Version, 'v*-rc*') then
  20. Result := Version
  21. else if Version = 'master' then
  22. Result := ''
  23. else if WildCardMatch(Version, 'release/v*') then
  24. begin
  25. ReleaseVerPart := Version;
  26. Log('ReleaseVerPart=' + ReleaseVerPart)
  27. Delete(ReleaseVerPart, 1, Length('release/'));
  28. Log('ReleaseVerPart=' + ReleaseVerPart)
  29. Found := False;
  30. for i := 0 to GetArrayLength(IDFDownloadAvailableVersions) - 1 do
  31. begin
  32. if Pos(ReleaseVerPart, IDFDownloadAvailableVersions[i]) = 1 then
  33. begin
  34. Result := IDFDownloadAvailableVersions[i];
  35. Found := True;
  36. break;
  37. end;
  38. end;
  39. if not Found then
  40. Result := '';
  41. end;
  42. Log('GetIDFZIPFileVersion(' + Version + ')=' + Result);
  43. end;
  44. procedure IDFAddDownload();
  45. var
  46. Url, MirrorUrl: String;
  47. begin
  48. IDFZIPFileVersion := GetIDFZIPFileVersion(IDFDownloadVersion);
  49. if IDFZIPFileVersion <> '' then
  50. begin
  51. Url := 'https://github.com/espressif/esp-idf/releases/download/' + IDFZIPFileVersion + '/esp-idf-' + IDFZIPFileVersion + '.zip';
  52. MirrorUrl := 'https://dl.espressif.com/github_assets/espressif/esp-idf/releases/download/' + IDFZIPFileVersion + '/esp-idf-' + IDFZIPFileVersion + '.zip';
  53. IDFZIPFileName := ExpandConstant('{app}\releases\esp-idf-' + IDFZIPFileVersion + '.zip')
  54. if not FileExists(IDFZIPFileName) then
  55. begin
  56. ForceDirectories(ExpandConstant('{app}\releases'))
  57. Log('Adding download: ' + Url + ', mirror: ' + MirrorUrl + ', destination: ' + IDFZIPFileName);
  58. idpAddFile(Url, IDFZIPFileName);
  59. idpAddMirror(Url, MirrorUrl);
  60. end else begin
  61. Log(IDFZIPFileName + ' already exists')
  62. end;
  63. end;
  64. end;
  65. procedure RemoveAlternatesFile(Path: String);
  66. begin
  67. Log('Removing ' + Path);
  68. DeleteFile(Path);
  69. end;
  70. {
  71. Replacement of the '--dissociate' flag of 'git clone', to support older versions of Git.
  72. '--reference' is supported for submodules since git 2.12, but '--dissociate' only from 2.18.
  73. }
  74. procedure GitRepoDissociate(Path: String);
  75. var
  76. CmdLine: String;
  77. begin
  78. CmdLine := GitExecutablePath + ' -C ' + Path + ' repack -d -a'
  79. DoCmdlineInstall('Finishing ESP-IDF installation', 'Re-packing the repository', CmdLine);
  80. CmdLine := GitExecutablePath + ' -C ' + Path + ' submodule foreach git repack -d -a'
  81. DoCmdlineInstall('Finishing ESP-IDF installation', 'Re-packing the submodules', CmdLine);
  82. FindFileRecursive(Path + '\.git', 'alternates', @RemoveAlternatesFile);
  83. end;
  84. {
  85. Run git config fileMode is repairing problem when git repo was zipped on Linux and extracted on Windows.
  86. The repo and submodules are marked as dirty which confuses users that fresh installation already contains changes.
  87. More information: https://mirrors.edge.kernel.org/pub/software/scm/git/docs/git-config.html
  88. }
  89. procedure GitRepoFixFileMode(Path: String);
  90. var
  91. CmdLine: String;
  92. begin
  93. CmdLine := GitExecutablePath + ' -C ' + Path + ' config --local core.fileMode false';
  94. Log('Setting core.fileMode on repository: ' + CmdLine);
  95. DoCmdlineInstall('Finishing ESP-IDF installation', 'Updating fileMode', CmdLine);
  96. Log('Setting core.fileMode on repository for submodules: ' + CmdLine);
  97. CmdLine := GitExecutablePath + ' -C ' + Path + ' submodule foreach --recursive git config --local core.fileMode false';
  98. DoCmdlineInstall('Finishing ESP-IDF installation', 'Updating fileMode in submodules', CmdLine);
  99. end;
  100. { Run git reset --hard in the repo and in the submodules, to fix the newlines. }
  101. procedure GitRepoFixNewlines(Path: String);
  102. var
  103. CmdLine: String;
  104. begin
  105. CmdLine := GitExecutablePath + ' -C ' + Path + ' reset --hard';
  106. Log('Resetting the repository: ' + CmdLine);
  107. DoCmdlineInstall('Finishing ESP-IDF installation', 'Updating newlines', CmdLine);
  108. Log('Resetting the submodules: ' + CmdLine);
  109. CmdLine := GitExecutablePath + ' -C ' + Path + ' submodule foreach git reset --hard';
  110. DoCmdlineInstall('Finishing ESP-IDF installation', 'Updating newlines in submodules', CmdLine);
  111. end;
  112. {
  113. There are 3 possible ways how an ESP-IDF copy can be obtained:
  114. - Download the .zip archive with submodules included, extract to destination directory,
  115. then do 'git reset --hard' and 'git submodule foreach git reset --hard' to correct for
  116. possibly different newlines. This is done for release versions.
  117. - Do a git clone of the Github repository into the destination directory.
  118. This is done for the master branch.
  119. - Download the .zip archive of a "close enough" release version, extract into a temporary
  120. directory. Then do a git clone of the Github repository, using the temporary directory
  121. as a '--reference'. This is done for other versions (such as release branches).
  122. }
  123. procedure IDFDownload();
  124. var
  125. CmdLine: String;
  126. IDFTempPath: String;
  127. IDFPath: String;
  128. NeedToClone: Boolean;
  129. begin
  130. IDFPath := IDFDownloadPath;
  131. { If there is a release archive to download, IDFZIPFileName and IDFZIPFileVersion will be set.
  132. See GetIDFZIPFileVersion function.
  133. }
  134. if IDFZIPFileName <> '' then
  135. begin
  136. if IDFZIPFileVersion <> IDFDownloadVersion then
  137. begin
  138. { The version of .zip file downloaded is not the same as the version the user has requested.
  139. Will use 'git clone --reference' to obtain the correct version, using the contents
  140. of the .zip file as reference.
  141. }
  142. NeedToClone := True;
  143. end;
  144. ExtractTemporaryFile('7za.exe')
  145. CmdLine := ExpandConstant('{tmp}\7za.exe x -o' + ExpandConstant('{tmp}') + ' -r -aoa "' + IDFZIPFileName + '"');
  146. IDFTempPath := ExpandConstant('{tmp}\esp-idf-') + IDFZIPFileVersion;
  147. Log('Extracting ESP-IDF reference repository: ' + CmdLine);
  148. Log('Reference repository path: ' + IDFTempPath);
  149. DoCmdlineInstall('Extracting ESP-IDF', 'Setting up reference repository', CmdLine);
  150. end else begin
  151. { IDFZIPFileName is not set, meaning that we will rely on 'git clone'. }
  152. NeedToClone := True;
  153. Log('Not .zip release archive. Will do full clone.');
  154. end;
  155. if NeedToClone then
  156. begin
  157. CmdLine := GitExecutablePath + ' clone --recursive --progress -b ' + IDFDownloadVersion;
  158. if IDFTempPath <> '' then
  159. CmdLine := CmdLine + ' --reference ' + IDFTempPath;
  160. CmdLine := CmdLine + ' https://github.com/espressif/esp-idf.git ' + IDFPath;
  161. Log('Cloning IDF: ' + CmdLine);
  162. DoCmdlineInstall('Downloading ESP-IDF', 'Using git to clone ESP-IDF repository', CmdLine);
  163. if IDFTempPath <> '' then
  164. GitRepoDissociate(IDFPath);
  165. end else begin
  166. Log('Copying ' + IDFTempPath + ' to ' + IDFPath);
  167. if DirExists(IDFPath) then
  168. begin
  169. if not DirIsEmpty(IDFPath) then
  170. begin
  171. MsgBox('Destination directory exists and is not empty: ' + IDFPath, mbError, MB_OK);
  172. RaiseException('Failed to copy ESP-IDF')
  173. end;
  174. end;
  175. { If cmd.exe command argument starts with a quote, the first and last quote chars in the command
  176. will be removed by cmd.exe.
  177. Keys explanation: /s+/e includes all subdirectories, /i assumes that destination is a directory,
  178. /h copies hidden files, /q disables file name logging (making copying faster!)
  179. }
  180. CmdLine := ExpandConstant('cmd.exe /c ""xcopy" /s /e /i /h /q "' + IDFTempPath + '" "' + IDFPath + '""');
  181. DoCmdlineInstall('Extracting ESP-IDF', 'Copying ESP-IDF into the destination directory', CmdLine);
  182. GitRepoFixFileMode(IDFPath);
  183. GitRepoFixNewlines(IDFPath);
  184. DelTree(IDFTempPath, True, True, True);
  185. end;
  186. end;
  187. { ------------------------------ IDF Tools setup, Python environment setup ------------------------------ }
  188. function UseBundledIDFToolsPy(Version: String) : Boolean;
  189. begin
  190. Result := False;
  191. { Use bundled copy of idf_tools.py, as the copy shipped with these IDF versions can not work due to
  192. the --no-site-packages bug.
  193. }
  194. if (Version = 'v4.0') or (Version = 'v3.3.1') then
  195. begin
  196. Log('UseBundledIDFToolsPy: version=' + Version + ', using bundled idf_tools.py');
  197. Result := True;
  198. end;
  199. end;
  200. procedure IDFToolsSetup();
  201. var
  202. CmdLine: String;
  203. IDFPath: String;
  204. IDFToolsPyPath: String;
  205. IDFToolsPyCmd: String;
  206. BundledIDFToolsPyPath: String;
  207. JSONArg: String;
  208. begin
  209. IDFPath := GetIDFPath('');
  210. IDFToolsPyPath := IDFPath + '\tools\idf_tools.py';
  211. BundledIDFToolsPyPath := ExpandConstant('{app}\idf_tools_fallback.py');
  212. JSONArg := '';
  213. if FileExists(IDFToolsPyPath) then
  214. begin
  215. Log('idf_tools.py exists in IDF directory');
  216. if UseBundledIDFToolsPy(IDFDownloadVersion) then
  217. begin
  218. Log('Using the bundled idf_tools.py copy');
  219. IDFToolsPyCmd := BundledIDFToolsPyPath;
  220. end else begin
  221. IDFToolsPyCmd := IDFToolsPyPath;
  222. end;
  223. end else begin
  224. Log('idf_tools.py does not exist in IDF directory, using a fallback version');
  225. IDFToolsPyCmd := BundledIDFToolsPyPath;
  226. JSONArg := ExpandConstant('--tools "{app}\tools_fallback.json"');
  227. end;
  228. { IDFPath not quoted, as it can not contain spaces }
  229. IDFToolsPyCmd := PythonExecutablePath + ' "' + IDFToolsPyCmd + '" --idf-path ' + IDFPath + JSONArg;
  230. SetEnvironmentVariable('PYTHONUNBUFFERED', '1')
  231. Log('idf_tools.py command: ' + IDFToolsPyCmd);
  232. CmdLine := IDFToolsPyCmd + ' install';
  233. Log('Installing tools:' + CmdLine);
  234. DoCmdlineInstall('Installing ESP-IDF tools', '', CmdLine);
  235. CmdLine := IDFToolsPyCmd + ' install-python-env';
  236. Log('Installing Python environment:' + CmdLine);
  237. DoCmdlineInstall('Installing Python environment', '', CmdLine);
  238. end;
  239. { Find Major and Minor version in esp_idf_version.h file. }
  240. function GetIDFVersionFromHeaderFile():String;
  241. var
  242. HeaderFileName: String;
  243. HeaderLines: TArrayOfString;
  244. LineIndex: Integer;
  245. LineCount: Longint;
  246. Line: String;
  247. MajorVersion: String;
  248. MinorVersion: String;
  249. begin
  250. HeaderFileName := GetIDFPath('') + '\components\esp_common\include\esp_idf_version.h';
  251. if (not FileExists(HeaderFileName)) then begin
  252. Result := '';
  253. Exit;
  254. end;
  255. LoadStringsFromFile(HeaderFileName, HeaderLines);
  256. LineCount := GetArrayLength(HeaderLines);
  257. for LineIndex := 0 to LineCount - 1 do begin
  258. Line := HeaderLines[LineIndex];
  259. if (pos('define ESP_IDF_VERSION_MAJOR', Line) > 0) then begin
  260. Delete(Line, 1, 29);
  261. MajorVersion := Trim(Line);
  262. end else if (pos('define ESP_IDF_VERSION_MINOR', Line) > 0) then begin
  263. Delete(Line, 1, 29);
  264. MinorVersion := Trim(Line);
  265. Result := MajorVersion + '.' + MinorVersion;
  266. Exit;
  267. end
  268. end;
  269. end;
  270. { ------------------------------ Start menu shortcut ------------------------------ }
  271. procedure CreateIDFCommandPromptShortcut(LnkString: String);
  272. var
  273. Destination: String;
  274. Description: String;
  275. VersionIndex: Integer;
  276. MajorString: String;
  277. MinorString: String;
  278. DotIndex: Integer;
  279. IDFVersionString: String;
  280. PythonVirtualEnvPath: String;
  281. Command: String;
  282. begin
  283. ForceDirectories(ExpandConstant(LnkString));
  284. Destination := ExpandConstant(LnkString + '\{#IDFCmdExeShortcutFile}');
  285. Description := '{#IDFCmdExeShortcutDescription}';
  286. IDFVersionString := IDFDownloadVersion;
  287. { Transform version vx.y or release/vx.y to x.y }
  288. VersionIndex := pos('v', IDFVersionString);
  289. if (VersionIndex > 0) then begin
  290. Delete(IDFVersionString, 1, VersionIndex);
  291. end;
  292. { Transform version x.y.z to x.y }
  293. DotIndex := pos('.', IDFVersionString);
  294. if (DotIndex > 0) then begin
  295. MajorString := Copy(IDFVersionString, 1, DotIndex - 1);
  296. Delete(IDFVersionString, 1, DotIndex);
  297. { Trim trailing version numbers. }
  298. DotIndex := pos('.', IDFVersionString);
  299. if (DotIndex > 0) then begin
  300. MinorString := Copy(IDFVersionString, 1, DotIndex - 1);
  301. IDFVersionString := MajorString + '.' + MinorString;
  302. end else begin
  303. IDFVersionString := MajorString + '.' + IDFVersionString;
  304. end;
  305. end;
  306. { Transform master to x.y }
  307. if (IDFVersionString = 'master') then begin
  308. IDFVersionString := GetIDFVersionFromHeaderFile();
  309. end;
  310. { The links should contain reference to Python vitual env }
  311. PythonVirtualEnvPath := ExpandConstant('{app}\python_env\idf') + IDFVersionString + '_py' + PythonVersion + '_env\Scripts';
  312. { Fallback in case of not existing environment. }
  313. if (not FileExists(PythonVirtualEnvPath + '\python.exe')) then begin
  314. PythonVirtualEnvPath := PythonPath;
  315. end;
  316. { If cmd.exe command argument starts with a quote, the first and last quote chars in the command
  317. will be removed by cmd.exe; each argument needs to be surrounded by quotes as well. }
  318. Command := ExpandConstant('/k ""{app}\idf_cmd_init.bat" "') + PythonVirtualEnvPath + '" "' + GitPath + '""';
  319. Log('CreateShellLink Destination=' + Destination + ' Description=' + Description + ' Command=' + Command)
  320. try
  321. CreateShellLink(
  322. Destination,
  323. Description,
  324. 'cmd.exe',
  325. Command,
  326. GetIDFPath(''),
  327. '', 0, SW_SHOWNORMAL);
  328. except
  329. MsgBox('Failed to create the Start menu shortcut: ' + Destination, mbError, MB_OK);
  330. RaiseException('Failed to create the shortcut');
  331. end;
  332. end;
  333. { ------------------------------ WD exclusion registration ------------------------------ }
  334. procedure RegisterIDFToolsExecutablesInWD();
  335. var
  336. CmdLine: String;
  337. begin
  338. CmdLine := ExpandConstant('powershell -ExecutionPolicy ByPass -File "{app}\dist\tools_WD_excl.ps1" -AddExclPath "{app}\*.exe"');
  339. Log('Registering IDF Tools executables in Windows Defender: ' + CmdLine);
  340. DoCmdlineInstall('Finishing ESP-IDF installation', 'Registering IDF Tools executables in Windows Defender', CmdLine);
  341. end;
  342. <event('CurPageChanged')>
  343. procedure CheckWinDefenderAvailable(CurPageID: Integer);
  344. var
  345. x: Integer;
  346. begin
  347. if CurPageID = wpSelectTasks then
  348. begin
  349. { WD registration checkbox is identified by 'Windows Defender' substring anywhere in its caption.
  350. Please, keep this in mind when making changes }
  351. for x:=0 to (WizardForm.TasksList.Items.Count-1) do
  352. begin
  353. if Pos('Windows Defender', WizardForm.TasksList.ItemCaption[x]) > 0 then
  354. begin
  355. WizardForm.TasksList.ItemEnabled[x] := isWindowsDefenderEnabled;
  356. WizardForm.TasksList.Checked[x] := isWindowsDefenderEnabled;
  357. break;
  358. end;
  359. end;
  360. end;
  361. end;