Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# -*- coding: utf-8 -*- 

2""" 

3@file 

4@brief Functions to prepare a setup on Windows 

5""" 

6from __future__ import print_function 

7 

8import os 

9import shutil 

10import sys 

11import warnings 

12import datetime 

13 

14from ..installhelper.install_cmd_helper import update_pip, run_cmd, python_version 

15from ..installhelper.module_dependencies import missing_dependencies 

16 

17from .win_batch import create_win_batches 

18from .win_ipy_kernels import install_kernels 

19from .win_innosetup_helper import run_innosetup, innosetup_replacements 

20from .win_fix_compiler_c import switch_to_VS_compiler, switch_to_mingw_compiler 

21from .win_patch import win_patch_paths 

22from .win_setup_main_helper import dtnow, copy_icons, win_download, win_install, create_links_tools 

23from .win_setup_main_helper import win_download_notebooks, win_install_julia_step, win_install_r_step 

24from .win_packages import win_install_packages_other_python, get_modules_version 

25from .win_extract import clean_msi 

26from .win_ipython_helper import ipython_create_profile, ipython_update_profile, install_jupyter_extension 

27from .win_setup_r import get_package_description 

28from .win_exception import WinInstallMissingDependency 

29from .tutorial import copy_tutorial 

30from .win_setup_main_checkings import distribution_checkings 

31 

32if sys.version_info[0] == 2: 

33 from codecs import open 

34 FileNotFoundError = Exception 

35 

36 

37license = """ 

38Copyright (c) 2013-2016, Xavier Dupré 

39 

40Permission is hereby granted, free of charge, to any person obtaining a copy 

41of this software and associated documentation files (the "Software"), to deal 

42in the Software without restriction, including without limitation the rights 

43to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 

44copies of the Software, and to permit persons to whom the Software is 

45furnished to do so, subject to the following conditions: 

46 

47The above copyright notice and this permission notice shall be included in 

48all copies or substantial portions of the Software. 

49 

50THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 

51IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

52FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 

53AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 

54LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 

55OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 

56THE SOFTWARE. 

57""" 

58 

59_default_notebooks = [ 

60 ("docs/ensae", ["http://www.xavierdupre.fr/app/ensae_teaching_cs/helpsphinx3/_downloads/td1a_cenonce_session_12.ipynb", 

61 "http://www.xavierdupre.fr/app/ensae_teaching_cs/helpsphinx3/_downloads/td2a_cenonce_session_2A.ipynb", 

62 "http://www.xavierdupre.fr/app/ensae_teaching_cs/helpsphinx3/_downloads/td2a_cenonce_session_2B.ipynb", 

63 "http://www.xavierdupre.fr/app/ensae_teaching_cs/helpsphinx3/_downloads/td2a_cenonce_session_2C.ipynb", 

64 ]), 

65 ("docs/actuariat", ["http://www.xavierdupre.fr/app/actuariat_python/helpsphinx/_downloads/population_recuperation_donnees.ipynb", 

66 ]), 

67] 

68 

69 

70def architecture(): 

71 """ 

72 @return either 32bit or 64bit 

73 """ 

74 o, b = python_version() 

75 return b 

76 

77 

78def win_python_setup(folder="dist/win_python_setup_" + architecture(), 

79 download_folder="build/win_python_setup_" + architecture(), 

80 module_list=None, verbose=False, fLOG=print, download_only=False, 

81 no_setup=False, notebooks=None, selection=None, 

82 documentation=True, last_function=None, r_packages=True, 

83 julia_packages=True, tutorial=None, source=None, 

84 embed=True): 

