|
1 #!/usr/bin/env python3 |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 # Copyright (c) 2002 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
5 # |
|
6 # This is the install script for eric6. |
|
7 |
|
8 """ |
|
9 Installation script for the eric6 IDE and all eric6 related tools. |
|
10 """ |
|
11 |
|
12 from __future__ import unicode_literals, print_function |
|
13 try: |
|
14 # Python2 only |
|
15 import cStringIO as io |
|
16 try: |
|
17 from PyQt5 import sip |
|
18 except ImportError: |
|
19 import sip |
|
20 sip.setapi('QString', 2) |
|
21 sip.setapi('QVariant', 2) |
|
22 sip.setapi('QTextStream', 2) |
|
23 input = raw_input # __IGNORE_WARNING__ |
|
24 except (ImportError): |
|
25 import io # __IGNORE_WARNING__ |
|
26 |
|
27 import sys |
|
28 import os |
|
29 import re |
|
30 import compileall |
|
31 import py_compile |
|
32 import glob |
|
33 import shutil |
|
34 import fnmatch |
|
35 import codecs |
|
36 import subprocess |
|
37 import time |
|
38 |
|
39 # Define the globals. |
|
40 progName = None |
|
41 currDir = os.getcwd() |
|
42 modDir = None |
|
43 pyModDir = None |
|
44 platBinDir = None |
|
45 platBinDirOld = None |
|
46 distDir = None |
|
47 apisDir = None |
|
48 installApis = True |
|
49 doCleanup = True |
|
50 doCleanDesktopLinks = False |
|
51 forceCleanDesktopLinks = False |
|
52 doCompile = True |
|
53 includePythonVariant = False |
|
54 cfg = {} |
|
55 progLanguages = ["Python", "Ruby", "QSS"] |
|
56 sourceDir = "eric" |
|
57 eric6SourceDir = os.path.join(sourceDir, "eric6") |
|
58 configName = 'eric6config.py' |
|
59 defaultMacAppBundleName = "eric6.app" |
|
60 defaultMacAppBundlePath = "/Applications" |
|
61 defaultMacPythonExe = "{0}/Resources/Python.app/Contents/MacOS/Python".format( |
|
62 sys.exec_prefix) |
|
63 macAppBundleName = defaultMacAppBundleName |
|
64 macAppBundlePath = defaultMacAppBundlePath |
|
65 macPythonExe = defaultMacPythonExe |
|
66 pyqtVariant = "" |
|
67 pyqtOverride = False |
|
68 |
|
69 # Define blacklisted versions of the prerequisites |
|
70 BlackLists = { |
|
71 "sip": [], |
|
72 "PyQt4": [], |
|
73 "PyQt5": [], |
|
74 "QScintilla2": [], |
|
75 } |
|
76 PlatformsBlackLists = { |
|
77 "windows": { |
|
78 "sip": [], |
|
79 "PyQt4": [], |
|
80 "PyQt5": [], |
|
81 "QScintilla2": [], |
|
82 }, |
|
83 |
|
84 "linux": { |
|
85 "sip": [], |
|
86 "PyQt4": [], |
|
87 "PyQt5": [], |
|
88 "QScintilla2": [], |
|
89 }, |
|
90 |
|
91 "mac": { |
|
92 "sip": [], |
|
93 "PyQt4": [], |
|
94 "PyQt5": [], |
|
95 "QScintilla2": [], |
|
96 }, |
|
97 } |
|
98 |
|
99 # Define file name markers for Python variants |
|
100 PythonMarkers = { |
|
101 2: "_py2", |
|
102 3: "_py3", |
|
103 } |
|
104 # Define a mapping of markers to full text |
|
105 PythonTextMarkers = { |
|
106 "_py2": "Python 2", |
|
107 "_py3": "Python 3", |
|
108 } |
|
109 |
|
110 |
|
111 def exit(rcode=0): |
|
112 """ |
|
113 Exit the install script. |
|
114 |
|
115 @param rcode result code to report back (integer) |
|
116 """ |
|
117 global currDir |
|
118 |
|
119 print() |
|
120 |
|
121 if sys.platform.startswith(("win", "cygwin")): |
|
122 # different meaning of input between Py2 and Py3 |
|
123 try: |
|
124 input("Press enter to continue...") |
|
125 except (EOFError, SyntaxError): |
|
126 pass |
|
127 |
|
128 os.chdir(currDir) |
|
129 |
|
130 sys.exit(rcode) |
|
131 |
|
132 |
|
133 def usage(rcode=2): |
|
134 """ |
|
135 Display a usage message and exit. |
|
136 |
|
137 @param rcode the return code passed back to the calling process. |
|
138 """ |
|
139 global progName, modDir, distDir, apisDir |
|
140 global macAppBundleName, macAppBundlePath, macPythonExe |
|
141 global pyqtVariant |
|
142 |
|
143 print() |
|
144 print("Usage:") |
|
145 if sys.platform == "darwin": |
|
146 print(" {0} [-chxyz] [-a dir] [-b dir] [-d dir] [-f file] [-i dir]" |
|
147 " [-m name] [-n path] [-p python] [--no-apis] [--pyqt=version]" |
|
148 .format(progName)) |
|
149 elif sys.platform.startswith(("win", "cygwin")): |
|
150 print(" {0} [-chxyz] [-a dir] [-b dir] [-d dir] [-f file]" |
|
151 " [--clean-desktop] [--no-apis] [--pyqt=version]" |
|
152 .format(progName)) |
|
153 else: |
|
154 print(" {0} [-chxyz] [-a dir] [-b dir] [-d dir] [-f file] [-i dir]" |
|
155 " [--no-apis] [--pyqt=version]" |
|
156 .format(progName)) |
|
157 print("where:") |
|
158 print(" -h, --help display this help message") |
|
159 print(" -a dir where the API files will be installed") |
|
160 if apisDir: |
|
161 print(" (default: {0})".format(apisDir)) |
|
162 else: |
|
163 print(" (no default value)") |
|
164 print(" --no-apis don't install API files") |
|
165 print(" -b dir where the binaries will be installed") |
|
166 print(" (default: {0})".format(platBinDir)) |
|
167 print(" -d dir where eric6 python files will be installed") |
|
168 print(" (default: {0})".format(modDir)) |
|
169 print(" -f file configuration file naming the various installation" |
|
170 " paths") |
|
171 if not sys.platform.startswith(("win", "cygwin")): |
|
172 print(" -i dir temporary install prefix") |
|
173 print(" (default: {0})".format(distDir)) |
|
174 if sys.platform == "darwin": |
|
175 print(" -m name name of the Mac app bundle") |
|
176 print(" (default: {0})".format(macAppBundleName)) |
|
177 print(" -n path path of the directory the Mac app bundle will") |
|
178 print(" be created in") |
|
179 print(" (default: {0})".format(macAppBundlePath)) |
|
180 print(" -p python path of the python executable") |
|
181 print(" (default: {0})".format(macPythonExe)) |
|
182 print(" -c don't cleanup old installation first") |
|
183 if sys.platform.startswith(("win", "cygwin")): |
|
184 (" --clean-desktop delete desktop links before installation") |
|
185 print(" -x don't perform dependency checks (use on your own" |
|
186 " risk)") |
|
187 print(" -y add the Python variant to the executable names") |
|
188 print(" -z don't compile the installed python files") |
|
189 print(" --pyqt=version version of PyQt to be used (one of 4 or 5)") |
|
190 if pyqtVariant: |
|
191 print(" (default: {0})".format(pyqtVariant[-1])) |
|
192 else: |
|
193 print(" (no PyQt variant found)") |
|
194 print() |
|
195 print("The file given to the -f option must be valid Python code" |
|
196 " defining a") |
|
197 print("dictionary called 'cfg' with the keys 'ericDir', 'ericPixDir'," |
|
198 " 'ericIconDir',") |
|
199 print("'ericDTDDir', 'ericCSSDir', 'ericStylesDir', 'ericDocDir'," |
|
200 " 'ericExamplesDir',") |
|
201 print("'ericTranslationsDir', 'ericTemplatesDir', 'ericCodeTemplatesDir',") |
|
202 print("'ericOthersDir','bindir', 'mdir' and 'apidir.") |
|
203 print("These define the directories for the installation of the various" |
|
204 " parts of eric6.") |
|
205 |
|
206 exit(rcode) |
|
207 |
|
208 |
|
209 def determinePyQtVariant(): |
|
210 """ |
|
211 Module function to determine the PyQt variant to be used. |
|
212 """ |
|
213 global pyqtVariant, pyqtOverride |
|
214 |
|
215 pyqtVariant = "" |
|
216 # need to handle the --pyqt= option here |
|
217 if "--pyqt=4" in sys.argv: |
|
218 pyqtVariant = "PyQt4" |
|
219 pyqtOverride = True |
|
220 elif "--pyqt=5" in sys.argv: |
|
221 pyqtVariant = "PyQt5" |
|
222 pyqtOverride = True |
|
223 else: |
|
224 try: |
|
225 import PyQt5 # __IGNORE_WARNING__ |
|
226 pyqtVariant = "PyQt5" |
|
227 del sys.modules["PyQt5"] |
|
228 except ImportError: |
|
229 try: |
|
230 import PyQt4 # __IGNORE_WARNING__ |
|
231 pyqtVariant = "PyQt4" |
|
232 del sys.modules["PyQt4"] |
|
233 except ImportError: |
|
234 # default to PyQt5, installation will be asked for |
|
235 pyqtVariant = "PyQt5" |
|
236 |
|
237 |
|
238 def initGlobals(): |
|
239 """ |
|
240 Module function to set the values of globals that need more than a |
|
241 simple assignment. |
|
242 """ |
|
243 global platBinDir, modDir, pyModDir, apisDir, pyqtVariant, platBinDirOld |
|
244 |
|
245 try: |
|
246 import distutils.sysconfig |
|
247 except ImportError: |
|
248 print("Please install the 'distutils' package first.") |
|
249 exit(5) |
|
250 |
|
251 if sys.platform.startswith(("win", "cygwin")): |
|
252 platBinDir = sys.exec_prefix |
|
253 if platBinDir.endswith("\\"): |
|
254 platBinDir = platBinDir[:-1] |
|
255 platBinDirOld = platBinDir |
|
256 platBinDir = os.path.join(platBinDir, "Scripts") |
|
257 if not os.path.exists(platBinDir): |
|
258 platBinDir = platBinDirOld |
|
259 else: |
|
260 pyBinDir = os.path.normpath(os.path.dirname(sys.executable)) |
|
261 if os.access(pyBinDir, os.W_OK): |
|
262 # install the eric scripts along the python executable |
|
263 platBinDir = pyBinDir |
|
264 else: |
|
265 # install them in the user's bin directory |
|
266 platBinDir = os.path.expanduser("~/bin") |
|
267 if platBinDir != "/usr/local/bin" and \ |
|
268 os.access("/usr/local/bin", os.W_OK): |
|
269 platBinDirOld = "/usr/local/bin" |
|
270 |
|
271 modDir = distutils.sysconfig.get_python_lib(True) |
|
272 pyModDir = modDir |
|
273 |
|
274 pyqtDataDir = os.path.join(modDir, pyqtVariant) |
|
275 if os.path.exists(os.path.join(pyqtDataDir, "qsci")): |
|
276 # it's the installer |
|
277 qtDataDir = pyqtDataDir |
|
278 elif os.path.exists(os.path.join(pyqtDataDir, "Qt", "qsci")): |
|
279 # it's the wheel |
|
280 qtDataDir = os.path.join(pyqtDataDir, "Qt") |
|
281 else: |
|
282 # determine dynamically |
|
283 try: |
|
284 if pyqtVariant == "PyQt4": |
|
285 from PyQt4.QtCore import QLibraryInfo |
|
286 else: |
|
287 from PyQt5.QtCore import QLibraryInfo |
|
288 qtDataDir = QLibraryInfo.location(QLibraryInfo.DataPath) |
|
289 except ImportError: |
|
290 qtDataDir = None |
|
291 if qtDataDir: |
|
292 apisDir = os.path.join(qtDataDir, "qsci", "api") |
|
293 else: |
|
294 apisDir = None |
|
295 |
|
296 |
|
297 def copyToFile(name, text): |
|
298 """ |
|
299 Copy a string to a file. |
|
300 |
|
301 @param name the name of the file. |
|
302 @param text the contents to copy to the file. |
|
303 """ |
|
304 f = open(name, "w") |
|
305 if sys.version_info[0] == 2: |
|
306 text = codecs.encode(text, "utf-8") |
|
307 f.write(text) |
|
308 f.close() |
|
309 |
|
310 |
|
311 def copyDesktopFile(src, dst, marker): |
|
312 """ |
|
313 Modify a desktop file and write it to its destination. |
|
314 |
|
315 @param src source file name (string) |
|
316 @param dst destination file name (string) |
|
317 @param marker marker to be used (string) |
|
318 """ |
|
319 global cfg, platBinDir |
|
320 |
|
321 if sys.version_info[0] == 2: |
|
322 f = codecs.open(src, "r", "utf-8") |
|
323 else: |
|
324 f = open(src, "r", encoding="utf-8") |
|
325 text = f.read() |
|
326 f.close() |
|
327 |
|
328 text = text.replace("@BINDIR@", platBinDir) |
|
329 text = text.replace("@MARKER@", marker) |
|
330 if marker: |
|
331 t_marker = " ({0})".format(PythonTextMarkers[marker]) |
|
332 else: |
|
333 t_marker = "" |
|
334 text = text.replace("@PY_MARKER@", t_marker) |
|
335 |
|
336 if sys.version_info[0] == 2: |
|
337 f = codecs.open(dst, "w", "utf-8") |
|
338 else: |
|
339 f = open(dst, "w", encoding="utf-8") |
|
340 f.write(text) |
|
341 f.close() |
|
342 os.chmod(dst, 0o644) |
|
343 |
|
344 |
|
345 def copyAppStreamFile(src, dst, marker): |
|
346 """ |
|
347 Modify an appstream file and write it to its destination. |
|
348 |
|
349 @param src source file name (string) |
|
350 @param dst destination file name (string) |
|
351 @param marker marker to be used (string) |
|
352 """ |
|
353 if os.path.exists(os.path.join("eric", "eric6", "UI", "Info.py")): |
|
354 # Installing from archive |
|
355 from eric.eric6.UI.Info import Version |
|
356 elif os.path.exists(os.path.join("eric6", "UI", "Info.py")): |
|
357 # Installing from source tree |
|
358 from eric6.UI.Info import Version |
|
359 else: |
|
360 Version = "Unknown" |
|
361 |
|
362 if sys.version_info[0] == 2: |
|
363 f = codecs.open(src, "r", "utf-8") |
|
364 else: |
|
365 f = open(src, "r", encoding="utf-8") |
|
366 text = f.read() |
|
367 f.close() |
|
368 |
|
369 text = text.replace("@MARKER@", marker)\ |
|
370 .replace("@VERSION@", Version.split(None, 1)[0])\ |
|
371 .replace("@DATE@", time.strftime("%Y-%m-%d")) |
|
372 |
|
373 if sys.version_info[0] == 2: |
|
374 f = codecs.open(dst, "w", "utf-8") |
|
375 else: |
|
376 f = open(dst, "w", encoding="utf-8") |
|
377 f.write(text) |
|
378 f.close() |
|
379 os.chmod(dst, 0o644) |
|
380 |
|
381 |
|
382 def wrapperNames(dname, wfile): |
|
383 """ |
|
384 Create the platform specific names for the wrapper script. |
|
385 |
|
386 @param dname name of the directory to place the wrapper into |
|
387 @param wfile basename (without extension) of the wrapper script |
|
388 @return the names of the wrapper scripts |
|
389 """ |
|
390 if sys.platform.startswith(("win", "cygwin")): |
|
391 wnames = (dname + "\\" + wfile + ".cmd", |
|
392 dname + "\\" + wfile + ".bat") |
|
393 else: |
|
394 wnames = (dname + "/" + wfile, ) |
|
395 |
|
396 return wnames |
|
397 |
|
398 |
|
399 def createPyWrapper(pydir, wfile, saveDir, isGuiScript=True): |
|
400 """ |
|
401 Create an executable wrapper for a Python script. |
|
402 |
|
403 @param pydir the name of the directory where the Python script will |
|
404 eventually be installed (string) |
|
405 @param wfile the basename of the wrapper (string) |
|
406 @param saveDir directory to save the file into (string) |
|
407 @param isGuiScript flag indicating a wrapper script for a GUI |
|
408 application (boolean) |
|
409 @return the platform specific name of the wrapper (string) |
|
410 """ |
|
411 global includePythonVariant, pyqtVariant, pyqtOverride |
|
412 |
|
413 if includePythonVariant: |
|
414 marker = PythonMarkers[sys.version_info.major] |
|
415 else: |
|
416 marker = "" |
|
417 |
|
418 if pyqtOverride and pyqtVariant == "PyQt4": |
|
419 pyqt4opt = " --pyqt4" |
|
420 else: |
|
421 pyqt4opt = "" |
|
422 |
|
423 # all kinds of Windows systems |
|
424 if sys.platform.startswith(("win", "cygwin")): |
|
425 wname = wfile + marker + ".cmd" |
|
426 if isGuiScript: |
|
427 wrapper = \ |
|
428 '''@echo off\n''' \ |
|
429 '''start "" "{2}\\pythonw.exe"''' \ |
|
430 ''' "{0}\\{1}.pyw"{3}''' \ |
|
431 ''' %1 %2 %3 %4 %5 %6 %7 %8 %9\n'''.format( |
|
432 pydir, wfile, sys.exec_prefix, pyqt4opt) |
|
433 else: |
|
434 wrapper = \ |
|
435 '''@"{0}\\python" "{1}\\{2}.py"{3}''' \ |
|
436 ''' %1 %2 %3 %4 %5 %6 %7 %8 %9\n'''.format( |
|
437 sys.exec_prefix, pydir, wfile, pyqt4opt) |
|
438 |
|
439 # Mac OS X |
|
440 elif sys.platform == "darwin": |
|
441 major = sys.version_info.major |
|
442 pyexec = "{0}/bin/pythonw{1}".format(sys.exec_prefix, major) |
|
443 if not os.path.exists(pyexec): |
|
444 pyexec = "{0}/bin/python{1}".format(sys.exec_prefix, major) |
|
445 wname = wfile + marker |
|
446 wrapper = ('''#!/bin/sh\n''' |
|
447 '''\n''' |
|
448 '''exec "{0}" "{1}/{2}.py"{3} "$@"\n''' |
|
449 .format(pyexec, pydir, wfile, pyqt4opt)) |
|
450 |
|
451 # *nix systems |
|
452 else: |
|
453 wname = wfile + marker |
|
454 wrapper = ('''#!/bin/sh\n''' |
|
455 '''\n''' |
|
456 '''exec "{0}" "{1}/{2}.py"{3} "$@"\n''' |
|
457 .format(sys.executable, pydir, wfile, pyqt4opt)) |
|
458 |
|
459 wname = os.path.join(saveDir, wname) |
|
460 copyToFile(wname, wrapper) |
|
461 os.chmod(wname, 0o755) |
|
462 |
|
463 return wname |
|
464 |
|
465 |
|
466 def copyTree(src, dst, filters, excludeDirs=None, excludePatterns=None): |
|
467 """ |
|
468 Copy Python, translation, documentation, wizards configuration, |
|
469 designer template files and DTDs of a directory tree. |
|
470 |
|
471 @param src name of the source directory |
|
472 @param dst name of the destination directory |
|
473 @param filters list of filter pattern determining the files to be copied |
|
474 @param excludeDirs list of (sub)directories to exclude from copying |
|
475 @keyparam excludePatterns list of filter pattern determining the files to |
|
476 be skipped |
|
477 """ |
|
478 if excludeDirs is None: |
|
479 excludeDirs = [] |
|
480 if excludePatterns is None: |
|
481 excludePatterns = [] |
|
482 try: |
|
483 names = os.listdir(src) |
|
484 except OSError: |
|
485 # ignore missing directories (most probably the i18n directory) |
|
486 return |
|
487 |
|
488 for name in names: |
|
489 skipIt = False |
|
490 for excludePattern in excludePatterns: |
|
491 if fnmatch.fnmatch(name, excludePattern): |
|
492 skipIt = True |
|
493 break |
|
494 if not skipIt: |
|
495 srcname = os.path.join(src, name) |
|
496 dstname = os.path.join(dst, name) |
|
497 for fileFilter in filters: |
|
498 if fnmatch.fnmatch(srcname, fileFilter): |
|
499 if not os.path.isdir(dst): |
|
500 os.makedirs(dst) |
|
501 shutil.copy2(srcname, dstname) |
|
502 os.chmod(dstname, 0o644) |
|
503 break |
|
504 else: |
|
505 if os.path.isdir(srcname) and srcname not in excludeDirs: |
|
506 copyTree(srcname, dstname, filters, |
|
507 excludePatterns=excludePatterns) |
|
508 |
|
509 |
|
510 def createGlobalPluginsDir(): |
|
511 """ |
|
512 Create the global plugins directory, if it doesn't exist. |
|
513 """ |
|
514 global cfg, distDir |
|
515 |
|
516 pdir = os.path.join(cfg['mdir'], "eric6plugins") |
|
517 fname = os.path.join(pdir, "__init__.py") |
|
518 if not os.path.exists(fname): |
|
519 if not os.path.exists(pdir): |
|
520 os.mkdir(pdir, 0o755) |
|
521 f = open(fname, "w") |
|
522 f.write( |
|
523 '''# -*- coding: utf-8 -*- |
|
524 |
|
525 """ |
|
526 Package containing the global plugins. |
|
527 """ |
|
528 ''' |
|
529 ) |
|
530 f.close() |
|
531 os.chmod(fname, 0o644) |
|
532 |
|
533 |
|
534 def cleanupSource(dirName): |
|
535 """ |
|
536 Cleanup the sources directory to get rid of leftover files |
|
537 and directories. |
|
538 |
|
539 @param dirName name of the directory to prune (string) |
|
540 """ |
|
541 # step 1: delete all Ui_*.py files without a corresponding |
|
542 # *.ui file |
|
543 dirListing = os.listdir(dirName) |
|
544 for formName, sourceName in [ |
|
545 (f.replace('Ui_', "").replace(".py", ".ui"), f) |
|
546 for f in dirListing if fnmatch.fnmatch(f, "Ui_*.py")]: |
|
547 if not os.path.exists(os.path.join(dirName, formName)): |
|
548 os.remove(os.path.join(dirName, sourceName)) |
|
549 if os.path.exists(os.path.join(dirName, sourceName + "c")): |
|
550 os.remove(os.path.join(dirName, sourceName + "c")) |
|
551 |
|
552 # step 2: delete the __pycache__ directory and all remaining *.pyc files |
|
553 if os.path.exists(os.path.join(dirName, "__pycache__")): |
|
554 shutil.rmtree(os.path.join(dirName, "__pycache__")) |
|
555 for name in [f for f in os.listdir(dirName) |
|
556 if fnmatch.fnmatch(f, "*.pyc")]: |
|
557 os.remove(os.path.join(dirName, name)) |
|
558 |
|
559 # step 3: delete *.orig files |
|
560 for name in [f for f in os.listdir(dirName) |
|
561 if fnmatch.fnmatch(f, "*.orig")]: |
|
562 os.remove(os.path.join(dirName, name)) |
|
563 |
|
564 # step 4: descent into subdirectories and delete them if empty |
|
565 for name in os.listdir(dirName): |
|
566 name = os.path.join(dirName, name) |
|
567 if os.path.isdir(name): |
|
568 cleanupSource(name) |
|
569 if len(os.listdir(name)) == 0: |
|
570 os.rmdir(name) |
|
571 |
|
572 |
|
573 def cleanUp(): |
|
574 """ |
|
575 Uninstall the old eric files. |
|
576 """ |
|
577 global platBinDir, platBinDirOld, includePythonVariant |
|
578 |
|
579 try: |
|
580 from eric6config import getConfig |
|
581 except ImportError: |
|
582 # eric6 wasn't installed previously |
|
583 return |
|
584 |
|
585 global pyModDir, progLanguages |
|
586 |
|
587 # Remove the menu entry for Linux systems |
|
588 if sys.platform.startswith("linux"): |
|
589 cleanUpLinuxSpecifics() |
|
590 # Remove the Desktop and Start Menu entries for Windows systems |
|
591 elif sys.platform.startswith(("win", "cygwin")): |
|
592 cleanUpWindowsLinks() |
|
593 |
|
594 # Remove the wrapper scripts |
|
595 rem_wnames = [ |
|
596 "eric6_api", "eric6_compare", |
|
597 "eric6_configure", "eric6_diff", |
|
598 "eric6_doc", "eric6_qregularexpression", |
|
599 "eric6_qregexp", "eric6_re", |
|
600 "eric6_trpreviewer", "eric6_uipreviewer", |
|
601 "eric6_unittest", "eric6", |
|
602 "eric6_tray", "eric6_editor", |
|
603 "eric6_plugininstall", "eric6_pluginuninstall", |
|
604 "eric6_pluginrepository", "eric6_sqlbrowser", |
|
605 "eric6_webbrowser", "eric6_iconeditor", |
|
606 "eric6_snap", "eric6_hexeditor", "eric6_browser", |
|
607 "eric6_shell", |
|
608 ] |
|
609 if includePythonVariant: |
|
610 marker = PythonMarkers[sys.version_info.major] |
|
611 rem_wnames = [n + marker for n in rem_wnames] |
|
612 |
|
613 try: |
|
614 dirs = [platBinDir, getConfig('bindir')] |
|
615 if platBinDirOld: |
|
616 dirs.append(platBinDirOld) |
|
617 for rem_wname in rem_wnames: |
|
618 for d in dirs: |
|
619 for rwname in wrapperNames(d, rem_wname): |
|
620 if os.path.exists(rwname): |
|
621 os.remove(rwname) |
|
622 |
|
623 # Cleanup our config file(s) |
|
624 for name in ['eric6config.py', 'eric6config.pyc', 'eric6.pth']: |
|
625 e6cfile = os.path.join(pyModDir, name) |
|
626 if os.path.exists(e6cfile): |
|
627 os.remove(e6cfile) |
|
628 e6cfile = os.path.join(pyModDir, "__pycache__", name) |
|
629 path, ext = os.path.splitext(e6cfile) |
|
630 for f in glob.glob("{0}.*{1}".format(path, ext)): |
|
631 os.remove(f) |
|
632 |
|
633 # Cleanup the install directories |
|
634 for name in ['ericExamplesDir', 'ericDocDir', 'ericDTDDir', |
|
635 'ericCSSDir', 'ericIconDir', 'ericPixDir', |
|
636 'ericTemplatesDir', 'ericCodeTemplatesDir', |
|
637 'ericOthersDir', 'ericStylesDir', 'ericDir']: |
|
638 if os.path.exists(getConfig(name)): |
|
639 shutil.rmtree(getConfig(name), True) |
|
640 |
|
641 # Cleanup translations |
|
642 for name in glob.glob( |
|
643 os.path.join(getConfig('ericTranslationsDir'), 'eric6_*.qm')): |
|
644 if os.path.exists(name): |
|
645 os.remove(name) |
|
646 |
|
647 # Cleanup API files |
|
648 try: |
|
649 apidir = getConfig('apidir') |
|
650 for progLanguage in progLanguages: |
|
651 for name in getConfig('apis'): |
|
652 apiname = os.path.join(apidir, progLanguage.lower(), name) |
|
653 if os.path.exists(apiname): |
|
654 os.remove(apiname) |
|
655 for apiname in glob.glob( |
|
656 os.path.join(apidir, progLanguage.lower(), "*.bas")): |
|
657 if os.path.basename(apiname) != "eric6.bas": |
|
658 os.remove(apiname) |
|
659 except AttributeError: |
|
660 pass |
|
661 |
|
662 if sys.platform == "darwin": |
|
663 # delete the Mac app bundle |
|
664 cleanUpMacAppBundle() |
|
665 except (IOError, OSError) as msg: |
|
666 sys.stderr.write( |
|
667 'Error: {0}\nTry install with admin rights.\n'.format(msg)) |
|
668 exit(7) |
|
669 |
|
670 |
|
671 def cleanUpLinuxSpecifics(): |
|
672 """ |
|
673 Clean up Linux specific files. |
|
674 """ |
|
675 if os.getuid() == 0: |
|
676 for name in ["/usr/share/pixmaps/eric.png", |
|
677 "/usr/share/pixmaps/ericWeb.png"]: |
|
678 if os.path.exists(name): |
|
679 os.remove(name) |
|
680 if includePythonVariant: |
|
681 marker = PythonMarkers[sys.version_info.major] |
|
682 else: |
|
683 marker = "" |
|
684 for name in [ |
|
685 "/usr/share/applications/eric6" + marker + ".desktop", |
|
686 "/usr/share/appdata/eric6" + marker + ".appdata.xml", |
|
687 "/usr/share/metainfo/eric6" + marker + ".appdata.xml", |
|
688 "/usr/share/applications/eric6_webbrowser" + marker + |
|
689 ".desktop", |
|
690 "/usr/share/applications/eric6_browser" + marker + |
|
691 ".desktop", |
|
692 "/usr/share/pixmaps/eric" + marker + ".png", |
|
693 "/usr/share/pixmaps/ericWeb" + marker + ".png", |
|
694 ]: |
|
695 if os.path.exists(name): |
|
696 os.remove(name) |
|
697 elif os.getuid() >= 1000: |
|
698 # it is assumed that user ids start at 1000 |
|
699 for name in ["~/.local/share/pixmaps/eric.png", |
|
700 "~/.local/share/pixmaps/ericWeb.png"]: |
|
701 path = os.path.expanduser(name) |
|
702 if os.path.exists(path): |
|
703 os.remove(path) |
|
704 if includePythonVariant: |
|
705 marker = PythonMarkers[sys.version_info.major] |
|
706 else: |
|
707 marker = "" |
|
708 for name in [ |
|
709 "~/.local/share/applications/eric6" + marker + ".desktop", |
|
710 "~/.local/share/appdata/eric6" + marker + ".appdata.xml", |
|
711 "~/.local/share/metainfo/eric6" + marker + ".appdata.xml", |
|
712 "~/.local/share/applications/eric6_webbrowser" + marker + |
|
713 ".desktop", |
|
714 "~/.local/share/applications/eric6_browser" + marker + |
|
715 ".desktop", |
|
716 "~/.local/share/pixmaps/eric" + marker + ".png", |
|
717 "~/.local/share/pixmaps/ericWeb" + marker + ".png", |
|
718 ]: |
|
719 path = os.path.expanduser(name) |
|
720 if os.path.exists(path): |
|
721 os.remove(path) |
|
722 |
|
723 |
|
724 def cleanUpMacAppBundle(): |
|
725 """ |
|
726 Uninstall the macOS application bundle. |
|
727 """ |
|
728 from eric6config import getConfig |
|
729 try: |
|
730 macAppBundlePath = getConfig("macAppBundlePath") |
|
731 macAppBundleName = getConfig("macAppBundleName") |
|
732 except AttributeError: |
|
733 macAppBundlePath = defaultMacAppBundlePath |
|
734 macAppBundleName = defaultMacAppBundleName |
|
735 for bundlePath in [os.path.join(defaultMacAppBundlePath, |
|
736 macAppBundleName), |
|
737 os.path.join(macAppBundlePath, |
|
738 macAppBundleName), |
|
739 ]: |
|
740 if os.path.exists(bundlePath): |
|
741 shutil.rmtree(bundlePath) |
|
742 |
|
743 |
|
744 def cleanUpWindowsLinks(): |
|
745 """ |
|
746 Clean up the Desktop and Start Menu entries for Windows. |
|
747 """ |
|
748 global doCleanDesktopLinks, forceCleanDesktopLinks |
|
749 |
|
750 try: |
|
751 from pywintypes import com_error # __IGNORE_WARNING__ |
|
752 except ImportError: |
|
753 # links were not created by install.py |
|
754 return |
|
755 |
|
756 regPath = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer" + \ |
|
757 "\\User Shell Folders" |
|
758 |
|
759 if doCleanDesktopLinks or forceCleanDesktopLinks: |
|
760 # 1. cleanup desktop links |
|
761 regName = "Desktop" |
|
762 desktopEntry = getWinregEntry(regName, regPath) |
|
763 if desktopEntry: |
|
764 desktopFolder = os.path.normpath(os.path.expandvars(desktopEntry)) |
|
765 for linkName in windowsDesktopNames(): |
|
766 linkPath = os.path.join(desktopFolder, linkName) |
|
767 if os.path.exists(linkPath): |
|
768 try: |
|
769 os.remove(linkPath) |
|
770 except EnvironmentError: |
|
771 # maybe restrictions prohibited link removal |
|
772 print("Could not remove '{0}'.".format(linkPath)) |
|
773 |
|
774 # 2. cleanup start menu entry |
|
775 regName = "Programs" |
|
776 programsEntry = getWinregEntry(regName, regPath) |
|
777 if programsEntry: |
|
778 programsFolder = os.path.normpath(os.path.expandvars(programsEntry)) |
|
779 eric6EntryPath = os.path.join(programsFolder, windowsProgramsEntry()) |
|
780 if os.path.exists(eric6EntryPath): |
|
781 try: |
|
782 shutil.rmtree(eric6EntryPath) |
|
783 except EnvironmentError: |
|
784 # maybe restrictions prohibited link removal |
|
785 print("Could not remove '{0}'.".format(eric6EntryPath)) |
|
786 |
|
787 |
|
788 def shutilCopy(src, dst, perm=0o644): |
|
789 """ |
|
790 Wrapper function around shutil.copy() to ensure the permissions. |
|
791 |
|
792 @param src source file name (string) |
|
793 @param dst destination file name or directory name (string) |
|
794 @keyparam perm permissions to be set (integer) |
|
795 """ |
|
796 shutil.copy(src, dst) |
|
797 if os.path.isdir(dst): |
|
798 dst = os.path.join(dst, os.path.basename(src)) |
|
799 os.chmod(dst, perm) |
|
800 |
|
801 |
|
802 def installEric(): |
|
803 """ |
|
804 Actually perform the installation steps. |
|
805 |
|
806 @return result code (integer) |
|
807 """ |
|
808 global distDir, doCleanup, cfg, progLanguages, sourceDir, configName |
|
809 global includePythonVariant, installApis |
|
810 |
|
811 # Create the platform specific wrappers. |
|
812 scriptsDir = "install_scripts" |
|
813 if not os.path.isdir(scriptsDir): |
|
814 os.mkdir(scriptsDir) |
|
815 wnames = [] |
|
816 for name in ["eric6_api", "eric6_doc"]: |
|
817 wnames.append(createPyWrapper(cfg['ericDir'], name, scriptsDir, False)) |
|
818 for name in ["eric6_compare", "eric6_configure", "eric6_diff", |
|
819 "eric6_editor", "eric6_hexeditor", "eric6_iconeditor", |
|
820 "eric6_plugininstall", "eric6_pluginrepository", |
|
821 "eric6_pluginuninstall", "eric6_qregexp", |
|
822 "eric6_qregularexpression", "eric6_re", "eric6_snap", |
|
823 "eric6_sqlbrowser", "eric6_tray", "eric6_trpreviewer", |
|
824 "eric6_uipreviewer", "eric6_unittest", "eric6_webbrowser", |
|
825 "eric6_browser", "eric6_shell", "eric6"]: |
|
826 wnames.append(createPyWrapper(cfg['ericDir'], name, scriptsDir)) |
|
827 |
|
828 # set install prefix, if not None |
|
829 if distDir: |
|
830 for key in list(cfg.keys()): |
|
831 cfg[key] = os.path.normpath(os.path.join(distDir, cfg[key])) |
|
832 |
|
833 try: |
|
834 # Install the files |
|
835 # make the install directories |
|
836 for key in cfg.keys(): |
|
837 if cfg[key] and not os.path.isdir(cfg[key]): |
|
838 os.makedirs(cfg[key]) |
|
839 |
|
840 # copy the eric6 config file |
|
841 if distDir: |
|
842 shutilCopy(configName, cfg['mdir']) |
|
843 if os.path.exists(configName + 'c'): |
|
844 shutilCopy(configName + 'c', cfg['mdir']) |
|
845 else: |
|
846 shutilCopy(configName, modDir) |
|
847 if os.path.exists(configName + 'c'): |
|
848 shutilCopy(configName + 'c', modDir) |
|
849 |
|
850 # copy the various parts of eric6 |
|
851 copyTree( |
|
852 eric6SourceDir, cfg['ericDir'], |
|
853 ['*.py', '*.pyc', '*.pyo', '*.pyw'], |
|
854 [os.path.join(sourceDir, "Examples"), |
|
855 os.path.join(sourceDir, ".ropeproject")], |
|
856 excludePatterns=["eric6config.py*"]) |
|
857 copyTree( |
|
858 os.path.join(eric6SourceDir, "Plugins"), |
|
859 os.path.join(cfg['ericDir'], "Plugins"), |
|
860 ['*.svgz', '*.svg', '*.png', '*.style', '*.tmpl', '*.txt']) |
|
861 copyTree( |
|
862 os.path.join(eric6SourceDir, "Documentation"), |
|
863 cfg['ericDocDir'], |
|
864 ['*.html', '*.qch']) |
|
865 copyTree( |
|
866 os.path.join(eric6SourceDir, "CSSs"), |
|
867 cfg['ericCSSDir'], |
|
868 ['*.css']) |
|
869 copyTree( |
|
870 os.path.join(eric6SourceDir, "Styles"), |
|
871 cfg['ericStylesDir'], |
|
872 ['*.qss', '*.e4h', '*.e6h']) |
|
873 copyTree( |
|
874 os.path.join(eric6SourceDir, "i18n"), |
|
875 cfg['ericTranslationsDir'], |
|
876 ['*.qm']) |
|
877 copyTree( |
|
878 os.path.join(eric6SourceDir, "icons"), |
|
879 cfg['ericIconDir'], |
|
880 ['*.svgz', '*.svg', '*.png', 'LICENSE*.*', 'readme.txt']) |
|
881 copyTree( |
|
882 os.path.join(eric6SourceDir, "pixmaps"), |
|
883 cfg['ericPixDir'], |
|
884 ['*.svgz', '*.svg', '*.png', '*.xpm', '*.ico', '*.gif']) |
|
885 copyTree( |
|
886 os.path.join(eric6SourceDir, "DesignerTemplates"), |
|
887 cfg['ericTemplatesDir'], |
|
888 ['*.tmpl']) |
|
889 copyTree( |
|
890 os.path.join(eric6SourceDir, "CodeTemplates"), |
|
891 cfg['ericCodeTemplatesDir'], |
|
892 ['*.tmpl']) |
|
893 |
|
894 # copy the wrappers |
|
895 for wname in wnames: |
|
896 shutilCopy(wname, cfg['bindir'], perm=0o755) |
|
897 os.remove(wname) |
|
898 shutil.rmtree(scriptsDir) |
|
899 |
|
900 # copy the license file |
|
901 shutilCopy(os.path.join(sourceDir, "docs", "LICENSE.GPL3"), |
|
902 cfg['ericDir']) |
|
903 |
|
904 # create the global plugins directory |
|
905 createGlobalPluginsDir() |
|
906 |
|
907 except (IOError, OSError) as msg: |
|
908 sys.stderr.write( |
|
909 'Error: {0}\nTry install with admin rights.\n'.format(msg)) |
|
910 return(7) |
|
911 |
|
912 # copy some text files to the doc area |
|
913 for name in ["LICENSE.GPL3", "THANKS", "changelog"]: |
|
914 try: |
|
915 shutilCopy(os.path.join(sourceDir, "docs", name), |
|
916 cfg['ericDocDir']) |
|
917 except EnvironmentError: |
|
918 print("Could not install '{0}'.".format( |
|
919 os.path.join(sourceDir, "docs", name))) |
|
920 for name in glob.glob(os.path.join(sourceDir, 'docs', 'README*.*')): |
|
921 try: |
|
922 shutilCopy(name, cfg['ericDocDir']) |
|
923 except EnvironmentError: |
|
924 print("Could not install '{0}'.".format(name)) |
|
925 |
|
926 # copy some more stuff |
|
927 for name in ['default.e4k', 'default_Mac.e4k']: |
|
928 try: |
|
929 shutilCopy(os.path.join(sourceDir, "others", name), |
|
930 cfg['ericOthersDir']) |
|
931 except EnvironmentError: |
|
932 print("Could not install '{2}{0}others{0}{1}'.".format( |
|
933 os.path.join(sourceDir, "docs", name))) |
|
934 |
|
935 # install the API file |
|
936 if installApis: |
|
937 for progLanguage in progLanguages: |
|
938 apidir = os.path.join(cfg['apidir'], progLanguage.lower()) |
|
939 print("Installing {0} API files to '{1}'.".format( |
|
940 progLanguage, apidir)) |
|
941 if not os.path.exists(apidir): |
|
942 os.makedirs(apidir) |
|
943 for apiName in glob.glob(os.path.join(eric6SourceDir, "APIs", |
|
944 progLanguage, "*.api")): |
|
945 try: |
|
946 shutilCopy(apiName, apidir) |
|
947 except EnvironmentError: |
|
948 print("Could not install '{0}' (no permission)." |
|
949 .format(apiName)) |
|
950 for apiName in glob.glob(os.path.join(eric6SourceDir, "APIs", |
|
951 progLanguage, "*.bas")): |
|
952 try: |
|
953 shutilCopy(apiName, apidir) |
|
954 except EnvironmentError: |
|
955 print("Could not install '{0}' (no permission)." |
|
956 .format(apiName)) |
|
957 if progLanguage == "Python": |
|
958 # copy Python3 API files to the same destination |
|
959 for apiName in glob.glob(os.path.join(eric6SourceDir, "APIs", |
|
960 "Python3", "*.api")): |
|
961 try: |
|
962 shutilCopy(apiName, apidir) |
|
963 except EnvironmentError: |
|
964 print("Could not install '{0}' (no permission)." |
|
965 .format(apiName)) |
|
966 for apiName in glob.glob(os.path.join(eric6SourceDir, "APIs", |
|
967 "Python3", "*.bas")): |
|
968 if os.path.exists(os.path.join( |
|
969 apidir, os.path.basename( |
|
970 apiName.replace(".bas", ".api")))): |
|
971 try: |
|
972 shutilCopy(apiName, apidir) |
|
973 except EnvironmentError: |
|
974 print("Could not install '{0}' (no permission)." |
|
975 .format(apiName)) |
|
976 |
|
977 # Create menu entry for Linux systems |
|
978 if sys.platform.startswith("linux"): |
|
979 createLinuxSpecifics() |
|
980 |
|
981 # Create Desktop and Start Menu entries for Windows systems |
|
982 elif sys.platform.startswith(("win", "cygwin")): |
|
983 createWindowsLinks() |
|
984 |
|
985 # Create a Mac application bundle |
|
986 elif sys.platform == "darwin": |
|
987 createMacAppBundle(cfg['ericDir']) |
|
988 |
|
989 return 0 |
|
990 |
|
991 |
|
992 def createLinuxSpecifics(): |
|
993 """ |
|
994 Install Linux specific files. |
|
995 """ |
|
996 global distDir, sourceDir, includePythonVariant |
|
997 |
|
998 if includePythonVariant: |
|
999 marker = PythonMarkers[sys.version_info.major] |
|
1000 else: |
|
1001 marker = "" |
|
1002 |
|
1003 if distDir: |
|
1004 dst = os.path.normpath(os.path.join(distDir, "usr/share/pixmaps")) |
|
1005 if not os.path.exists(dst): |
|
1006 os.makedirs(dst) |
|
1007 shutilCopy( |
|
1008 os.path.join(eric6SourceDir, "icons", "default", "eric.png"), |
|
1009 os.path.join(dst, "eric" + marker + ".png")) |
|
1010 shutilCopy( |
|
1011 os.path.join(eric6SourceDir, "icons", "default", "ericWeb48.png"), |
|
1012 os.path.join(dst, "ericWeb" + marker + ".png")) |
|
1013 dst = os.path.normpath( |
|
1014 os.path.join(distDir, "usr/share/applications")) |
|
1015 if not os.path.exists(dst): |
|
1016 os.makedirs(dst) |
|
1017 copyDesktopFile(os.path.join(sourceDir, "linux", "eric6.desktop.in"), |
|
1018 os.path.join(dst, "eric6" + marker + ".desktop"), |
|
1019 marker) |
|
1020 copyDesktopFile( |
|
1021 os.path.join(sourceDir, "linux", "eric6_webbrowser.desktop.in"), |
|
1022 os.path.join(dst, "eric6_webbrowser" + marker + ".desktop"), |
|
1023 marker) |
|
1024 copyDesktopFile( |
|
1025 os.path.join(sourceDir, "linux", "eric6_browser.desktop.in"), |
|
1026 os.path.join(dst, "eric6_browser" + marker + ".desktop"), |
|
1027 marker) |
|
1028 dst = os.path.normpath( |
|
1029 os.path.join(distDir, "usr/share/metainfo")) |
|
1030 if not os.path.exists(dst): |
|
1031 os.makedirs(dst) |
|
1032 copyAppStreamFile( |
|
1033 os.path.join(sourceDir, "linux", "eric6.appdata.xml.in"), |
|
1034 os.path.join(dst, "eric6" + marker + ".appdata.xml"), |
|
1035 marker) |
|
1036 elif os.getuid() == 0: |
|
1037 shutilCopy(os.path.join( |
|
1038 eric6SourceDir, "icons", "default", "eric.png"), |
|
1039 "/usr/share/pixmaps/eric" + marker + ".png") |
|
1040 copyDesktopFile( |
|
1041 os.path.join(sourceDir, "linux", "eric6.desktop.in"), |
|
1042 "/usr/share/applications/eric6" + marker + ".desktop", |
|
1043 marker) |
|
1044 if os.path.exists("/usr/share/metainfo"): |
|
1045 copyAppStreamFile( |
|
1046 os.path.join(sourceDir, "linux", "eric6.appdata.xml.in"), |
|
1047 "/usr/share/metainfo/eric6" + marker + ".appdata.xml", |
|
1048 marker) |
|
1049 elif os.path.exists("/usr/share/appdata"): |
|
1050 copyAppStreamFile( |
|
1051 os.path.join(sourceDir, "linux", "eric6.appdata.xml.in"), |
|
1052 "/usr/share/appdata/eric6" + marker + ".appdata.xml", |
|
1053 marker) |
|
1054 shutilCopy(os.path.join( |
|
1055 eric6SourceDir, "icons", "default", "ericWeb48.png"), |
|
1056 "/usr/share/pixmaps/ericWeb" + marker + ".png") |
|
1057 copyDesktopFile( |
|
1058 os.path.join(sourceDir, "linux", "eric6_webbrowser.desktop.in"), |
|
1059 "/usr/share/applications/eric6_webbrowser" + marker + |
|
1060 ".desktop", |
|
1061 marker) |
|
1062 copyDesktopFile( |
|
1063 os.path.join(sourceDir, "linux", "eric6_browser.desktop.in"), |
|
1064 "/usr/share/applications/eric6_browser" + marker + |
|
1065 ".desktop", |
|
1066 marker) |
|
1067 elif os.getuid() >= 1000: |
|
1068 # it is assumed, that user ids start at 1000 |
|
1069 localPath = os.path.join(os.path.expanduser("~"), |
|
1070 ".local", "share") |
|
1071 # create directories first |
|
1072 for directory in [os.path.join(localPath, name) |
|
1073 for name in ("pixmaps", "applications", |
|
1074 "metainfo", "appdata")]: |
|
1075 if not os.path.isdir(directory): |
|
1076 os.makedirs(directory) |
|
1077 # now copy the files |
|
1078 shutilCopy( |
|
1079 os.path.join(eric6SourceDir, "icons", "default", "eric.png"), |
|
1080 os.path.join(localPath, "pixmaps", "eric" + marker + ".png")) |
|
1081 copyDesktopFile( |
|
1082 os.path.join(sourceDir, "linux", "eric6.desktop.in"), |
|
1083 os.path.join(localPath, "applications", |
|
1084 "eric6" + marker + ".desktop"), |
|
1085 marker) |
|
1086 copyAppStreamFile( |
|
1087 os.path.join(sourceDir, "linux", "eric6.appdata.xml.in"), |
|
1088 os.path.join(localPath, "metainfo", |
|
1089 "eric6" + marker + ".appdata.xml"), |
|
1090 marker) |
|
1091 copyAppStreamFile( |
|
1092 os.path.join(sourceDir, "linux", "eric6.appdata.xml.in"), |
|
1093 os.path.join(localPath, "appdata", |
|
1094 "eric6" + marker + ".appdata.xml"), |
|
1095 marker) |
|
1096 shutilCopy( |
|
1097 os.path.join(eric6SourceDir, "icons", "default", "ericWeb48.png"), |
|
1098 os.path.join(localPath, "pixmaps", |
|
1099 "ericWeb" + marker + ".png")) |
|
1100 copyDesktopFile( |
|
1101 os.path.join(sourceDir, "linux", "eric6_webbrowser.desktop.in"), |
|
1102 os.path.join(localPath, "applications", |
|
1103 "eric6_webbrowser" + marker + ".desktop"), |
|
1104 marker) |
|
1105 copyDesktopFile( |
|
1106 os.path.join(sourceDir, "linux", "eric6_browser.desktop.in"), |
|
1107 os.path.join(localPath, "applications", |
|
1108 "eric6_browser" + marker + ".desktop"), |
|
1109 marker) |
|
1110 |
|
1111 |
|
1112 def createWindowsLinks(): |
|
1113 """ |
|
1114 Create Desktop and Start Menu links. |
|
1115 """ |
|
1116 try: |
|
1117 # check, if pywin32 is available |
|
1118 from win32com.client import Dispatch # __IGNORE_WARNING__ |
|
1119 except ImportError: |
|
1120 installed = pipInstall( |
|
1121 "pywin32", |
|
1122 "\nThe Python package 'pywin32' could not be imported." |
|
1123 ) |
|
1124 if installed: |
|
1125 # create the links via an external script to get around some |
|
1126 # startup magic done by pywin32.pth |
|
1127 args = [ |
|
1128 sys.executable, |
|
1129 os.path.join(os.path.dirname(__file__), |
|
1130 "create_windows_links.py"), |
|
1131 ] |
|
1132 if includePythonVariant: |
|
1133 args.append("-y") |
|
1134 subprocess.call(args) |
|
1135 else: |
|
1136 print( |
|
1137 "\nThe Python package 'pywin32' is not installed. Desktop and" |
|
1138 " Start Menu entries will not be created." |
|
1139 ) |
|
1140 return |
|
1141 |
|
1142 regPath = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer" + \ |
|
1143 "\\User Shell Folders" |
|
1144 |
|
1145 # 1. create desktop shortcuts |
|
1146 regName = "Desktop" |
|
1147 desktopFolder = os.path.normpath( |
|
1148 os.path.expandvars(getWinregEntry(regName, regPath))) |
|
1149 for linkName, targetPath, iconPath in windowsDesktopEntries(): |
|
1150 linkPath = os.path.join(desktopFolder, linkName) |
|
1151 createWindowsShortcut(linkPath, targetPath, iconPath) |
|
1152 |
|
1153 # 2. create start menu entry and shortcuts |
|
1154 regName = "Programs" |
|
1155 programsEntry = getWinregEntry(regName, regPath) |
|
1156 if programsEntry: |
|
1157 programsFolder = os.path.normpath(os.path.expandvars(programsEntry)) |
|
1158 eric6EntryPath = os.path.join(programsFolder, windowsProgramsEntry()) |
|
1159 if not os.path.exists(eric6EntryPath): |
|
1160 try: |
|
1161 os.makedirs(eric6EntryPath) |
|
1162 except EnvironmentError: |
|
1163 # maybe restrictions prohibited link creation |
|
1164 return |
|
1165 |
|
1166 for linkName, targetPath, iconPath in windowsDesktopEntries(): |
|
1167 linkPath = os.path.join(eric6EntryPath, linkName) |
|
1168 createWindowsShortcut(linkPath, targetPath, iconPath) |
|
1169 |
|
1170 |
|
1171 def createMacAppBundle(pydir): |
|
1172 """ |
|
1173 Create a Mac application bundle. |
|
1174 |
|
1175 @param pydir the name of the directory where the Python script will |
|
1176 eventually be installed |
|
1177 @type str |
|
1178 """ |
|
1179 global cfg, macAppBundleName, macPythonExe, macAppBundlePath, pyqtVariant |
|
1180 |
|
1181 dirs = { |
|
1182 "contents": "{0}/{1}/Contents/".format( |
|
1183 macAppBundlePath, macAppBundleName), |
|
1184 "exe": "{0}/{1}/Contents/MacOS".format( |
|
1185 macAppBundlePath, macAppBundleName), |
|
1186 "icns": "{0}/{1}/Contents/Resources".format( |
|
1187 macAppBundlePath, macAppBundleName) |
|
1188 } |
|
1189 os.makedirs(dirs["contents"]) |
|
1190 os.makedirs(dirs["exe"]) |
|
1191 os.makedirs(dirs["icns"]) |
|
1192 |
|
1193 if macPythonExe == defaultMacPythonExe: |
|
1194 starter = os.path.join(dirs["exe"], "eric") |
|
1195 os.symlink(macPythonExe, starter) |
|
1196 else: |
|
1197 starter = "python{0}".format(sys.version_info.major) |
|
1198 |
|
1199 wname = os.path.join(dirs["exe"], "eric6") |
|
1200 |
|
1201 # determine entry for DYLD_FRAMEWORK_PATH |
|
1202 dyldLine = "" |
|
1203 try: |
|
1204 if pyqtVariant == "PyQt4": |
|
1205 from PyQt4.QtCore import QLibraryInfo |
|
1206 else: |
|
1207 from PyQt5.QtCore import QLibraryInfo |
|
1208 qtLibraryDir = QLibraryInfo.location(QLibraryInfo.LibrariesPath) |
|
1209 except ImportError: |
|
1210 qtLibraryDir = "" |
|
1211 if qtLibraryDir: |
|
1212 dyldLine = "DYLD_FRAMEWORK_PATH={0}\n".format(qtLibraryDir) |
|
1213 |
|
1214 # determine entry for PATH |
|
1215 pathLine = "" |
|
1216 path = os.getenv("PATH", "") |
|
1217 if path: |
|
1218 pybin = os.path.join(sys.exec_prefix, "bin") |
|
1219 pathlist = path.split(os.pathsep) |
|
1220 pathlist_n = [pybin] |
|
1221 for path_ in pathlist: |
|
1222 if path_ and path_ not in pathlist_n: |
|
1223 pathlist_n.append(path_) |
|
1224 pathLine = "PATH={0}\n".format(os.pathsep.join(pathlist_n)) |
|
1225 |
|
1226 # create the wrapper script |
|
1227 wrapper = ('''#!/bin/sh\n''' |
|
1228 '''\n''' |
|
1229 '''{0}''' |
|
1230 '''{1}''' |
|
1231 '''exec "{2}" "{3}/{4}.py" "$@"\n''' |
|
1232 .format(pathLine, dyldLine, starter, pydir, "eric6")) |
|
1233 copyToFile(wname, wrapper) |
|
1234 os.chmod(wname, 0o755) |
|
1235 |
|
1236 shutilCopy(os.path.join(eric6SourceDir, "pixmaps", "eric_2.icns"), |
|
1237 os.path.join(dirs["icns"], "eric.icns")) |
|
1238 |
|
1239 if os.path.exists(os.path.join("eric", "eric6", "UI", "Info.py")): |
|
1240 # Installing from archive |
|
1241 from eric.eric6.UI.Info import Version, CopyrightShort |
|
1242 elif os.path.exists(os.path.join("eric6", "UI", "Info.py")): |
|
1243 # Installing from source tree |
|
1244 from eric6.UI.Info import Version, CopyrightShort |
|
1245 else: |
|
1246 Version = "Unknown" |
|
1247 CopyrightShort = "(c) 2002 - 2019 Detlev Offenbach" |
|
1248 |
|
1249 copyToFile( |
|
1250 os.path.join(dirs["contents"], "Info.plist"), |
|
1251 '''<?xml version="1.0" encoding="UTF-8"?>\n''' |
|
1252 '''<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"\n''' |
|
1253 ''' "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n''' |
|
1254 '''<plist version="1.0">\n''' |
|
1255 '''<dict>\n''' |
|
1256 ''' <key>CFBundleExecutable</key>\n''' |
|
1257 ''' <string>eric6</string>\n''' |
|
1258 ''' <key>CFBundleIconFile</key>\n''' |
|
1259 ''' <string>eric.icns</string>\n''' |
|
1260 ''' <key>CFBundleInfoDictionaryVersion</key>\n''' |
|
1261 ''' <string>{1}</string>\n''' |
|
1262 ''' <key>CFBundleName</key>\n''' |
|
1263 ''' <string>{0}</string>\n''' |
|
1264 ''' <key>CFBundleDisplayName</key>\n''' |
|
1265 ''' <string>{0}</string>\n''' |
|
1266 ''' <key>CFBundlePackageType</key>\n''' |
|
1267 ''' <string>APPL</string>\n''' |
|
1268 ''' <key>CFBundleSignature</key>\n''' |
|
1269 ''' <string>ERIC-IDE</string>\n''' |
|
1270 ''' <key>CFBundleVersion</key>\n''' |
|
1271 ''' <string>{1}</string>\n''' |
|
1272 ''' <key>CFBundleGetInfoString</key>\n''' |
|
1273 ''' <string>{1}, {2}</string>\n''' |
|
1274 ''' <key>CFBundleIdentifier</key>\n''' |
|
1275 ''' <string>org.python-projects.eric-ide</string>\n''' |
|
1276 '''</dict>\n''' |
|
1277 '''</plist>\n'''.format( |
|
1278 macAppBundleName.replace(".app", ""), |
|
1279 Version.split(None, 1)[0], |
|
1280 CopyrightShort)) |
|
1281 |
|
1282 |
|
1283 def createInstallConfig(): |
|
1284 """ |
|
1285 Create the installation config dictionary. |
|
1286 """ |
|
1287 global modDir, platBinDir, cfg, apisDir, installApis |
|
1288 |
|
1289 ericdir = os.path.join(modDir, "eric6") |
|
1290 cfg = { |
|
1291 'ericDir': ericdir, |
|
1292 'ericPixDir': os.path.join(ericdir, "pixmaps"), |
|
1293 'ericIconDir': os.path.join(ericdir, "icons"), |
|
1294 'ericDTDDir': os.path.join(ericdir, "DTDs"), |
|
1295 'ericCSSDir': os.path.join(ericdir, "CSSs"), |
|
1296 'ericStylesDir': os.path.join(ericdir, "Styles"), |
|
1297 'ericDocDir': os.path.join(ericdir, "Documentation"), |
|
1298 'ericExamplesDir': os.path.join(ericdir, "Examples"), |
|
1299 'ericTranslationsDir': os.path.join(ericdir, "i18n"), |
|
1300 'ericTemplatesDir': os.path.join(ericdir, "DesignerTemplates"), |
|
1301 'ericCodeTemplatesDir': os.path.join(ericdir, 'CodeTemplates'), |
|
1302 'ericOthersDir': ericdir, |
|
1303 'bindir': platBinDir, |
|
1304 'mdir': modDir, |
|
1305 } |
|
1306 if installApis: |
|
1307 if apisDir: |
|
1308 cfg['apidir'] = apisDir |
|
1309 else: |
|
1310 cfg['apidir'] = os.path.join(ericdir, "api") |
|
1311 else: |
|
1312 cfg['apidir'] = "" |
|
1313 configLength = 15 |
|
1314 |
|
1315 |
|
1316 def createConfig(): |
|
1317 """ |
|
1318 Create a config file with the respective config entries. |
|
1319 """ |
|
1320 global cfg, macAppBundlePath, configName |
|
1321 |
|
1322 apis = [] |
|
1323 if installApis: |
|
1324 for progLanguage in progLanguages: |
|
1325 for apiName in sorted(glob.glob( |
|
1326 os.path.join(eric6SourceDir, "APIs", progLanguage, "*.api"))): |
|
1327 apis.append(os.path.basename(apiName)) |
|
1328 if progLanguage == "Python": |
|
1329 # treat Python3 API files the same as Python API files |
|
1330 for apiName in sorted(glob.glob( |
|
1331 os.path.join(eric6SourceDir, "APIs", "Python3", "*.api"))): |
|
1332 apis.append(os.path.basename(apiName)) |
|
1333 |
|
1334 config = ( |
|
1335 """# -*- coding: utf-8 -*-\n""" |
|
1336 """#\n""" |
|
1337 """# This module contains the configuration of the individual eric6""" |
|
1338 """ installation\n""" |
|
1339 """#\n""" |
|
1340 """\n""" |
|
1341 """_pkg_config = {{\n""" |
|
1342 """ 'ericDir': r'{0}',\n""" |
|
1343 """ 'ericPixDir': r'{1}',\n""" |
|
1344 """ 'ericIconDir': r'{2}',\n""" |
|
1345 """ 'ericDTDDir': r'{3}',\n""" |
|
1346 """ 'ericCSSDir': r'{4}',\n""" |
|
1347 """ 'ericStylesDir': r'{5}',\n""" |
|
1348 """ 'ericDocDir': r'{6}',\n""" |
|
1349 """ 'ericExamplesDir': r'{7}',\n""" |
|
1350 """ 'ericTranslationsDir': r'{8}',\n""" |
|
1351 """ 'ericTemplatesDir': r'{9}',\n""" |
|
1352 """ 'ericCodeTemplatesDir': r'{10}',\n""" |
|
1353 """ 'ericOthersDir': r'{11}',\n""" |
|
1354 """ 'bindir': r'{12}',\n""" |
|
1355 """ 'mdir': r'{13}',\n""" |
|
1356 """ 'apidir': r'{14}',\n""" |
|
1357 """ 'apis': {15},\n""" |
|
1358 """ 'macAppBundlePath': r'{16}',\n""" |
|
1359 """ 'macAppBundleName': r'{17}',\n""" |
|
1360 """}}\n""" |
|
1361 """\n""" |
|
1362 """def getConfig(name):\n""" |
|
1363 """ '''\n""" |
|
1364 """ Module function to get a configuration value.\n""" |
|
1365 """\n""" |
|
1366 """ @param name name of the configuration value""" |
|
1367 """ @type str\n""" |
|
1368 """ @exception AttributeError raised to indicate an invalid""" |
|
1369 """ config entry\n""" |
|
1370 """ '''\n""" |
|
1371 """ try:\n""" |
|
1372 """ return _pkg_config[name]\n""" |
|
1373 """ except KeyError:\n""" |
|
1374 """ pass\n""" |
|
1375 """\n""" |
|
1376 """ raise AttributeError(\n""" |
|
1377 """ '"{{0}}" is not a valid configuration value'""" |
|
1378 """.format(name))\n""" |
|
1379 ).format( |
|
1380 cfg['ericDir'], cfg['ericPixDir'], cfg['ericIconDir'], |
|
1381 cfg['ericDTDDir'], cfg['ericCSSDir'], |
|
1382 cfg['ericStylesDir'], cfg['ericDocDir'], |
|
1383 cfg['ericExamplesDir'], cfg['ericTranslationsDir'], |
|
1384 cfg['ericTemplatesDir'], |
|
1385 cfg['ericCodeTemplatesDir'], cfg['ericOthersDir'], |
|
1386 cfg['bindir'], cfg['mdir'], |
|
1387 cfg['apidir'], sorted(apis), |
|
1388 macAppBundlePath, macAppBundleName, |
|
1389 ) |
|
1390 copyToFile(configName, config) |
|
1391 |
|
1392 |
|
1393 def pipInstall(packageName, message): |
|
1394 """ |
|
1395 Install the given package via pip. |
|
1396 |
|
1397 @param packageName name of the package to be installed |
|
1398 @type str |
|
1399 @param message message to be shown to the user |
|
1400 @type str |
|
1401 @return flag indicating a successful installation |
|
1402 @rtype bool |
|
1403 """ |
|
1404 ok = False |
|
1405 print("{0}\n\nShall '{1}' be installed using pip? (Y/n)" |
|
1406 .format(message, packageName), end=" ") |
|
1407 if sys.version_info[0] == 2: |
|
1408 answer = raw_input() # __IGNORE_WARNING__ |
|
1409 else: |
|
1410 answer = input() |
|
1411 if answer in ("", "Y", "y"): |
|
1412 exitCode = subprocess.call( |
|
1413 [sys.executable, "-m", "pip", "install", packageName]) |
|
1414 ok = (exitCode == 0) |
|
1415 |
|
1416 return ok |
|
1417 |
|
1418 |
|
1419 def doDependancyChecks(): |
|
1420 """ |
|
1421 Perform some dependency checks. |
|
1422 """ |
|
1423 print('Checking dependencies') |
|
1424 |
|
1425 # perform dependency checks |
|
1426 print("Python Version: {0:d}.{1:d}.{2:d}".format(*sys.version_info[:3])) |
|
1427 if sys.version_info < (2, 7, 10): |
|
1428 print('Sorry, you must have Python 2.7.10 or higher or ' |
|
1429 'Python 3.5.0 or higher.') |
|
1430 exit(5) |
|
1431 elif sys.version_info < (3, 5, 0) and sys.version_info[0] == 3: |
|
1432 print('Sorry, you must have Python 3.5.0 or higher.') |
|
1433 exit(5) |
|
1434 if sys.version_info[0] > 3: |
|
1435 print('Sorry, eric6 requires Python 3 or Python 2 for running.') |
|
1436 exit(5) |
|
1437 |
|
1438 try: |
|
1439 import xml.etree # __IGNORE_WARNING__ |
|
1440 except ImportError: |
|
1441 print('Your Python installation is missing the XML module.') |
|
1442 print('Please install it and try again.') |
|
1443 exit(5) |
|
1444 |
|
1445 if pyqtVariant == "PyQt4": |
|
1446 try: |
|
1447 from PyQt4.QtCore import qVersion |
|
1448 except ImportError as msg: |
|
1449 print('Sorry, please install PyQt4.') |
|
1450 print('Error: {0}'.format(msg)) |
|
1451 exit(1) |
|
1452 print("Found PyQt4") |
|
1453 else: |
|
1454 try: |
|
1455 from PyQt5.QtCore import qVersion |
|
1456 except ImportError as msg: |
|
1457 if sys.version_info[0] == 2: |
|
1458 # no PyQt5 wheels available for Python 2 |
|
1459 installed = False |
|
1460 else: |
|
1461 installed = pipInstall( |
|
1462 "PyQt5", |
|
1463 "PyQt5 could not be detected.\nError: {0}".format(msg) |
|
1464 ) |
|
1465 if installed: |
|
1466 # try to import it again |
|
1467 try: |
|
1468 from PyQt5.QtCore import qVersion |
|
1469 except ImportError as msg: |
|
1470 print('Sorry, please install PyQt5.') |
|
1471 print('Error: {0}'.format(msg)) |
|
1472 exit(1) |
|
1473 else: |
|
1474 print('Sorry, please install PyQt5.') |
|
1475 print('Error: {0}'.format(msg)) |
|
1476 exit(1) |
|
1477 print("Found PyQt5") |
|
1478 |
|
1479 try: |
|
1480 if pyqtVariant == "PyQt4": |
|
1481 pyuic = "pyuic4" |
|
1482 from PyQt4 import uic # __IGNORE_WARNING__ |
|
1483 else: |
|
1484 pyuic = "pyuic5" |
|
1485 from PyQt5 import uic # __IGNORE_WARNING__ |
|
1486 except ImportError as msg: |
|
1487 print("Sorry, {0} is not installed.".format(pyuic)) |
|
1488 print('Error: {0}'.format(msg)) |
|
1489 exit(1) |
|
1490 print("Found {0}".format(pyuic)) |
|
1491 |
|
1492 if pyqtVariant != "PyQt4": |
|
1493 try: |
|
1494 from PyQt5 import QtWebEngineWidgets # __IGNORE_WARNING__ |
|
1495 except ImportError as msg: |
|
1496 from PyQt5.QtCore import PYQT_VERSION |
|
1497 if PYQT_VERSION >= 0x50c00: |
|
1498 # PyQt 5.12 separated QtWebEngine into a separate wheel |
|
1499 installed = pipInstall( |
|
1500 "PyQtWebEngine", |
|
1501 "PyQtWebEngine could not be detected.\nError: {0}" |
|
1502 .format(msg) |
|
1503 ) |
|
1504 |
|
1505 try: |
|
1506 if pyqtVariant == "PyQt4": |
|
1507 from PyQt4 import Qsci # __IGNORE_WARNING__ |
|
1508 else: |
|
1509 from PyQt5 import Qsci # __IGNORE_WARNING__ |
|
1510 except ImportError as msg: |
|
1511 if pyqtVariant == "PyQt4": |
|
1512 message = str(msg) |
|
1513 else: |
|
1514 installed = pipInstall( |
|
1515 "QScintilla", |
|
1516 "QScintilla could not be detected.\nError: {0}".format(msg) |
|
1517 ) |
|
1518 if installed: |
|
1519 # try to import it again |
|
1520 try: |
|
1521 from PyQt5 import Qsci # __IGNORE_WARNING__ |
|
1522 message = None |
|
1523 except ImportError as msg: |
|
1524 message = str(msg) |
|
1525 else: |
|
1526 message = "QScintilla could not be installed." |
|
1527 if message: |
|
1528 print("Sorry, please install QScintilla2 and") |
|
1529 print("its PyQt5/PyQt4 wrapper.") |
|
1530 print('Error: {0}'.format(message)) |
|
1531 exit(1) |
|
1532 print("Found QScintilla2") |
|
1533 |
|
1534 if pyqtVariant == "PyQt4": |
|
1535 impModulesList = [ |
|
1536 "PyQt4.QtGui", "PyQt4.QtNetwork", "PyQt4.QtSql", |
|
1537 "PyQt4.QtSvg", "PyQt4.QtWebKit", |
|
1538 ] |
|
1539 altModulesList = [] |
|
1540 else: |
|
1541 impModulesList = [ |
|
1542 "PyQt5.QtGui", "PyQt5.QtNetwork", "PyQt5.QtPrintSupport", |
|
1543 "PyQt5.QtSql", "PyQt5.QtSvg", "PyQt5.QtWidgets", |
|
1544 ] |
|
1545 altModulesList = [ |
|
1546 # Tuple with alternatives, flag indicating it's ok to not be |
|
1547 # available (e.g. for 32-Bit Windows) |
|
1548 (("PyQt5.QtWebEngineWidgets", "PyQt5.QtWebKitWidgets"), |
|
1549 sys.maxsize <= 2**32), |
|
1550 ] |
|
1551 # check mandatory modules |
|
1552 modulesOK = True |
|
1553 for impModule in impModulesList: |
|
1554 name = impModule.split(".")[1] |
|
1555 try: |
|
1556 __import__(impModule) |
|
1557 print("Found", name) |
|
1558 except ImportError as msg: |
|
1559 print('Sorry, please install {0}.'.format(name)) |
|
1560 print('Error: {0}'.format(msg)) |
|
1561 modulesOK = False |
|
1562 if not modulesOK: |
|
1563 exit(1) |
|
1564 # check mandatory modules with alternatives |
|
1565 if altModulesList: |
|
1566 altModulesOK = True |
|
1567 for altModules, forcedOk in altModulesList: |
|
1568 modulesOK = False |
|
1569 for altModule in altModules: |
|
1570 name = altModule.split(".")[1] |
|
1571 try: |
|
1572 __import__(altModule) |
|
1573 print("Found", name) |
|
1574 modulesOK = True |
|
1575 break |
|
1576 except ImportError: |
|
1577 pass |
|
1578 if not modulesOK and not forcedOk: |
|
1579 altModulesOK = False |
|
1580 print('Sorry, please install {0}.' |
|
1581 .format(" or ".join(altModules))) |
|
1582 if not altModulesOK: |
|
1583 exit(1) |
|
1584 |
|
1585 # determine the platform dependent black list |
|
1586 if sys.platform.startswith(("win", "cygwin")): |
|
1587 PlatformBlackLists = PlatformsBlackLists["windows"] |
|
1588 elif sys.platform.startswith("linux"): |
|
1589 PlatformBlackLists = PlatformsBlackLists["linux"] |
|
1590 else: |
|
1591 PlatformBlackLists = PlatformsBlackLists["mac"] |
|
1592 |
|
1593 # check version of Qt |
|
1594 qtMajor = int(qVersion().split('.')[0]) |
|
1595 qtMinor = int(qVersion().split('.')[1]) |
|
1596 print("Qt Version: {0}".format(qVersion().strip())) |
|
1597 if qtMajor < 4 or \ |
|
1598 (qtMajor == 4 and qtMinor < 8) or \ |
|
1599 (qtMajor == 5 and qtMinor < 9): |
|
1600 print('Sorry, you must have Qt version 4.8.0 or better or') |
|
1601 print('5.9.0 or better.') |
|
1602 exit(2) |
|
1603 |
|
1604 # check version of sip |
|
1605 try: |
|
1606 try: |
|
1607 from PyQt5 import sip |
|
1608 except ImportError: |
|
1609 import sip |
|
1610 sipVersion = sip.SIP_VERSION_STR |
|
1611 print("sip Version:", sipVersion.strip()) |
|
1612 # always assume, that snapshots or dev versions are new enough |
|
1613 if "snapshot" not in sipVersion and "dev" not in sipVersion: |
|
1614 while sipVersion.count('.') < 2: |
|
1615 sipVersion += '.0' |
|
1616 (major, minor, pat) = sipVersion.split('.') |
|
1617 major = int(major) |
|
1618 minor = int(minor) |
|
1619 pat = int(pat) |
|
1620 if major < 4 or (major == 4 and minor < 14) or \ |
|
1621 (major == 4 and minor == 14 and pat < 2): |
|
1622 print('Sorry, you must have sip 4.14.2 or higher or' |
|
1623 ' a recent snapshot release.') |
|
1624 exit(3) |
|
1625 # check for blacklisted versions |
|
1626 for vers in BlackLists["sip"] + PlatformBlackLists["sip"]: |
|
1627 if vers == sipVersion: |
|
1628 print( |
|
1629 'Sorry, sip version {0} is not compatible with eric6.' |
|
1630 .format(vers)) |
|
1631 print('Please install another version.') |
|
1632 exit(3) |
|
1633 except (ImportError, AttributeError): |
|
1634 pass |
|
1635 |
|
1636 # check version of PyQt |
|
1637 if pyqtVariant == "PyQt4": |
|
1638 from PyQt4.QtCore import PYQT_VERSION_STR |
|
1639 else: |
|
1640 from PyQt5.QtCore import PYQT_VERSION_STR |
|
1641 pyqtVersion = PYQT_VERSION_STR |
|
1642 print("PyQt Version:", pyqtVersion.strip()) |
|
1643 # always assume, that snapshots or dev versions are new enough |
|
1644 if "snapshot" not in pyqtVersion and "dev" not in pyqtVersion: |
|
1645 while pyqtVersion.count('.') < 2: |
|
1646 pyqtVersion += '.0' |
|
1647 (major, minor, pat) = pyqtVersion.split('.') |
|
1648 major = int(major) |
|
1649 minor = int(minor) |
|
1650 pat = int(pat) |
|
1651 if major < 4 or \ |
|
1652 (major == 4 and minor < 10) or \ |
|
1653 (major == 5 and minor < 9): |
|
1654 print('Sorry, you must have PyQt 4.10.0 or better or') |
|
1655 print('PyQt 5.9.0 or better or' |
|
1656 ' a recent snapshot release.') |
|
1657 exit(4) |
|
1658 # check for blacklisted versions |
|
1659 for vers in BlackLists[pyqtVariant] + PlatformBlackLists[pyqtVariant]: |
|
1660 if vers == pyqtVersion: |
|
1661 print('Sorry, PyQt version {0} is not compatible with eric6.' |
|
1662 .format(vers)) |
|
1663 print('Please install another version.') |
|
1664 exit(4) |
|
1665 |
|
1666 # check version of QScintilla |
|
1667 if pyqtVariant == "PyQt4": |
|
1668 from PyQt4.Qsci import QSCINTILLA_VERSION_STR |
|
1669 else: |
|
1670 from PyQt5.Qsci import QSCINTILLA_VERSION_STR |
|
1671 scintillaVersion = QSCINTILLA_VERSION_STR |
|
1672 print("QScintilla Version:", QSCINTILLA_VERSION_STR.strip()) |
|
1673 # always assume, that snapshots or dev versions are new enough |
|
1674 if "snapshot" not in scintillaVersion and "dev" not in scintillaVersion: |
|
1675 while scintillaVersion.count('.') < 2: |
|
1676 scintillaVersion += '.0' |
|
1677 (major, minor, pat) = scintillaVersion.split('.') |
|
1678 major = int(major) |
|
1679 minor = int(minor) |
|
1680 pat = int(pat) |
|
1681 if major < 2 or (major == 2 and minor < 10): |
|
1682 print('Sorry, you must have QScintilla 2.10.0 or higher or' |
|
1683 ' a recent snapshot release.') |
|
1684 exit(5) |
|
1685 # check for blacklisted versions |
|
1686 for vers in BlackLists["QScintilla2"] + \ |
|
1687 PlatformBlackLists["QScintilla2"]: |
|
1688 if vers == scintillaVersion: |
|
1689 print( |
|
1690 'Sorry, QScintilla2 version {0} is not compatible with' |
|
1691 ' eric6.'.format(vers)) |
|
1692 print('Please install another version.') |
|
1693 exit(5) |
|
1694 |
|
1695 print("All dependencies ok.") |
|
1696 print() |
|
1697 |
|
1698 |
|
1699 def __pyName(py_dir, py_file): |
|
1700 """ |
|
1701 Local function to create the Python source file name for the compiled |
|
1702 .ui file. |
|
1703 |
|
1704 @param py_dir suggested name of the directory (string) |
|
1705 @param py_file suggested name for the compile source file (string) |
|
1706 @return tuple of directory name (string) and source file name (string) |
|
1707 """ |
|
1708 return py_dir, "Ui_{0}".format(py_file) |
|
1709 |
|
1710 |
|
1711 def compileUiFiles(): |
|
1712 """ |
|
1713 Compile the .ui files to Python sources. |
|
1714 """ |
|
1715 if pyqtVariant == "PyQt4": |
|
1716 from PyQt4.uic import compileUiDir |
|
1717 else: |
|
1718 from PyQt5.uic import compileUiDir |
|
1719 compileUiDir(eric6SourceDir, True, __pyName) |
|
1720 |
|
1721 |
|
1722 def prepareInfoFile(fileName): |
|
1723 """ |
|
1724 Function to prepare an Info.py file when installing from source. |
|
1725 |
|
1726 @param fileName name of the Python file containing the info (string) |
|
1727 """ |
|
1728 if not fileName: |
|
1729 return |
|
1730 |
|
1731 try: |
|
1732 os.rename(fileName, fileName + ".orig") |
|
1733 except EnvironmentError: |
|
1734 pass |
|
1735 try: |
|
1736 hgOut = subprocess.check_output(["hg", "identify", "-i"]) |
|
1737 if sys.version_info[0] == 3: |
|
1738 hgOut = hgOut.decode() |
|
1739 except (OSError, subprocess.CalledProcessError): |
|
1740 hgOut = "" |
|
1741 if hgOut: |
|
1742 hgOut = hgOut.strip() |
|
1743 if hgOut.endswith("+"): |
|
1744 hgOut = hgOut[:-1] |
|
1745 if sys.version_info[0] == 2: |
|
1746 f = codecs.open(fileName + ".orig", "r", "utf-8") |
|
1747 else: |
|
1748 f = open(fileName + ".orig", "r", encoding="utf-8") |
|
1749 text = f.read() |
|
1750 f.close() |
|
1751 text = text.replace("@@REVISION@@", hgOut)\ |
|
1752 .replace("@@VERSION@@", "rev_" + hgOut) |
|
1753 copyToFile(fileName, text) |
|
1754 else: |
|
1755 shutil.copy(fileName + ".orig", fileName) |
|
1756 |
|
1757 |
|
1758 def getWinregEntry(name, path): |
|
1759 """ |
|
1760 Function to get an entry from the Windows Registry. |
|
1761 |
|
1762 @param name variable name |
|
1763 @type str |
|
1764 @param path registry path of the variable |
|
1765 @type str |
|
1766 @return value of requested registry variable |
|
1767 @rtype any |
|
1768 """ |
|
1769 try: |
|
1770 import _winreg as winreg |
|
1771 except ImportError: |
|
1772 try: |
|
1773 import winreg |
|
1774 except ImportError: |
|
1775 return None |
|
1776 |
|
1777 try: |
|
1778 registryKey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0, |
|
1779 winreg.KEY_READ) |
|
1780 value, _ = winreg.QueryValueEx(registryKey, name) |
|
1781 winreg.CloseKey(registryKey) |
|
1782 return value |
|
1783 except WindowsError: |
|
1784 return None |
|
1785 |
|
1786 |
|
1787 def createWindowsShortcut(linkPath, targetPath, iconPath): |
|
1788 """ |
|
1789 Create Windows shortcut. |
|
1790 |
|
1791 @param linkPath path of the shortcut file |
|
1792 @type str |
|
1793 @param targetPath path the shortcut shall point to |
|
1794 @type str |
|
1795 @param iconPath path of the icon file |
|
1796 @type str |
|
1797 """ |
|
1798 from win32com.client import Dispatch |
|
1799 from pywintypes import com_error |
|
1800 |
|
1801 try: |
|
1802 shell = Dispatch('WScript.Shell') |
|
1803 shortcut = shell.CreateShortCut(linkPath) |
|
1804 shortcut.Targetpath = targetPath |
|
1805 shortcut.WorkingDirectory = os.path.dirname(targetPath) |
|
1806 shortcut.IconLocation = iconPath |
|
1807 shortcut.save() |
|
1808 except com_error: |
|
1809 # maybe restrictions prohibited link creation |
|
1810 pass |
|
1811 |
|
1812 |
|
1813 def windowsDesktopNames(): |
|
1814 """ |
|
1815 Function to generate the link names for the Windows Desktop. |
|
1816 |
|
1817 @return list of desktop link names |
|
1818 @rtype list of str |
|
1819 """ |
|
1820 return [e[0] for e in windowsDesktopEntries()] |
|
1821 |
|
1822 |
|
1823 def windowsDesktopEntries(): |
|
1824 """ |
|
1825 Function to generate data for the Windows Desktop links. |
|
1826 |
|
1827 @return list of tuples containing the desktop link name, |
|
1828 the link target and the icon target |
|
1829 @rtype list of tuples of (str, str, str) |
|
1830 """ |
|
1831 global cfg, includePythonVariant |
|
1832 |
|
1833 if includePythonVariant: |
|
1834 marker = PythonMarkers[sys.version_info.major] |
|
1835 else: |
|
1836 marker = "" |
|
1837 |
|
1838 majorVersion, minorVersion = sys.version_info[:2] |
|
1839 entriesTemplates = [ |
|
1840 ("eric6 (Python {0}.{1}).lnk", |
|
1841 os.path.join(cfg["bindir"], "eric6" + marker + ".cmd"), |
|
1842 os.path.join(cfg["ericPixDir"], "eric6.ico")), |
|
1843 ] |
|
1844 if sys.version_info.major == 2: |
|
1845 entriesTemplates.append(( |
|
1846 "eric6 Browser (Python {0}.{1}).lnk", |
|
1847 os.path.join(cfg["bindir"], "eric6_webbrowser" + marker + ".cmd"), |
|
1848 os.path.join(cfg["ericPixDir"], "ericWeb48.ico") |
|
1849 )) |
|
1850 else: |
|
1851 entriesTemplates.append(( |
|
1852 "eric6 Browser (Python {0}.{1}).lnk", |
|
1853 os.path.join(cfg["bindir"], "eric6_browser" + marker + ".cmd"), |
|
1854 os.path.join(cfg["ericPixDir"], "ericWeb48.ico") |
|
1855 )) |
|
1856 |
|
1857 return [ |
|
1858 (e[0].format(majorVersion, minorVersion), e[1], e[2]) |
|
1859 for e in entriesTemplates |
|
1860 ] |
|
1861 |
|
1862 |
|
1863 def windowsProgramsEntry(): |
|
1864 """ |
|
1865 Function to generate the name of the Start Menu top entry. |
|
1866 |
|
1867 @return name of the Start Menu top entry |
|
1868 @rtype str |
|
1869 """ |
|
1870 majorVersion, minorVersion = sys.version_info[:2] |
|
1871 return "eric6 (Python {0}.{1})".format(majorVersion, minorVersion) |
|
1872 |
|
1873 |
|
1874 def main(argv): |
|
1875 """ |
|
1876 The main function of the script. |
|
1877 |
|
1878 @param argv list of command line arguments |
|
1879 @type list of str |
|
1880 """ |
|
1881 import getopt |
|
1882 |
|
1883 # Parse the command line. |
|
1884 global progName, modDir, doCleanup, doCompile, distDir, cfg, apisDir |
|
1885 global sourceDir, eric6SourceDir, configName, includePythonVariant |
|
1886 global macAppBundlePath, macAppBundleName, macPythonExe |
|
1887 global pyqtVariant, pyqtOverride, installApis, doCleanDesktopLinks |
|
1888 |
|
1889 if sys.version_info < (2, 7, 0) or sys.version_info > (3, 9, 9): |
|
1890 print('Sorry, eric6 requires at least Python 2.7 or ' |
|
1891 'Python 3 for running.') |
|
1892 exit(5) |
|
1893 |
|
1894 progName = os.path.basename(argv[0]) |
|
1895 |
|
1896 if os.path.dirname(argv[0]): |
|
1897 os.chdir(os.path.dirname(argv[0])) |
|
1898 |
|
1899 determinePyQtVariant() |
|
1900 initGlobals() |
|
1901 |
|
1902 try: |
|
1903 if sys.platform.startswith(("win", "cygwin")): |
|
1904 optlist, args = getopt.getopt( |
|
1905 argv[1:], "chxyza:b:d:f:", |
|
1906 ["help", "pyqt=", "no-apis"]) |
|
1907 elif sys.platform == "darwin": |
|
1908 optlist, args = getopt.getopt( |
|
1909 argv[1:], "chxyza:b:d:f:i:m:n:p:", |
|
1910 ["help", "pyqt=", "no-apis"]) |
|
1911 else: |
|
1912 optlist, args = getopt.getopt( |
|
1913 argv[1:], "chxyza:b:d:f:i:", |
|
1914 ["help", "pyqt=", "no-apis"]) |
|
1915 except getopt.GetoptError as err: |
|
1916 print(err) |
|
1917 usage() |
|
1918 |
|
1919 global platBinDir |
|
1920 |
|
1921 depChecks = True |
|
1922 |
|
1923 for opt, arg in optlist: |
|
1924 if opt in ["-h", "--help"]: |
|
1925 usage(0) |
|
1926 elif opt == "-a": |
|
1927 apisDir = arg |
|
1928 elif opt == "-b": |
|
1929 platBinDir = arg |
|
1930 elif opt == "-d": |
|
1931 modDir = arg |
|
1932 elif opt == "-i": |
|
1933 distDir = os.path.normpath(arg) |
|
1934 elif opt == "-x": |
|
1935 depChecks = False |
|
1936 elif opt == "-c": |
|
1937 doCleanup = False |
|
1938 elif opt == "-z": |
|
1939 doCompile = False |
|
1940 elif opt == "-y": |
|
1941 includePythonVariant = True |
|
1942 elif opt == "-f": |
|
1943 try: |
|
1944 exec(compile(open(arg).read(), arg, 'exec'), globals()) |
|
1945 if len(cfg) != configLength: |
|
1946 print("The configuration dictionary in '{0}' is incorrect." |
|
1947 " Aborting".format(arg)) |
|
1948 exit(6) |
|
1949 except Exception: |
|
1950 cfg = {} |
|
1951 elif opt == "-m": |
|
1952 macAppBundleName = arg |
|
1953 elif opt == "-n": |
|
1954 macAppBundlePath = arg |
|
1955 elif opt == "-p": |
|
1956 macPythonExe = arg |
|
1957 elif opt == "--pyqt": |
|
1958 if arg not in ["4", "5"]: |
|
1959 print("Invalid PyQt version given; should be 4 or 5. Aborting") |
|
1960 exit(6) |
|
1961 pyqtVariant = "PyQt{0}".format(arg) |
|
1962 pyqtOverride = True |
|
1963 elif opt == "--no-apis": |
|
1964 installApis = False |
|
1965 elif opt == "--clean-desktop": |
|
1966 doCleanDesktopLinks = True |
|
1967 |
|
1968 infoName = "" |
|
1969 installFromSource = not os.path.isdir(sourceDir) |
|
1970 |
|
1971 # check dependencies |
|
1972 if depChecks: |
|
1973 doDependancyChecks() |
|
1974 |
|
1975 # cleanup source if installing from source |
|
1976 if installFromSource: |
|
1977 print("Cleaning up source ...") |
|
1978 sourceDir = os.path.abspath("..") |
|
1979 eric6SourceDir = os.path.join(sourceDir, "eric6") |
|
1980 cleanupSource(eric6SourceDir) |
|
1981 print() |
|
1982 |
|
1983 if installFromSource: |
|
1984 sourceDir = os.path.abspath("..") |
|
1985 eric6SourceDir = os.path.join(sourceDir, "eric6") |
|
1986 configName = os.path.join(eric6SourceDir, "eric6config.py") |
|
1987 if os.path.exists(os.path.join(sourceDir, ".hg")): |
|
1988 # we are installing from source with repo |
|
1989 infoName = os.path.join(eric6SourceDir, "UI", "Info.py") |
|
1990 prepareInfoFile(infoName) |
|
1991 |
|
1992 if len(cfg) == 0: |
|
1993 createInstallConfig() |
|
1994 |
|
1995 # get rid of development config file, if it exists |
|
1996 try: |
|
1997 if installFromSource: |
|
1998 os.rename(configName, configName + ".orig") |
|
1999 configNameC = configName + 'c' |
|
2000 if os.path.exists(configNameC): |
|
2001 os.remove(configNameC) |
|
2002 os.remove(configName) |
|
2003 except EnvironmentError: |
|
2004 pass |
|
2005 |
|
2006 # cleanup old installation |
|
2007 print("Cleaning up old installation ...") |
|
2008 try: |
|
2009 if doCleanup: |
|
2010 if distDir: |
|
2011 shutil.rmtree(distDir, True) |
|
2012 else: |
|
2013 cleanUp() |
|
2014 except (IOError, OSError) as msg: |
|
2015 sys.stderr.write('Error: {0}\nTry install as root.\n'.format(msg)) |
|
2016 exit(7) |
|
2017 |
|
2018 # Create a config file and delete the default one |
|
2019 print("\nCreating configuration file ...") |
|
2020 createConfig() |
|
2021 |
|
2022 # Compile .ui files |
|
2023 print("\nCompiling user interface files ...") |
|
2024 # step 1: remove old Ui_*.py files |
|
2025 for root, _, files in os.walk(sourceDir): |
|
2026 for file in [f for f in files if fnmatch.fnmatch(f, 'Ui_*.py')]: |
|
2027 os.remove(os.path.join(root, file)) |
|
2028 # step 2: compile the forms |
|
2029 compileUiFiles() |
|
2030 |
|
2031 if doCompile: |
|
2032 print("\nCompiling source files ...") |
|
2033 # Hide compile errors (mainly because of Py2/Py3 differences) |
|
2034 skipRe = re.compile(r"DebugClients[\\/]Python[\\/]") |
|
2035 sys.stdout = io.StringIO() |
|
2036 if distDir: |
|
2037 compileall.compile_dir( |
|
2038 eric6SourceDir, |
|
2039 ddir=os.path.join(distDir, modDir, cfg['ericDir']), |
|
2040 rx=skipRe, |
|
2041 quiet=True) |
|
2042 py_compile.compile( |
|
2043 configName, |
|
2044 dfile=os.path.join(distDir, modDir, "eric6config.py")) |
|
2045 else: |
|
2046 compileall.compile_dir( |
|
2047 eric6SourceDir, |
|
2048 ddir=os.path.join(modDir, cfg['ericDir']), |
|
2049 rx=skipRe, |
|
2050 quiet=True) |
|
2051 py_compile.compile(configName, |
|
2052 dfile=os.path.join(modDir, "eric6config.py")) |
|
2053 sys.stdout = sys.__stdout__ |
|
2054 print("\nInstalling eric6 ...") |
|
2055 res = installEric() |
|
2056 |
|
2057 # do some cleanup |
|
2058 try: |
|
2059 if installFromSource: |
|
2060 os.remove(configName) |
|
2061 configNameC = configName + 'c' |
|
2062 if os.path.exists(configNameC): |
|
2063 os.remove(configNameC) |
|
2064 os.rename(configName + ".orig", configName) |
|
2065 except EnvironmentError: |
|
2066 pass |
|
2067 try: |
|
2068 if installFromSource and infoName: |
|
2069 os.remove(infoName) |
|
2070 infoNameC = infoName + 'c' |
|
2071 if os.path.exists(infoNameC): |
|
2072 os.remove(infoNameC) |
|
2073 os.rename(infoName + ".orig", infoName) |
|
2074 except EnvironmentError: |
|
2075 pass |
|
2076 |
|
2077 print("\nInstallation complete.") |
|
2078 print() |
|
2079 |
|
2080 exit(res) |
|
2081 |
|
2082 |
|
2083 if __name__ == "__main__": |
|
2084 try: |
|
2085 main(sys.argv) |
|
2086 except SystemExit: |
|
2087 raise |
|
2088 except Exception: |
|
2089 print("""An internal error occured. Please report all the output""" |
|
2090 """ of the program,\nincluding the following traceback, to""" |
|
2091 """ eric-bugs@eric-ide.python-projects.org.\n""") |
|
2092 raise |
|
2093 |
|
2094 # |
|
2095 # eflag: noqa = M801 |