idf_setup.iss.inc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. { Copyright 2019-2021 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. GitRepoParam: String;
  130. GitResetParam: String;
  131. GitRecursiveParam: String;
  132. begin
  133. IDFPath := IDFDownloadPath;
  134. { If there is a release archive to download, IDFZIPFileName and IDFZIPFileVersion will be set.
  135. See GetIDFZIPFileVersion function.
  136. }
  137. if IDFZIPFileName <> '' then
  138. begin
  139. if IDFZIPFileVersion <> IDFDownloadVersion then
  140. begin
  141. { The version of .zip file downloaded is not the same as the version the user has requested.
  142. Will use 'git clone --reference' to obtain the correct version, using the contents
  143. of the .zip file as reference.
  144. }
  145. NeedToClone := True;
  146. end;
  147. CmdLine := ExpandConstant('{tmp}\7za.exe x -o' + ExpandConstant('{tmp}') + ' -r -aoa "' + IDFZIPFileName + '"');
  148. IDFTempPath := ExpandConstant('{tmp}\esp-idf-') + IDFZIPFileVersion;
  149. Log('Extracting ESP-IDF reference repository: ' + CmdLine);
  150. Log('Reference repository path: ' + IDFTempPath);
  151. DoCmdlineInstall('Extracting ESP-IDF', 'Setting up reference repository', CmdLine);
  152. end else begin
  153. { IDFZIPFileName is not set, meaning that we will rely on 'git clone'. }
  154. NeedToClone := True;
  155. Log('Not .zip release archive. Will do full clone.');
  156. end;
  157. if NeedToClone then
  158. begin
  159. CmdLine := GitExecutablePath + ' clone --progress -b ' + IDFDownloadVersion;
  160. GitRecursiveParam := ExpandConstant('{param:GITRECURSIVE|yes}');
  161. if (GitRecursiveParam = 'yes') then begin
  162. CmdLine := CmdLine + ' --recursive ';
  163. end;
  164. if IDFTempPath <> '' then
  165. CmdLine := CmdLine + ' --reference ' + IDFTempPath;
  166. GitRepoParam := ExpandConstant('{param:GITREPO|https://github.com/espressif/esp-idf.git}');
  167. CmdLine := CmdLine + ' ' + GitRepoParam +' ' + IDFPath;
  168. Log('Cloning IDF: ' + CmdLine);
  169. DoCmdlineInstall('Downloading ESP-IDF', 'Using git to clone ESP-IDF repository', CmdLine);
  170. if IDFTempPath <> '' then
  171. GitRepoDissociate(IDFPath);
  172. end else begin
  173. Log('Copying ' + IDFTempPath + ' to ' + IDFPath);
  174. if DirExists(IDFPath) then
  175. begin
  176. if not DirIsEmpty(IDFPath) then
  177. begin
  178. MsgBox('Destination directory exists and is not empty: ' + IDFPath, mbError, MB_OK);
  179. RaiseException('Failed to copy ESP-IDF')
  180. end;
  181. end;
  182. { If cmd.exe command argument starts with a quote, the first and last quote chars in the command
  183. will be removed by cmd.exe.
  184. Keys explanation: /s+/e includes all subdirectories, /i assumes that destination is a directory,
  185. /h copies hidden files, /q disables file name logging (making copying faster!)
  186. }
  187. CmdLine := ExpandConstant('cmd.exe /c ""xcopy" /s /e /i /h /q "' + IDFTempPath + '" "' + IDFPath + '""');
  188. DoCmdlineInstall('Extracting ESP-IDF', 'Copying ESP-IDF into the destination directory', CmdLine);
  189. GitRepoFixFileMode(IDFPath);
  190. GitResetParam := ExpandConstant('{param:GITRESET|yes}');
  191. if (GitResetParam = 'yes') then begin
  192. GitRepoFixNewlines(IDFPath);
  193. end else begin
  194. Log('Git reset disabled by command line option /GITRESET=yes.');
  195. end;
  196. DelTree(IDFTempPath, True, True, True);
  197. end;
  198. end;
  199. { ------------------------------ IDF Tools setup, Python environment setup ------------------------------ }
  200. function UseBundledIDFToolsPy(Version: String) : Boolean;
  201. begin
  202. Result := False;
  203. { Use bundled copy of idf_tools.py, as the copy shipped with these IDF versions can not work due to
  204. the --no-site-packages bug.
  205. }
  206. if (Version = 'v4.0') or (Version = 'v3.3.1') then
  207. begin
  208. Log('UseBundledIDFToolsPy: version=' + Version + ', using bundled idf_tools.py');
  209. Result := True;
  210. end;
  211. end;
  212. { Find Major and Minor version in esp_idf_version.h file. }
  213. function GetIDFVersionFromHeaderFile():String;
  214. var
  215. HeaderFileName: String;
  216. HeaderLines: TArrayOfString;
  217. LineIndex: Integer;
  218. LineCount: Longint;
  219. Line: String;
  220. MajorVersion: String;
  221. MinorVersion: String;
  222. begin
  223. HeaderFileName := GetIDFPath('') + '\components\esp_common\include\esp_idf_version.h';
  224. if (not FileExists(HeaderFileName)) then begin
  225. Result := '';
  226. Exit;
  227. end;
  228. LoadStringsFromFile(HeaderFileName, HeaderLines);
  229. LineCount := GetArrayLength(HeaderLines);
  230. for LineIndex := 0 to LineCount - 1 do begin
  231. Line := HeaderLines[LineIndex];
  232. if (pos('define ESP_IDF_VERSION_MAJOR', Line) > 0) then begin
  233. Delete(Line, 1, 29);
  234. MajorVersion := Trim(Line);
  235. end else if (pos('define ESP_IDF_VERSION_MINOR', Line) > 0) then begin
  236. Delete(Line, 1, 29);
  237. MinorVersion := Trim(Line);
  238. Result := MajorVersion + '.' + MinorVersion;
  239. Exit;
  240. end
  241. end;
  242. end;
  243. { Get short version from long version e.g. 3.7.9 -> 3.7 }
  244. function GetShortVersion(VersionString:String):String;
  245. var
  246. VersionIndex: Integer;
  247. MajorString: String;
  248. MinorString: String;
  249. DotIndex: Integer;
  250. begin
  251. { Transform version vx.y or release/vx.y to x.y }
  252. VersionIndex := pos('v', VersionString);
  253. if (VersionIndex > 0) then begin
  254. Delete(VersionString, 1, VersionIndex);
  255. end;
  256. { Transform version x.y.z to x.y }
  257. DotIndex := pos('.', VersionString);
  258. if (DotIndex > 0) then begin
  259. MajorString := Copy(VersionString, 1, DotIndex - 1);
  260. Delete(VersionString, 1, DotIndex);
  261. { Trim trailing version numbers. }
  262. DotIndex := pos('.', VersionString);
  263. if (DotIndex > 0) then begin
  264. MinorString := Copy(VersionString, 1, DotIndex - 1);
  265. VersionString := MajorString + '.' + MinorString;
  266. end else begin
  267. VersionString := MajorString + '.' + VersionString;
  268. end;
  269. end;
  270. Result := VersionString;
  271. end;
  272. { Get IDF version string in combination with Python version. }
  273. { Result e.g.: idf4.1_py38 }
  274. function GetIDFPythonEnvironmentVersion():String;
  275. var
  276. IDFVersionString: String;
  277. begin
  278. { Transform main or master to x.y }
  279. if (Pos('main', IDFDownloadVersion) > 0) or (Pos('master', IDFDownloadVersion) > 0) then begin
  280. IDFVersionString := GetIDFVersionFromHeaderFile();
  281. end else begin
  282. IDFVersionString := GetShortVersion(IDFDownloadVersion);
  283. end;
  284. Result := 'idf' + IDFVersionString + '_py' + GetShortVersion(PythonVersion);
  285. end;
  286. procedure IDFToolsSetup();
  287. var
  288. CmdLine: String;
  289. IDFPath: String;
  290. IDFToolsPyPath: String;
  291. IDFToolsPyCmd: String;
  292. BundledIDFToolsPyPath: String;
  293. JSONArg: String;
  294. OfflineParameter: String;
  295. PythonWheelsUrlParameter: String;
  296. begin
  297. IDFPath := GetIDFPath('');
  298. IDFToolsPyPath := IDFPath + '\tools\idf_tools.py';
  299. BundledIDFToolsPyPath := ExpandConstant('{app}\idf_tools_fallback.py');
  300. JSONArg := '';
  301. if FileExists(IDFToolsPyPath) then
  302. begin
  303. Log('idf_tools.py exists in IDF directory');
  304. if UseBundledIDFToolsPy(IDFDownloadVersion) then
  305. begin
  306. Log('Using the bundled idf_tools.py copy');
  307. IDFToolsPyCmd := BundledIDFToolsPyPath;
  308. end else begin
  309. IDFToolsPyCmd := IDFToolsPyPath;
  310. end;
  311. end else begin
  312. Log('idf_tools.py does not exist in IDF directory, using a fallback version');
  313. IDFToolsPyCmd := BundledIDFToolsPyPath;
  314. JSONArg := ExpandConstant('--tools "{app}\tools_fallback.json"');
  315. end;
  316. { IDFPath not quoted, as it can not contain spaces }
  317. IDFToolsPyCmd := PythonExecutablePath + ' "' + IDFToolsPyCmd + '" --idf-path ' + IDFPath + JSONArg;
  318. SetEnvironmentVariable('PYTHONUNBUFFERED', '1');
  319. OfflineParameter := ExpandConstant('{param:OFFLINE|no}');
  320. if (OfflineParameter = 'yes') then begin
  321. SetEnvironmentVariable('PIP_NO_INDEX', 'true');
  322. Log('Offline installation selected. Setting environment variable PIP_NO_INDEX=1');
  323. end else begin
  324. PythonWheelsUrlParameter := ExpandConstant('{param:PYTHONWHEELSURL|https://dl.espressif.com/pypi}');
  325. SetEnvironmentVariable('PIP_EXTRA_INDEX_URL', PythonWheelsUrlParameter);
  326. Log('Adding extra Python wheels location. Setting environment variable PIP_EXTRA_INDEX_URL=' + PythonWheelsUrlParameter);
  327. end;
  328. Log('idf_tools.py command: ' + IDFToolsPyCmd);
  329. CmdLine := IDFToolsPyCmd + ' install';
  330. Log('Installing tools:' + CmdLine);
  331. DoCmdlineInstall('Installing ESP-IDF tools', '', CmdLine);
  332. CmdLine := IDFToolsPyCmd + ' install-python-env';
  333. Log('Installing Python environment:' + CmdLine);
  334. DoCmdlineInstall('Installing Python environment', '', CmdLine);
  335. end;
  336. { ------------------------------ Start menu shortcut ------------------------------ }
  337. procedure CreateIDFCommandPromptShortcut(LnkString: String);
  338. var
  339. Destination: String;
  340. Description: String;
  341. PythonVirtualEnvPath: String;
  342. Command: String;
  343. begin
  344. ForceDirectories(ExpandConstant(LnkString));
  345. Destination := ExpandConstant(LnkString + '\{#IDFCmdExeShortcutFile}');
  346. Description := '{#IDFCmdExeShortcutDescription}';
  347. { The links should contain reference to Python vitual env }
  348. PythonVirtualEnvPath := ExpandConstant('{app}\python_env\') + GetIDFPythonEnvironmentVersion() + '_env\Scripts';
  349. Log('Path to Python in virtual env: ' + PythonVirtualEnvPath);
  350. { Fallback in case of not existing environment. }
  351. if (not FileExists(PythonVirtualEnvPath + '\python.exe')) then begin
  352. PythonVirtualEnvPath := PythonPath;
  353. Log('python.exe not found, reverting to:' + PythonPath);
  354. end;
  355. { If cmd.exe command argument starts with a quote, the first and last quote chars in the command
  356. will be removed by cmd.exe; each argument needs to be surrounded by quotes as well. }
  357. Command := ExpandConstant('/k ""{app}\idf_cmd_init.bat" "') + PythonVirtualEnvPath + '" "' + GitPath + '""';
  358. Log('CreateShellLink Destination=' + Destination + ' Description=' + Description + ' Command=' + Command)
  359. try
  360. CreateShellLink(
  361. Destination,
  362. Description,
  363. 'cmd.exe',
  364. Command,
  365. GetIDFPath(''),
  366. '', 0, SW_SHOWNORMAL);
  367. except
  368. MsgBox('Failed to create the shortcut: ' + Destination, mbError, MB_OK);
  369. RaiseException('Failed to create the shortcut');
  370. end;
  371. end;
  372. procedure CreateIDFPowershellShortcut(LnkString: String);
  373. var
  374. Destination: String;
  375. Description: String;
  376. Command: String;
  377. GitPathWithForwardSlashes: String;
  378. PythonPathWithForwardSlashes: String;
  379. begin
  380. ForceDirectories(ExpandConstant(LnkString));
  381. Destination := ExpandConstant(LnkString + '\{#IDFPsShortcutFile}');
  382. Description := '{#IDFPsShortcutDescription}';
  383. GitPathWithForwardSlashes := GitPath;
  384. PythonPathWithForwardSlashes := PythonPath;
  385. StringChangeEx(GitPathWithForwardSlashes, '\', '/', True);
  386. StringChangeEx(PythonPathWithForwardSlashes, '\', '/', True);
  387. Command := ExpandConstant('-ExecutionPolicy Bypass -NoExit -File ""{app}\idf_cmd_init.ps1"" ') + '"' + GitPathWithForwardSlashes + '" "' + PythonPathWithForwardSlashes + '"'
  388. Log('CreateShellLink Destination=' + Destination + ' Description=' + Description + ' Command=' + Command)
  389. try
  390. CreateShellLink(
  391. Destination,
  392. Description,
  393. 'powershell.exe',
  394. Command,
  395. GetIDFPath(''),
  396. '', 0, SW_SHOWNORMAL);
  397. except
  398. MsgBox('Failed to create the shortcut: ' + Destination, mbError, MB_OK);
  399. RaiseException('Failed to create the shortcut');
  400. end;
  401. end;
  402. { ------------------------------ WD exclusion registration ------------------------------ }
  403. procedure RegisterIDFToolsExecutablesInWD();
  404. var
  405. CmdLine: String;
  406. begin
  407. CmdLine := ExpandConstant('powershell -ExecutionPolicy ByPass -File "{app}\dist\tools_WD_excl.ps1" -AddExclPath "{app}\*.exe"');
  408. Log('Registering IDF Tools executables in Windows Defender: ' + CmdLine);
  409. DoCmdlineInstall('Finishing ESP-IDF installation', 'Registering IDF Tools executables in Windows Defender', CmdLine);
  410. end;
  411. <event('CurPageChanged')>
  412. procedure CheckWinDefenderAvailable(CurPageID: Integer);
  413. var
  414. x: Integer;
  415. begin
  416. if CurPageID = wpSelectTasks then
  417. begin
  418. { WD registration checkbox is identified by 'Windows Defender' substring anywhere in its caption.
  419. Please, keep this in mind when making changes }
  420. for x:=0 to (WizardForm.TasksList.Items.Count-1) do
  421. begin
  422. if Pos('Windows Defender', WizardForm.TasksList.ItemCaption[x]) > 0 then
  423. begin
  424. WizardForm.TasksList.ItemEnabled[x] := isWindowsDefenderEnabled;
  425. WizardForm.TasksList.Checked[x] := isWindowsDefenderEnabled;
  426. break;
  427. end;
  428. end;
  429. end;
  430. end;