85 """ 

86 Prepares a Windows distribution of Python based on InnoSetup, 

87 inspired from WinPython but more easier to tweak I hope. 

88 

89 @param folder where to prepare the python version 

90 @param module_list list of module to install (see @see fn small_installation = default options) 

91 @param fLOG logging function 

92 @param download_only only downloads 

93 @param no_setup skip the building of the setup 

94 @param verbose print more information 

95 @param notebooks notebooks to copy to the workspace, list of ("subfolders", url) 

96 @param selection selection of tools to install, example: ``{"R", "mingw", "tdm"}``, 

97 empty by default 

98 @param last_function function to execute just before running InnoSetup, 

99 see `win_setup_helper.py 

100 <https://github.com/sdpython/ensae_teaching_cs/blob/master/src/ 

101 ensae_teaching_cs/automation/win_setup_helper.py>`_ 

102 for an example 

103 @param r_packages install R packages 

104 @param julia_packages install Julia packages 

105 @param documentation add documentation 

106 @param tutorial list of folders to copy in ``workspace/tutorial``, 

107 it can refer to internal tutorials (see folder ``win_installer/tutorial``) 

108 @param source source of python packages (see @see cl ModuleInstall) 

109 @param embed custom *Python* or embedded distribution (False) 

110 @return list of completed operations 

111 

112 The available tools to install must be chose among: 

113 

114 * `R <http://www.r-project.org/>`_ 

115 * `Julia <http://julialang.org/>`_ 

116 * `MinGW <http://www.mingw.org/>`_ 

117 * `TDM-GCC <http://tdm-gcc.tdragon.net/>`_ 

118 * `VS <https://www.visualstudio.com/en-us/products/visual-studio-express-vs.aspx>`_ 

119 * `Java JDK <http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html>`_ 

120 

121 By default, only R is included. Julia requires too much work. 

122 The command line does not always end. The building of the package 

123 is sometimes reluctant to work. And the Julia kernel is exclusive: 

124 it cannot be setup with others kernel. Maybe the version 0.5 will fix those issues. 

125 

126 The signature of function ``last_function`` should be the following:: 

127 

128 def last_function(inno_script, folders, verbose=False, fLOG=print): 

129 # something specific to do 

130 # before compiling the setup 

131 # such adding new tools 

132 # or replacing icons 

133 # the inno setup script is located in folders["logs"] 

134 

135 The parameters *folders* is a dictionary which gives access to the main folders 

136 of the distribution. 

137 

138 The setup will also contains `pandoc <http://pandoc.org/>`_, 

139 `7z <http://www.7-zip.org/>`_, 

140 `SQLiteSpy <https://www.yunqa.de/delphi/doku.php/products/sqlitespy/index>`_, 

141 `Scite <http://www.scintilla.org/SciTE.html>`_, 

142 `MinGW <http://www.mingw.org/>`_, 

143 `Graphviz <http://www.graphviz.org/>`_. 

144 

145 The function first downloads everything. 

146 It does not do it twice, so you can run the function again and directly go 

147 to where it was interrupted. If there is no notebooks, 

148 the setup will add some anyway. 

149 

150 It uses `InnoSetup <http://www.jrsoftware.org/isinfo.php>`_ to build the setup. 

151 

152 The distribution will contain the following subfolders: 

153 

154 * *tools*: subfolders for R, Julia, MinGW, Scite, pandoc, 7z, Putty... 

155 * *python*: subfolder for python interpreter 

156 * *workspace*: current folder for the notebooks 

157 * *build*: location of downloaded modules and tools 

158 

159 Comments and remarks: 

160 

161 * 7z setup needs a last click to complete 

162 * pandoc needs a last click to complete 

163 * R must be manually installed in the right folder 

164 * TDM-GCC is manually installed in the right folder 

165 * Julia produces a file exe, for the time being it must be done manually 

166 * MinGW is also installed manually, the command line is different from others tools, 

167 once it is installed, you should run the command line:: 

168 

169 mingw-get install binutils gcc g++ mingw32 fortran gdb mingw32 mingw w32api g77 

170 

171 * Python setups needs a last click to complete 

172 * Julia command line sometimes gets stuck, the setup needs to be stopped 

173 and restarted. It happens while installing the packages and 

174 also while building IJulia (the package to use Julia in a notebook). 

175 The Julia should be stopped instead of stopping the python script. 

176 That trick shows the standard output of Julia. 

177 * Julia kernel cannot be used with the others: it requires a different 

178 configuration which prevents others kernel to be available at the same time. 

179 We will skip for the time being. 

180 * If the R kernel fails to start, you should manually run the script 

181 `R_install.r <https://github.com/sdpython/pymyinstall/blob/master/src/pymyinstall/win_installer/R_install.r>`_. 

182 

183 With Julia, initialisation, installation or building takes time. 

184 The function writes a file ``log.step.<julia_step>.txt`` 

185 to tell the step has completed once. You can remove this file 

186 to do it again. 

187 

188 .. exref:: 

189 :title: Prepare a standalone distribution 

190 

191 The function downloads everything. The installation of tools 

192 is still manual. Package installation is automated. 

193 

194 :: 

195 

196 from pymyinstall import win_python_setup 

197 from pymyinstall.packaged import ensae_fullset 

198 list_modules = ensae_fullset() 

199 win_python_setup(module_list=list_modules, 

200 verbose=False, 

201 download_only=False) 

202 

203 This works only for Windows. 

204 

205 @warning The Julia installation might be stuck after the installation or the build. 

206 In that case, the script python should be stopped by *stopping the Julia 

207 process* from the Task Manager 

208 and started again. If it has completed, it will go to the 

209 next step. 

210 

211 .. index:: issue 

212 

213 **Known issues while preparing the setup:** 

214 

215 * Some modules generates utf-8 encoding errors while being installed. 

216 The python scripts stops. It should be started again, it will 

217 detect the module was insalled and will go to the next one in the list. 

218 * The setup are started by the Python script but the user needs to manually 

219 click on the final ok button to proceed. 

220 

221 **Known issues after the setup is installed:** 

222 

223 * The first run of Spyder after the installation usually fails (failure of python.exe). 

224 The second one succeeds. You should run Spyder from the installation setup before 

225 compiling the setup. 

226 

227 .. index:: missing modules, vcomp110.dll, llvmlite, numba, issue, theano, xgboost 

228 

229 **Known extra steps needed by some modules** 

230 

231 * **llvmlite**, **numba**, on Windows, if the dll 

232 *api-ms-win-crt-runtime-l1-1-0.dll* is missing, it is explained 

233 in `api-ms-win-crt-runtime-l1-1-0.dll error <https://github.com/cmderdev/cmder/issues/490>`_, 

234 `Visual C++ Redistributable for Visual Studio 2015 <https://www.microsoft.com/en-us/download/details.aspx?id=48145>`_ 

235 needs to be installed. 

236 * **theano** requires `TDM-GCC <http://tdm-gcc.tdragon.net/>`_, 

237 read `Installation of Theano on Windows <http://deeplearning.net/software/theano/install_windows.html>`_ 

238 * **xgboost** if DLL ``vcomp110.dll`` is missing, you should read blog :ref:`blog_xgboost_install` 

239 to understand how to get it. 

240 

241 @todo Use chocolatey to process installation. 

242 

243 @todo Fix Julia installation. 

244 """ 

245 if selection is None: 

246 selection = set() 

247 if notebooks is None: 

248 notebooks = _default_notebooks 

249 

250 if not isinstance(selection, set): 

251 selection = set(selection) 

252 selection.add("pandoc") 

253 selection.add("7z") 

254 selection.add("scite") 

255 selection.add("putty") 

256 selection.add("sqlitespy") 

257 selection.add("graphviz") 

258 selection.add("python") 

259 selection.add("jenkins") 

260 selection = set(_.lower() for _ in selection) 

261 

262 _allowed = {"pandoc", "7z", "scite", "putty", 

263 "sqlitespy", "scite", "python", "tdm", "vs", "r", "graphviz", 

264 "jenkins", "jdk"} 

265 for s in selection: 

266 s_ = s.split("==")[0] 

267 if s_ not in _allowed: 

268 raise ValueError("{0} unknown, should be in {1}".format( 

269 s, ", ".join(sorted(_allowed)))) 

270 

271 fLOG("[pymy] --- selection", selection) 

272 

273 ##### 

274 # we change for the version 

275 ##### 

276 versions = {} 

277 for sel in selection: 

278 if "==" in sel: 

279 spl = sel.split("==") 

280 versions[spl[0].lower()] = spl[1] 

281 else: 

282 versions[sel.lower()] = None 

283 selection = versions 

284 

285 ###### 

286 # next 

287 ###### 

288 

289 operations = [] 

290 operations.append(("time", dtnow())) 

291 

292 folder = os.path.abspath(folder) 

293 download_folder = os.path.abspath(download_folder) 

294 

295 if not os.path.exists(folder): 

296 os.makedirs(folder) 

297 operations.append(("mkdir", folder)) 

298 

299 if not os.path.exists(download_folder): 

300 os.makedirs(download_folder) 

301 operations.append(("mkdir", download_folder)) 

302 

303 ################### 

304 # definition of folders 

305 ################### 

306 operations.append(("time", dtnow())) 

307 folders = dict(tools=os.path.join(folder, "tools"), 

308 workspace=os.path.join(folder, "workspace"), 

309 python=os.path.join(folder, "python"), 

310 config=os.path.join(folder, "config"), 

311 logs=os.path.join(folder, "logs"), 

312 build=download_folder, 

313 ) 

314 

315 for k, v in folders.items(): 

316 if not os.path.exists(v): 

317 os.mkdir(v) 

318 

319 ########################### 

320 # download the documentation 

321 ########################### 

322 if documentation: 

323 operations.append(("documentation", "-")) 

324 op = win_download_notebooks( 

325 notebooks, folders["workspace"], verbose=verbose, fLOG=fLOG) 

326 operations.extend(op) 

327 operations.append(("time", dtnow())) 

328 

329 ###################### 

330 # download of everything 

331 ###################### 

332 for mod in module_list: 

333 mod.fLOG = fLOG 

334 operations.append(("download", "-")) 

335 op = win_download(folder=download_folder, 

336 module_list=module_list, 

337 verbose=verbose, fLOG=fLOG, 

338 selection=selection, source=source, 

339 embed=embed) 

340 operations.extend(op) 

341 operations.append(("time", dtnow())) 

342 

343 ######## 

344 # license 

345 ######## 

346 fLOG("[pymy] --- license") 

347 with open(os.path.join(folder, "license.txt"), "w") as f: 

348 f.write(license) 

349 operations.append(("license", "license.txt")) 

350 

351 if not download_only: 

352 ######################## 

353 # copy icons in tools/icons 

354 ####################### 

355 fLOG("[pymy] --- copy icons") 

356 op = copy_icons(os.path.join(os.path.dirname(__file__), "icons"), 

357 os.path.join(folders["tools"], "icons")) 

358 operations.extend(op) 

359 operations.append(("time", dtnow())) 

360 

361 ############# 

362 # install setups 

363 ############# 

364 fLOG("[pymy] --- installation of python and tools") 

365 fLOG("[pymy] --- you might have to it yourself for R, Julia") 

366 op, installed = win_install( 

367 folders=folders, download_folder=download_folder, verbose=verbose, fLOG=fLOG, 

368 selection=selection) 

369 operations.extend(op) 

370 operations.append(("time", dtnow())) 

371 if "pandoc" not in installed: 

372 raise FileNotFoundError("pandoc was not installed") 

373 

374 if verbose: 

375 for k, v in installed.items(): 

376 fLOG("[pymy] INSTALLED:", k, "-->", v) 

377 

378 ########## 

379 # clean msi 

380 ########## 

381 fLOG("[pymy] --- clean msi") 

382 op = clean_msi(folders["tools"], "*.msi", verbose=verbose, fLOG=fLOG) 

383 operations.extend(op) 

384 operations.append(("time", dtnow())) 

385 

386 ################ 

387 # create links tools 

388 ################ 

389 fLOG("[pymy] --- create links") 

390 op = create_links_tools(folder, installed, verbose=verbose, fLOG=fLOG) 

391 operations.extend(op) 

392 operations.append(("time", dtnow())) 

393 

394 ######################### 

395 # create batch command files 

396 ######################### 

397 fLOG("[pymy] --- create batch command file") 

398 op = create_win_batches( 

399 folders, verbose=verbose, fLOG=fLOG, selection=selection, module_list=module_list) 

400 operations.extend(op) 

401 

402 ########### 

403 # update pip 

404 ########### 

405 fLOG("[pymy] --- update pip") 

406 operations.append(("python pip", "update")) 

407 op = update_pip(folders["python"]) 

408 operations.extend(op) 

409 operations.append(("time", dtnow())) 

410 

411 if "julia" in selection and julia_packages: 

412 operations.append(("julia", "-")) 

413 ops = win_install_julia_step(folders, verbose=verbose, fLOG=fLOG) 

414 operations.extend(ops) 

415 operations.append(("time", dtnow())) 

416 

417 if "r" in selection and r_packages: 

418 operations.append(("r", "-")) 

419 ops = win_install_r_step(folders, verbose=verbose, fLOG=fLOG) 

420 operations.extend(ops) 

421 operations.append(("time", dtnow())) 

422 

423 ###################### 

424 # installation of packages 

425 ###################### 

426 fLOG("[pymy] --- installation of python packages") 

427 operations.append(("python packaes", "start")) 

428 python_path = folders["python"] 

429 win_install_packages_other_python( 

430 python_path, download_folder, verbose=verbose, fLOG=fLOG, 

431 module_list=module_list) 

432 fLOG("[pymy] done") 

433 operations.append(("time", dtnow())) 

434 

435 ########################## 

436 # mingw, add file distutils.cfg 

437 ########################## 

438 if "mingw" in selection and "vs" not in selection: 

439 fLOG("[pymy] --- switch_to_mingw_compiler") 

440 op = switch_to_mingw_compiler(folders["python"]) 

441 for o in op: 

442 operations.append(("modify", o)) 

443 

444 ########################## 

445 # Visual Studio, VS 2015 for Python 3.5 

446 ########################## 

447 if "vs" in selection: 

448 fLOG("[pymy] --- switch_to_VS_compiler") 

449 op = switch_to_VS_compiler(folders["python"]) 

450 for o in op: 

451 operations.append(("modify", o)) 

452 

453 ###################### 

454 # create jupyter profile 

455 ###################### 

456 has_jupyter = False 

457 for mod in module_list: 

458 if mod.name == "jupyter": 

459 has_jupyter = True 

460 if has_jupyter: 

461 fLOG("[pymy] --- create jupyter profile") 

462 operations.append(("jupyter", "create profile")) 

463 ipath = ipython_create_profile( 

464 folders["config"], folders["python"], fLOG=fLOG) 

465 operations.append(("profile", ipath)) 

466 operations.append(("time", dtnow())) 

467 

468 ###################### 

469 # update ipython profile 

470 ###################### 

471 if has_jupyter: 

472 fLOG("[pymy] --- update jupyter profile") 

473 operations.append(("jupyter", "update profile")) 

474 ipython_update_profile(ipath) 

475 operations.append(("time", dtnow())) 

476 

477 ###################### 

478 # update jupyter extension 

479 ###################### 

480 if has_jupyter: 

481 fLOG("[pymy] --- install jupyter extension") 

482 operations.append(("jupyter", "update install jupyter extension")) 

483 install_jupyter_extension(folders["python"]) 

484 operations.append(("time", dtnow())) 

485 

486 ###################### 

487 # copy pywin32 dll to main folders 

488 ###################### 

489 fLOG("[pymy] --- pywin32 dll to main folders") 

490 operations.append(("pywin32", "dll")) 

491 fdll = os.path.join( 

492 python_path, "Lib", "site-packages", "pywin32_system32") 

493 if not os.path.exists(fdll): 

494 fdll = os.path.join( 

495 python_path, "Lib", "site-packages", "pypiwin32_system32") 

496 if not os.path.exists(fdll): 

497 raise FileNotFoundError(fdll) 

498 for dll in os.listdir(fdll): 

499 full = os.path.join(fdll, dll) 

500 if os.path.isdir(full): 

501 continue 

502 try: 

503 shutil.copy(full, python_path) 

504 except KeyError as a: 

505 # it means it already exists 

506 continue 

507 operations.append(("pywin32", "copy " + dll)) 

508 operations.append(("time", dtnow())) 

509 

510 ######## 

511 # kernels 

512 ######## 

513 if has_jupyter: 

514 fLOG("[pymy] --- add kernels") 

515 operations.append(("kernel", "add")) 

516 res = install_kernels(folders["tools"], folders["python"]) 

517 for r in res: 

518 fLOG("[pymy] ADD: kernel", r) 

519 operations.append(("time", dtnow())) 

520 

521 ######### 

522 # checking 

523 ######### 

524 distribution_checkings(folders["python"], folders[ 

525 "tools"], fLOG=fLOG, module_list=module_list) 

526 

527 ######## 

528 # tutorial 

529 ######## 

530 if tutorial is not None: 

531 fLOG("[pymy] --- copy tutorial") 

532 operations.append(("tutorial", "begin")) 

533 fold_tuto = os.path.join(folders["workspace"], "tutorial") 

534 if not os.path.exists(fold_tuto): 

535 fLOG("[pymy] --- create ", fold_tuto) 

536 operations.append(("create", fold_tuto)) 

537 os.mkdir(fold_tuto) 

538 for tuto in tutorial: 

539 fLOG("[pymy] copy tutorial", tuto) 

540 operations.append(("tutorial", tuto)) 

541 res = copy_tutorial(tuto, fold_tuto) 

542 for a, b, c in res: 

543 operations.append(("copy", c)) 

544 

545 operations.append(("time", dtnow())) 

546 

547 ################################ 

548 # prepare setup script for InnoSetup 

549 ############################### 

550 fLOG("[pymy] --- prepare setup script for InnoSetup") 

551 replacements = dict(__DISTPATH__=folder) 

552 new_script = innosetup_replacements(replacements=replacements, fLOG=fLOG, 

553 temp_folder=os.path.join(folders["logs"])) 

554 fLOG("[pymy] done") 

555 operations.append(("InnoSetup", "replacement")) 

556 operations.append(("time", dtnow())) 

557 

558 if last_function is not None: 

559 ################# 

560 # run last_function 

561 ################# 

562 fLOG("[pymy] --- run last_function") 

563 operations.append(("start", "last_function")) 

564 last_function(new_script, folders, verbose=verbose, fLOG=fLOG) 

565 operations.append(("time", dtnow())) 

566 

567 ################## 

568 # check there is no missing modules 

569 ################## 

570 if not download_only: 

571 scr = "from pymyinstall.installhelper import missing_dependencies;r=missing_dependencies();" + \ 

572 "print('\\n'.join('\'{0}\' misses \'{1}\''.format(k,v) for k,v in sorted(r.items())))" 

573 cmd = '{0} -c "{1}"'.format(os.path.join( 

574 folders["python"], "python.exe"), scr) 

575 fLOG("[pymy] --- run dependencies") 

576 fLOG("[pymy] CMD:", cmd) 

577 out, err = run_cmd(cmd, wait=True) 

578 if len(err) > 0: 

579 raise WinInstallMissingDependency(err) 

580 fLOG(out) 

581 

582 miss = missing_dependencies() 

583 if len(miss) > 0: 

584 mes = "\n".join("'{0}' misses '{1}'".format(k, ", ".join(v)) 

585 for k, v in sorted(miss.items())) 

586 warnings.warn(mes) 

587 

588 ################## 

589 # patch exe in scripts 

590 ################## 

591 if not download_only: 

592 fLOG( 

593 "--- patch paths, see http://www.clemens-sielaff.com/create-a-portable-python-with-pip-on-windows/") 

594 op = win_patch_paths( 

595 os.path.join(folders["python"], "Scripts"), "", fLOG=fLOG) 

596 operations.extend(op) 

597 operations.append(("time", dtnow())) 

598 

599 ################# 

600 # print the list of modules (python) 

601 ################# 

602 if not download_only: 

603 fLOG("[pymy] --- pip freeze") 

604 mods = get_modules_version(folders["python"]) 

605 fLOG("[pymy] nb modules: {0}".format(len(mods))) 

606 if len(mods) == 0: 

607 raise ValueError( 

608 "unable to get module list from folder " + folders["python"]) 

609 with open(os.path.join(folders["config"], "installed.python.packages.txt"), "w") as f: 

610 mods = [(a.lower(), a, b) for a, b in mods.items()] 

611 for la, a, b in sorted(mods): 

612 f.write("{0}\t{1}\n".format(a, b)) 

613 

614 ################# 

615 # print the list of modules (R) 

616 ################# 

617 if not download_only: 

618 r_path = os.path.join(folders["tools"], "R") 

619 r_lib = os.path.join(r_path, "library") 

620 if os.path.exists(r_lib): 

621 fLOG("[pymy] --- list R packages") 

622 packs = os.listdir(r_lib) 

623 with open(os.path.join(folders["config"], "installed.R.packages.txt"), "w") as f: 

624 packs_ = [(p.lower(), p) for p in packs] 

625 for lp, pack in sorted(packs_): 

626 desc = get_package_description(r_path, pack) 

627 vers = desc.get("Version", "unknown") 

628 f.write("{0}\t{1}\n".format(pack, vers)) 

629 

630 if not no_setup: 

631 

632 if not os.path.exists(folders["workspace"]): 

633 raise FileNotFoundError(folders["workspace"]) 

634 if len(os.listdir(folders["workspace"])) == 0: 

635 raise FileNotFoundError( 

636 "folder {0} is empty, it should not".format(folders["workspace"])) 

637 

638 # remove 

639 fLOG("[pymy] --- remove setup") 

640 dist = os.path.join(folders["logs"], "..", "dist", "setup") 

641 if os.path.exists(dist): 

642 exe = [_ for _ in os.listdir(dist) if ".exe" in _] 

643 else: 

644 exe = [] 

645 if len(exe) > 0: 

646 for e in exe: 

647 os.remove(os.path.join(dist, e)) 

648 

649 ################################ 

650 # prepare setup script for InnoSetup 

651 ############################### 

652 fLOG("[pymy] --- building setup with InnoSetup") 

653 out = run_innosetup(new_script, fLOG=fLOG, 

654 temp_folder=os.path.join(folders["logs"])) 

655 with open(os.path.join(folders["logs"], "out.install.innosetup.txt"), "w", encoding="utf8") as f: 

656 f.write(out) 

657 fLOG("[pymy] done") 

658 operations.append(("InnoSetup", "done")) 

659 operations.append(("time", dtnow())) 

660 

661 # copy 

662 fLOG("[pymy] --- copy setup") 

663 dist = os.path.join(folders["logs"], "..", "dist", "setup") 

664 to = os.path.join(folders["logs"], "..", "..") 

665 exe = [_ for _ in os.listdir(dist) if ".exe" in _] 

666 if len(exe) > 0: 

667 dt = datetime.datetime.now() 

668 suffix = "%d%02d%02d.%d" % (dt.year, dt.month, dt.day, dt.hour) 

669 for e in exe: 

670 shutil.copy(os.path.join(dist, e), to) 

671 operations.append(("copy", e)) 

672 final = os.path.join(to, e) 

673 tof = final.replace(".exe", "_" + suffix + ".exe") 

674 operations.append(("rename", tof)) 

675 os.rename(final, tof) 

676 

677 ########## 

678 # store logs 

679 ########## 

680 with open(os.path.join(folders["logs"], "log.setup.txt"), "a", encoding="utf8") as f: 

681 f.write("\n") 

682 f.write("-------------------------------------------\n") 

683 f.write("NEW RUN\n") 

684 f.write("-------------------------------------------\n") 

685 for ab in operations: 

686 if isinstance(ab, tuple): 

687 if len(ab) == 2: 

688 a, b = ab 

689 elif len(ab) == 1: 

690 a, b = a, None 

691 else: 

692 a, b = ab[0], str(ab[1:]) 

693 

694 if isinstance(b, str # unicode# 

695 ): 

696 b = b.replace(folder, "") 

697 b = b.replace(os.environ.get( 

698 "USERNAME", os.environ["USER"]), "---") 

699 f.write("{0}\t{1}\n".format(a, b))