--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/install.py Sun Apr 14 15:09:21 2019 +0200 @@ -0,0 +1,2095 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright (c) 2002 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> +# +# This is the install script for eric6. + +""" +Installation script for the eric6 IDE and all eric6 related tools. +""" + +from __future__ import unicode_literals, print_function +try: + # Python2 only + import cStringIO as io + try: + from PyQt5 import sip + except ImportError: + import sip + sip.setapi('QString', 2) + sip.setapi('QVariant', 2) + sip.setapi('QTextStream', 2) + input = raw_input # __IGNORE_WARNING__ +except (ImportError): + import io # __IGNORE_WARNING__ + +import sys +import os +import re +import compileall +import py_compile +import glob +import shutil +import fnmatch +import codecs +import subprocess +import time + +# Define the globals. +progName = None +currDir = os.getcwd() +modDir = None +pyModDir = None +platBinDir = None +platBinDirOld = None +distDir = None +apisDir = None +installApis = True +doCleanup = True +doCleanDesktopLinks = False +forceCleanDesktopLinks = False +doCompile = True +includePythonVariant = False +cfg = {} +progLanguages = ["Python", "Ruby", "QSS"] +sourceDir = "eric" +eric6SourceDir = os.path.join(sourceDir, "eric6") +configName = 'eric6config.py' +defaultMacAppBundleName = "eric6.app" +defaultMacAppBundlePath = "/Applications" +defaultMacPythonExe = "{0}/Resources/Python.app/Contents/MacOS/Python".format( + sys.exec_prefix) +macAppBundleName = defaultMacAppBundleName +macAppBundlePath = defaultMacAppBundlePath +macPythonExe = defaultMacPythonExe +pyqtVariant = "" +pyqtOverride = False + +# Define blacklisted versions of the prerequisites +BlackLists = { + "sip": [], + "PyQt4": [], + "PyQt5": [], + "QScintilla2": [], +} +PlatformsBlackLists = { + "windows": { + "sip": [], + "PyQt4": [], + "PyQt5": [], + "QScintilla2": [], + }, + + "linux": { + "sip": [], + "PyQt4": [], + "PyQt5": [], + "QScintilla2": [], + }, + + "mac": { + "sip": [], + "PyQt4": [], + "PyQt5": [], + "QScintilla2": [], + }, +} + +# Define file name markers for Python variants +PythonMarkers = { + 2: "_py2", + 3: "_py3", +} +# Define a mapping of markers to full text +PythonTextMarkers = { + "_py2": "Python 2", + "_py3": "Python 3", +} + + +def exit(rcode=0): + """ + Exit the install script. + + @param rcode result code to report back (integer) + """ + global currDir + + print() + + if sys.platform.startswith(("win", "cygwin")): + # different meaning of input between Py2 and Py3 + try: + input("Press enter to continue...") + except (EOFError, SyntaxError): + pass + + os.chdir(currDir) + + sys.exit(rcode) + + +def usage(rcode=2): + """ + Display a usage message and exit. + + @param rcode the return code passed back to the calling process. + """ + global progName, modDir, distDir, apisDir + global macAppBundleName, macAppBundlePath, macPythonExe + global pyqtVariant + + print() + print("Usage:") + if sys.platform == "darwin": + print(" {0} [-chxyz] [-a dir] [-b dir] [-d dir] [-f file] [-i dir]" + " [-m name] [-n path] [-p python] [--no-apis] [--pyqt=version]" + .format(progName)) + elif sys.platform.startswith(("win", "cygwin")): + print(" {0} [-chxyz] [-a dir] [-b dir] [-d dir] [-f file]" + " [--clean-desktop] [--no-apis] [--pyqt=version]" + .format(progName)) + else: + print(" {0} [-chxyz] [-a dir] [-b dir] [-d dir] [-f file] [-i dir]" + " [--no-apis] [--pyqt=version]" + .format(progName)) + print("where:") + print(" -h, --help display this help message") + print(" -a dir where the API files will be installed") + if apisDir: + print(" (default: {0})".format(apisDir)) + else: + print(" (no default value)") + print(" --no-apis don't install API files") + print(" -b dir where the binaries will be installed") + print(" (default: {0})".format(platBinDir)) + print(" -d dir where eric6 python files will be installed") + print(" (default: {0})".format(modDir)) + print(" -f file configuration file naming the various installation" + " paths") + if not sys.platform.startswith(("win", "cygwin")): + print(" -i dir temporary install prefix") + print(" (default: {0})".format(distDir)) + if sys.platform == "darwin": + print(" -m name name of the Mac app bundle") + print(" (default: {0})".format(macAppBundleName)) + print(" -n path path of the directory the Mac app bundle will") + print(" be created in") + print(" (default: {0})".format(macAppBundlePath)) + print(" -p python path of the python executable") + print(" (default: {0})".format(macPythonExe)) + print(" -c don't cleanup old installation first") + if sys.platform.startswith(("win", "cygwin")): + (" --clean-desktop delete desktop links before installation") + print(" -x don't perform dependency checks (use on your own" + " risk)") + print(" -y add the Python variant to the executable names") + print(" -z don't compile the installed python files") + print(" --pyqt=version version of PyQt to be used (one of 4 or 5)") + if pyqtVariant: + print(" (default: {0})".format(pyqtVariant[-1])) + else: + print(" (no PyQt variant found)") + print() + print("The file given to the -f option must be valid Python code" + " defining a") + print("dictionary called 'cfg' with the keys 'ericDir', 'ericPixDir'," + " 'ericIconDir',") + print("'ericDTDDir', 'ericCSSDir', 'ericStylesDir', 'ericDocDir'," + " 'ericExamplesDir',") + print("'ericTranslationsDir', 'ericTemplatesDir', 'ericCodeTemplatesDir',") + print("'ericOthersDir','bindir', 'mdir' and 'apidir.") + print("These define the directories for the installation of the various" + " parts of eric6.") + + exit(rcode) + + +def determinePyQtVariant(): + """ + Module function to determine the PyQt variant to be used. + """ + global pyqtVariant, pyqtOverride + + pyqtVariant = "" + # need to handle the --pyqt= option here + if "--pyqt=4" in sys.argv: + pyqtVariant = "PyQt4" + pyqtOverride = True + elif "--pyqt=5" in sys.argv: + pyqtVariant = "PyQt5" + pyqtOverride = True + else: + try: + import PyQt5 # __IGNORE_WARNING__ + pyqtVariant = "PyQt5" + del sys.modules["PyQt5"] + except ImportError: + try: + import PyQt4 # __IGNORE_WARNING__ + pyqtVariant = "PyQt4" + del sys.modules["PyQt4"] + except ImportError: + # default to PyQt5, installation will be asked for + pyqtVariant = "PyQt5" + + +def initGlobals(): + """ + Module function to set the values of globals that need more than a + simple assignment. + """ + global platBinDir, modDir, pyModDir, apisDir, pyqtVariant, platBinDirOld + + try: + import distutils.sysconfig + except ImportError: + print("Please install the 'distutils' package first.") + exit(5) + + if sys.platform.startswith(("win", "cygwin")): + platBinDir = sys.exec_prefix + if platBinDir.endswith("\\"): + platBinDir = platBinDir[:-1] + platBinDirOld = platBinDir + platBinDir = os.path.join(platBinDir, "Scripts") + if not os.path.exists(platBinDir): + platBinDir = platBinDirOld + else: + pyBinDir = os.path.normpath(os.path.dirname(sys.executable)) + if os.access(pyBinDir, os.W_OK): + # install the eric scripts along the python executable + platBinDir = pyBinDir + else: + # install them in the user's bin directory + platBinDir = os.path.expanduser("~/bin") + if platBinDir != "/usr/local/bin" and \ + os.access("/usr/local/bin", os.W_OK): + platBinDirOld = "/usr/local/bin" + + modDir = distutils.sysconfig.get_python_lib(True) + pyModDir = modDir + + pyqtDataDir = os.path.join(modDir, pyqtVariant) + if os.path.exists(os.path.join(pyqtDataDir, "qsci")): + # it's the installer + qtDataDir = pyqtDataDir + elif os.path.exists(os.path.join(pyqtDataDir, "Qt", "qsci")): + # it's the wheel + qtDataDir = os.path.join(pyqtDataDir, "Qt") + else: + # determine dynamically + try: + if pyqtVariant == "PyQt4": + from PyQt4.QtCore import QLibraryInfo + else: + from PyQt5.QtCore import QLibraryInfo + qtDataDir = QLibraryInfo.location(QLibraryInfo.DataPath) + except ImportError: + qtDataDir = None + if qtDataDir: + apisDir = os.path.join(qtDataDir, "qsci", "api") + else: + apisDir = None + + +def copyToFile(name, text): + """ + Copy a string to a file. + + @param name the name of the file. + @param text the contents to copy to the file. + """ + f = open(name, "w") + if sys.version_info[0] == 2: + text = codecs.encode(text, "utf-8") + f.write(text) + f.close() + + +def copyDesktopFile(src, dst, marker): + """ + Modify a desktop file and write it to its destination. + + @param src source file name (string) + @param dst destination file name (string) + @param marker marker to be used (string) + """ + global cfg, platBinDir + + if sys.version_info[0] == 2: + f = codecs.open(src, "r", "utf-8") + else: + f = open(src, "r", encoding="utf-8") + text = f.read() + f.close() + + text = text.replace("@BINDIR@", platBinDir) + text = text.replace("@MARKER@", marker) + if marker: + t_marker = " ({0})".format(PythonTextMarkers[marker]) + else: + t_marker = "" + text = text.replace("@PY_MARKER@", t_marker) + + if sys.version_info[0] == 2: + f = codecs.open(dst, "w", "utf-8") + else: + f = open(dst, "w", encoding="utf-8") + f.write(text) + f.close() + os.chmod(dst, 0o644) + + +def copyAppStreamFile(src, dst, marker): + """ + Modify an appstream file and write it to its destination. + + @param src source file name (string) + @param dst destination file name (string) + @param marker marker to be used (string) + """ + if os.path.exists(os.path.join("eric", "eric6", "UI", "Info.py")): + # Installing from archive + from eric.eric6.UI.Info import Version + elif os.path.exists(os.path.join("eric6", "UI", "Info.py")): + # Installing from source tree + from eric6.UI.Info import Version + else: + Version = "Unknown" + + if sys.version_info[0] == 2: + f = codecs.open(src, "r", "utf-8") + else: + f = open(src, "r", encoding="utf-8") + text = f.read() + f.close() + + text = text.replace("@MARKER@", marker)\ + .replace("@VERSION@", Version.split(None, 1)[0])\ + .replace("@DATE@", time.strftime("%Y-%m-%d")) + + if sys.version_info[0] == 2: + f = codecs.open(dst, "w", "utf-8") + else: + f = open(dst, "w", encoding="utf-8") + f.write(text) + f.close() + os.chmod(dst, 0o644) + + +def wrapperNames(dname, wfile): + """ + Create the platform specific names for the wrapper script. + + @param dname name of the directory to place the wrapper into + @param wfile basename (without extension) of the wrapper script + @return the names of the wrapper scripts + """ + if sys.platform.startswith(("win", "cygwin")): + wnames = (dname + "\\" + wfile + ".cmd", + dname + "\\" + wfile + ".bat") + else: + wnames = (dname + "/" + wfile, ) + + return wnames + + +def createPyWrapper(pydir, wfile, saveDir, isGuiScript=True): + """ + Create an executable wrapper for a Python script. + + @param pydir the name of the directory where the Python script will + eventually be installed (string) + @param wfile the basename of the wrapper (string) + @param saveDir directory to save the file into (string) + @param isGuiScript flag indicating a wrapper script for a GUI + application (boolean) + @return the platform specific name of the wrapper (string) + """ + global includePythonVariant, pyqtVariant, pyqtOverride + + if includePythonVariant: + marker = PythonMarkers[sys.version_info.major] + else: + marker = "" + + if pyqtOverride and pyqtVariant == "PyQt4": + pyqt4opt = " --pyqt4" + else: + pyqt4opt = "" + + # all kinds of Windows systems + if sys.platform.startswith(("win", "cygwin")): + wname = wfile + marker + ".cmd" + if isGuiScript: + wrapper = \ + '''@echo off\n''' \ + '''start "" "{2}\\pythonw.exe"''' \ + ''' "{0}\\{1}.pyw"{3}''' \ + ''' %1 %2 %3 %4 %5 %6 %7 %8 %9\n'''.format( + pydir, wfile, sys.exec_prefix, pyqt4opt) + else: + wrapper = \ + '''@"{0}\\python" "{1}\\{2}.py"{3}''' \ + ''' %1 %2 %3 %4 %5 %6 %7 %8 %9\n'''.format( + sys.exec_prefix, pydir, wfile, pyqt4opt) + + # Mac OS X + elif sys.platform == "darwin": + major = sys.version_info.major + pyexec = "{0}/bin/pythonw{1}".format(sys.exec_prefix, major) + if not os.path.exists(pyexec): + pyexec = "{0}/bin/python{1}".format(sys.exec_prefix, major) + wname = wfile + marker + wrapper = ('''#!/bin/sh\n''' + '''\n''' + '''exec "{0}" "{1}/{2}.py"{3} "$@"\n''' + .format(pyexec, pydir, wfile, pyqt4opt)) + + # *nix systems + else: + wname = wfile + marker + wrapper = ('''#!/bin/sh\n''' + '''\n''' + '''exec "{0}" "{1}/{2}.py"{3} "$@"\n''' + .format(sys.executable, pydir, wfile, pyqt4opt)) + + wname = os.path.join(saveDir, wname) + copyToFile(wname, wrapper) + os.chmod(wname, 0o755) + + return wname + + +def copyTree(src, dst, filters, excludeDirs=None, excludePatterns=None): + """ + Copy Python, translation, documentation, wizards configuration, + designer template files and DTDs of a directory tree. + + @param src name of the source directory + @param dst name of the destination directory + @param filters list of filter pattern determining the files to be copied + @param excludeDirs list of (sub)directories to exclude from copying + @keyparam excludePatterns list of filter pattern determining the files to + be skipped + """ + if excludeDirs is None: + excludeDirs = [] + if excludePatterns is None: + excludePatterns = [] + try: + names = os.listdir(src) + except OSError: + # ignore missing directories (most probably the i18n directory) + return + + for name in names: + skipIt = False + for excludePattern in excludePatterns: + if fnmatch.fnmatch(name, excludePattern): + skipIt = True + break + if not skipIt: + srcname = os.path.join(src, name) + dstname = os.path.join(dst, name) + for fileFilter in filters: + if fnmatch.fnmatch(srcname, fileFilter): + if not os.path.isdir(dst): + os.makedirs(dst) + shutil.copy2(srcname, dstname) + os.chmod(dstname, 0o644) + break + else: + if os.path.isdir(srcname) and srcname not in excludeDirs: + copyTree(srcname, dstname, filters, + excludePatterns=excludePatterns) + + +def createGlobalPluginsDir(): + """ + Create the global plugins directory, if it doesn't exist. + """ + global cfg, distDir + + pdir = os.path.join(cfg['mdir'], "eric6plugins") + fname = os.path.join(pdir, "__init__.py") + if not os.path.exists(fname): + if not os.path.exists(pdir): + os.mkdir(pdir, 0o755) + f = open(fname, "w") + f.write( + '''# -*- coding: utf-8 -*- + +""" +Package containing the global plugins. +""" +''' + ) + f.close() + os.chmod(fname, 0o644) + + +def cleanupSource(dirName): + """ + Cleanup the sources directory to get rid of leftover files + and directories. + + @param dirName name of the directory to prune (string) + """ + # step 1: delete all Ui_*.py files without a corresponding + # *.ui file + dirListing = os.listdir(dirName) + for formName, sourceName in [ + (f.replace('Ui_', "").replace(".py", ".ui"), f) + for f in dirListing if fnmatch.fnmatch(f, "Ui_*.py")]: + if not os.path.exists(os.path.join(dirName, formName)): + os.remove(os.path.join(dirName, sourceName)) + if os.path.exists(os.path.join(dirName, sourceName + "c")): + os.remove(os.path.join(dirName, sourceName + "c")) + + # step 2: delete the __pycache__ directory and all remaining *.pyc files + if os.path.exists(os.path.join(dirName, "__pycache__")): + shutil.rmtree(os.path.join(dirName, "__pycache__")) + for name in [f for f in os.listdir(dirName) + if fnmatch.fnmatch(f, "*.pyc")]: + os.remove(os.path.join(dirName, name)) + + # step 3: delete *.orig files + for name in [f for f in os.listdir(dirName) + if fnmatch.fnmatch(f, "*.orig")]: + os.remove(os.path.join(dirName, name)) + + # step 4: descent into subdirectories and delete them if empty + for name in os.listdir(dirName): + name = os.path.join(dirName, name) + if os.path.isdir(name): + cleanupSource(name) + if len(os.listdir(name)) == 0: + os.rmdir(name) + + +def cleanUp(): + """ + Uninstall the old eric files. + """ + global platBinDir, platBinDirOld, includePythonVariant + + try: + from eric6config import getConfig + except ImportError: + # eric6 wasn't installed previously + return + + global pyModDir, progLanguages + + # Remove the menu entry for Linux systems + if sys.platform.startswith("linux"): + cleanUpLinuxSpecifics() + # Remove the Desktop and Start Menu entries for Windows systems + elif sys.platform.startswith(("win", "cygwin")): + cleanUpWindowsLinks() + + # Remove the wrapper scripts + rem_wnames = [ + "eric6_api", "eric6_compare", + "eric6_configure", "eric6_diff", + "eric6_doc", "eric6_qregularexpression", + "eric6_qregexp", "eric6_re", + "eric6_trpreviewer", "eric6_uipreviewer", + "eric6_unittest", "eric6", + "eric6_tray", "eric6_editor", + "eric6_plugininstall", "eric6_pluginuninstall", + "eric6_pluginrepository", "eric6_sqlbrowser", + "eric6_webbrowser", "eric6_iconeditor", + "eric6_snap", "eric6_hexeditor", "eric6_browser", + "eric6_shell", + ] + if includePythonVariant: + marker = PythonMarkers[sys.version_info.major] + rem_wnames = [n + marker for n in rem_wnames] + + try: + dirs = [platBinDir, getConfig('bindir')] + if platBinDirOld: + dirs.append(platBinDirOld) + for rem_wname in rem_wnames: + for d in dirs: + for rwname in wrapperNames(d, rem_wname): + if os.path.exists(rwname): + os.remove(rwname) + + # Cleanup our config file(s) + for name in ['eric6config.py', 'eric6config.pyc', 'eric6.pth']: + e6cfile = os.path.join(pyModDir, name) + if os.path.exists(e6cfile): + os.remove(e6cfile) + e6cfile = os.path.join(pyModDir, "__pycache__", name) + path, ext = os.path.splitext(e6cfile) + for f in glob.glob("{0}.*{1}".format(path, ext)): + os.remove(f) + + # Cleanup the install directories + for name in ['ericExamplesDir', 'ericDocDir', 'ericDTDDir', + 'ericCSSDir', 'ericIconDir', 'ericPixDir', + 'ericTemplatesDir', 'ericCodeTemplatesDir', + 'ericOthersDir', 'ericStylesDir', 'ericDir']: + if os.path.exists(getConfig(name)): + shutil.rmtree(getConfig(name), True) + + # Cleanup translations + for name in glob.glob( + os.path.join(getConfig('ericTranslationsDir'), 'eric6_*.qm')): + if os.path.exists(name): + os.remove(name) + + # Cleanup API files + try: + apidir = getConfig('apidir') + for progLanguage in progLanguages: + for name in getConfig('apis'): + apiname = os.path.join(apidir, progLanguage.lower(), name) + if os.path.exists(apiname): + os.remove(apiname) + for apiname in glob.glob( + os.path.join(apidir, progLanguage.lower(), "*.bas")): + if os.path.basename(apiname) != "eric6.bas": + os.remove(apiname) + except AttributeError: + pass + + if sys.platform == "darwin": + # delete the Mac app bundle + cleanUpMacAppBundle() + except (IOError, OSError) as msg: + sys.stderr.write( + 'Error: {0}\nTry install with admin rights.\n'.format(msg)) + exit(7) + + +def cleanUpLinuxSpecifics(): + """ + Clean up Linux specific files. + """ + if os.getuid() == 0: + for name in ["/usr/share/pixmaps/eric.png", + "/usr/share/pixmaps/ericWeb.png"]: + if os.path.exists(name): + os.remove(name) + if includePythonVariant: + marker = PythonMarkers[sys.version_info.major] + else: + marker = "" + for name in [ + "/usr/share/applications/eric6" + marker + ".desktop", + "/usr/share/appdata/eric6" + marker + ".appdata.xml", + "/usr/share/metainfo/eric6" + marker + ".appdata.xml", + "/usr/share/applications/eric6_webbrowser" + marker + + ".desktop", + "/usr/share/applications/eric6_browser" + marker + + ".desktop", + "/usr/share/pixmaps/eric" + marker + ".png", + "/usr/share/pixmaps/ericWeb" + marker + ".png", + ]: + if os.path.exists(name): + os.remove(name) + elif os.getuid() >= 1000: + # it is assumed that user ids start at 1000 + for name in ["~/.local/share/pixmaps/eric.png", + "~/.local/share/pixmaps/ericWeb.png"]: + path = os.path.expanduser(name) + if os.path.exists(path): + os.remove(path) + if includePythonVariant: + marker = PythonMarkers[sys.version_info.major] + else: + marker = "" + for name in [ + "~/.local/share/applications/eric6" + marker + ".desktop", + "~/.local/share/appdata/eric6" + marker + ".appdata.xml", + "~/.local/share/metainfo/eric6" + marker + ".appdata.xml", + "~/.local/share/applications/eric6_webbrowser" + marker + + ".desktop", + "~/.local/share/applications/eric6_browser" + marker + + ".desktop", + "~/.local/share/pixmaps/eric" + marker + ".png", + "~/.local/share/pixmaps/ericWeb" + marker + ".png", + ]: + path = os.path.expanduser(name) + if os.path.exists(path): + os.remove(path) + + +def cleanUpMacAppBundle(): + """ + Uninstall the macOS application bundle. + """ + from eric6config import getConfig + try: + macAppBundlePath = getConfig("macAppBundlePath") + macAppBundleName = getConfig("macAppBundleName") + except AttributeError: + macAppBundlePath = defaultMacAppBundlePath + macAppBundleName = defaultMacAppBundleName + for bundlePath in [os.path.join(defaultMacAppBundlePath, + macAppBundleName), + os.path.join(macAppBundlePath, + macAppBundleName), + ]: + if os.path.exists(bundlePath): + shutil.rmtree(bundlePath) + + +def cleanUpWindowsLinks(): + """ + Clean up the Desktop and Start Menu entries for Windows. + """ + global doCleanDesktopLinks, forceCleanDesktopLinks + + try: + from pywintypes import com_error # __IGNORE_WARNING__ + except ImportError: + # links were not created by install.py + return + + regPath = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer" + \ + "\\User Shell Folders" + + if doCleanDesktopLinks or forceCleanDesktopLinks: + # 1. cleanup desktop links + regName = "Desktop" + desktopEntry = getWinregEntry(regName, regPath) + if desktopEntry: + desktopFolder = os.path.normpath(os.path.expandvars(desktopEntry)) + for linkName in windowsDesktopNames(): + linkPath = os.path.join(desktopFolder, linkName) + if os.path.exists(linkPath): + try: + os.remove(linkPath) + except EnvironmentError: + # maybe restrictions prohibited link removal + print("Could not remove '{0}'.".format(linkPath)) + + # 2. cleanup start menu entry + regName = "Programs" + programsEntry = getWinregEntry(regName, regPath) + if programsEntry: + programsFolder = os.path.normpath(os.path.expandvars(programsEntry)) + eric6EntryPath = os.path.join(programsFolder, windowsProgramsEntry()) + if os.path.exists(eric6EntryPath): + try: + shutil.rmtree(eric6EntryPath) + except EnvironmentError: + # maybe restrictions prohibited link removal + print("Could not remove '{0}'.".format(eric6EntryPath)) + + +def shutilCopy(src, dst, perm=0o644): + """ + Wrapper function around shutil.copy() to ensure the permissions. + + @param src source file name (string) + @param dst destination file name or directory name (string) + @keyparam perm permissions to be set (integer) + """ + shutil.copy(src, dst) + if os.path.isdir(dst): + dst = os.path.join(dst, os.path.basename(src)) + os.chmod(dst, perm) + + +def installEric(): + """ + Actually perform the installation steps. + + @return result code (integer) + """ + global distDir, doCleanup, cfg, progLanguages, sourceDir, configName + global includePythonVariant, installApis + + # Create the platform specific wrappers. + scriptsDir = "install_scripts" + if not os.path.isdir(scriptsDir): + os.mkdir(scriptsDir) + wnames = [] + for name in ["eric6_api", "eric6_doc"]: + wnames.append(createPyWrapper(cfg['ericDir'], name, scriptsDir, False)) + for name in ["eric6_compare", "eric6_configure", "eric6_diff", + "eric6_editor", "eric6_hexeditor", "eric6_iconeditor", + "eric6_plugininstall", "eric6_pluginrepository", + "eric6_pluginuninstall", "eric6_qregexp", + "eric6_qregularexpression", "eric6_re", "eric6_snap", + "eric6_sqlbrowser", "eric6_tray", "eric6_trpreviewer", + "eric6_uipreviewer", "eric6_unittest", "eric6_webbrowser", + "eric6_browser", "eric6_shell", "eric6"]: + wnames.append(createPyWrapper(cfg['ericDir'], name, scriptsDir)) + + # set install prefix, if not None + if distDir: + for key in list(cfg.keys()): + cfg[key] = os.path.normpath(os.path.join(distDir, cfg[key])) + + try: + # Install the files + # make the install directories + for key in cfg.keys(): + if cfg[key] and not os.path.isdir(cfg[key]): + os.makedirs(cfg[key]) + + # copy the eric6 config file + if distDir: + shutilCopy(configName, cfg['mdir']) + if os.path.exists(configName + 'c'): + shutilCopy(configName + 'c', cfg['mdir']) + else: + shutilCopy(configName, modDir) + if os.path.exists(configName + 'c'): + shutilCopy(configName + 'c', modDir) + + # copy the various parts of eric6 + copyTree( + eric6SourceDir, cfg['ericDir'], + ['*.py', '*.pyc', '*.pyo', '*.pyw'], + [os.path.join(sourceDir, "Examples"), + os.path.join(sourceDir, ".ropeproject")], + excludePatterns=["eric6config.py*"]) + copyTree( + os.path.join(eric6SourceDir, "Plugins"), + os.path.join(cfg['ericDir'], "Plugins"), + ['*.svgz', '*.svg', '*.png', '*.style', '*.tmpl', '*.txt']) + copyTree( + os.path.join(eric6SourceDir, "Documentation"), + cfg['ericDocDir'], + ['*.html', '*.qch']) + copyTree( + os.path.join(eric6SourceDir, "CSSs"), + cfg['ericCSSDir'], + ['*.css']) + copyTree( + os.path.join(eric6SourceDir, "Styles"), + cfg['ericStylesDir'], + ['*.qss', '*.e4h', '*.e6h']) + copyTree( + os.path.join(eric6SourceDir, "i18n"), + cfg['ericTranslationsDir'], + ['*.qm']) + copyTree( + os.path.join(eric6SourceDir, "icons"), + cfg['ericIconDir'], + ['*.svgz', '*.svg', '*.png', 'LICENSE*.*', 'readme.txt']) + copyTree( + os.path.join(eric6SourceDir, "pixmaps"), + cfg['ericPixDir'], + ['*.svgz', '*.svg', '*.png', '*.xpm', '*.ico', '*.gif']) + copyTree( + os.path.join(eric6SourceDir, "DesignerTemplates"), + cfg['ericTemplatesDir'], + ['*.tmpl']) + copyTree( + os.path.join(eric6SourceDir, "CodeTemplates"), + cfg['ericCodeTemplatesDir'], + ['*.tmpl']) + + # copy the wrappers + for wname in wnames: + shutilCopy(wname, cfg['bindir'], perm=0o755) + os.remove(wname) + shutil.rmtree(scriptsDir) + + # copy the license file + shutilCopy(os.path.join(sourceDir, "docs", "LICENSE.GPL3"), + cfg['ericDir']) + + # create the global plugins directory + createGlobalPluginsDir() + + except (IOError, OSError) as msg: + sys.stderr.write( + 'Error: {0}\nTry install with admin rights.\n'.format(msg)) + return(7) + + # copy some text files to the doc area + for name in ["LICENSE.GPL3", "THANKS", "changelog"]: + try: + shutilCopy(os.path.join(sourceDir, "docs", name), + cfg['ericDocDir']) + except EnvironmentError: + print("Could not install '{0}'.".format( + os.path.join(sourceDir, "docs", name))) + for name in glob.glob(os.path.join(sourceDir, 'docs', 'README*.*')): + try: + shutilCopy(name, cfg['ericDocDir']) + except EnvironmentError: + print("Could not install '{0}'.".format(name)) + + # copy some more stuff + for name in ['default.e4k', 'default_Mac.e4k']: + try: + shutilCopy(os.path.join(sourceDir, "others", name), + cfg['ericOthersDir']) + except EnvironmentError: + print("Could not install '{2}{0}others{0}{1}'.".format( + os.path.join(sourceDir, "docs", name))) + + # install the API file + if installApis: + for progLanguage in progLanguages: + apidir = os.path.join(cfg['apidir'], progLanguage.lower()) + print("Installing {0} API files to '{1}'.".format( + progLanguage, apidir)) + if not os.path.exists(apidir): + os.makedirs(apidir) + for apiName in glob.glob(os.path.join(eric6SourceDir, "APIs", + progLanguage, "*.api")): + try: + shutilCopy(apiName, apidir) + except EnvironmentError: + print("Could not install '{0}' (no permission)." + .format(apiName)) + for apiName in glob.glob(os.path.join(eric6SourceDir, "APIs", + progLanguage, "*.bas")): + try: + shutilCopy(apiName, apidir) + except EnvironmentError: + print("Could not install '{0}' (no permission)." + .format(apiName)) + if progLanguage == "Python": + # copy Python3 API files to the same destination + for apiName in glob.glob(os.path.join(eric6SourceDir, "APIs", + "Python3", "*.api")): + try: + shutilCopy(apiName, apidir) + except EnvironmentError: + print("Could not install '{0}' (no permission)." + .format(apiName)) + for apiName in glob.glob(os.path.join(eric6SourceDir, "APIs", + "Python3", "*.bas")): + if os.path.exists(os.path.join( + apidir, os.path.basename( + apiName.replace(".bas", ".api")))): + try: + shutilCopy(apiName, apidir) + except EnvironmentError: + print("Could not install '{0}' (no permission)." + .format(apiName)) + + # Create menu entry for Linux systems + if sys.platform.startswith("linux"): + createLinuxSpecifics() + + # Create Desktop and Start Menu entries for Windows systems + elif sys.platform.startswith(("win", "cygwin")): + createWindowsLinks() + + # Create a Mac application bundle + elif sys.platform == "darwin": + createMacAppBundle(cfg['ericDir']) + + return 0 + + +def createLinuxSpecifics(): + """ + Install Linux specific files. + """ + global distDir, sourceDir, includePythonVariant + + if includePythonVariant: + marker = PythonMarkers[sys.version_info.major] + else: + marker = "" + + if distDir: + dst = os.path.normpath(os.path.join(distDir, "usr/share/pixmaps")) + if not os.path.exists(dst): + os.makedirs(dst) + shutilCopy( + os.path.join(eric6SourceDir, "icons", "default", "eric.png"), + os.path.join(dst, "eric" + marker + ".png")) + shutilCopy( + os.path.join(eric6SourceDir, "icons", "default", "ericWeb48.png"), + os.path.join(dst, "ericWeb" + marker + ".png")) + dst = os.path.normpath( + os.path.join(distDir, "usr/share/applications")) + if not os.path.exists(dst): + os.makedirs(dst) + copyDesktopFile(os.path.join(sourceDir, "linux", "eric6.desktop.in"), + os.path.join(dst, "eric6" + marker + ".desktop"), + marker) + copyDesktopFile( + os.path.join(sourceDir, "linux", "eric6_webbrowser.desktop.in"), + os.path.join(dst, "eric6_webbrowser" + marker + ".desktop"), + marker) + copyDesktopFile( + os.path.join(sourceDir, "linux", "eric6_browser.desktop.in"), + os.path.join(dst, "eric6_browser" + marker + ".desktop"), + marker) + dst = os.path.normpath( + os.path.join(distDir, "usr/share/metainfo")) + if not os.path.exists(dst): + os.makedirs(dst) + copyAppStreamFile( + os.path.join(sourceDir, "linux", "eric6.appdata.xml.in"), + os.path.join(dst, "eric6" + marker + ".appdata.xml"), + marker) + elif os.getuid() == 0: + shutilCopy(os.path.join( + eric6SourceDir, "icons", "default", "eric.png"), + "/usr/share/pixmaps/eric" + marker + ".png") + copyDesktopFile( + os.path.join(sourceDir, "linux", "eric6.desktop.in"), + "/usr/share/applications/eric6" + marker + ".desktop", + marker) + if os.path.exists("/usr/share/metainfo"): + copyAppStreamFile( + os.path.join(sourceDir, "linux", "eric6.appdata.xml.in"), + "/usr/share/metainfo/eric6" + marker + ".appdata.xml", + marker) + elif os.path.exists("/usr/share/appdata"): + copyAppStreamFile( + os.path.join(sourceDir, "linux", "eric6.appdata.xml.in"), + "/usr/share/appdata/eric6" + marker + ".appdata.xml", + marker) + shutilCopy(os.path.join( + eric6SourceDir, "icons", "default", "ericWeb48.png"), + "/usr/share/pixmaps/ericWeb" + marker + ".png") + copyDesktopFile( + os.path.join(sourceDir, "linux", "eric6_webbrowser.desktop.in"), + "/usr/share/applications/eric6_webbrowser" + marker + + ".desktop", + marker) + copyDesktopFile( + os.path.join(sourceDir, "linux", "eric6_browser.desktop.in"), + "/usr/share/applications/eric6_browser" + marker + + ".desktop", + marker) + elif os.getuid() >= 1000: + # it is assumed, that user ids start at 1000 + localPath = os.path.join(os.path.expanduser("~"), + ".local", "share") + # create directories first + for directory in [os.path.join(localPath, name) + for name in ("pixmaps", "applications", + "metainfo", "appdata")]: + if not os.path.isdir(directory): + os.makedirs(directory) + # now copy the files + shutilCopy( + os.path.join(eric6SourceDir, "icons", "default", "eric.png"), + os.path.join(localPath, "pixmaps", "eric" + marker + ".png")) + copyDesktopFile( + os.path.join(sourceDir, "linux", "eric6.desktop.in"), + os.path.join(localPath, "applications", + "eric6" + marker + ".desktop"), + marker) + copyAppStreamFile( + os.path.join(sourceDir, "linux", "eric6.appdata.xml.in"), + os.path.join(localPath, "metainfo", + "eric6" + marker + ".appdata.xml"), + marker) + copyAppStreamFile( + os.path.join(sourceDir, "linux", "eric6.appdata.xml.in"), + os.path.join(localPath, "appdata", + "eric6" + marker + ".appdata.xml"), + marker) + shutilCopy( + os.path.join(eric6SourceDir, "icons", "default", "ericWeb48.png"), + os.path.join(localPath, "pixmaps", + "ericWeb" + marker + ".png")) + copyDesktopFile( + os.path.join(sourceDir, "linux", "eric6_webbrowser.desktop.in"), + os.path.join(localPath, "applications", + "eric6_webbrowser" + marker + ".desktop"), + marker) + copyDesktopFile( + os.path.join(sourceDir, "linux", "eric6_browser.desktop.in"), + os.path.join(localPath, "applications", + "eric6_browser" + marker + ".desktop"), + marker) + + +def createWindowsLinks(): + """ + Create Desktop and Start Menu links. + """ + try: + # check, if pywin32 is available + from win32com.client import Dispatch # __IGNORE_WARNING__ + except ImportError: + installed = pipInstall( + "pywin32", + "\nThe Python package 'pywin32' could not be imported." + ) + if installed: + # create the links via an external script to get around some + # startup magic done by pywin32.pth + args = [ + sys.executable, + os.path.join(os.path.dirname(__file__), + "create_windows_links.py"), + ] + if includePythonVariant: + args.append("-y") + subprocess.call(args) + else: + print( + "\nThe Python package 'pywin32' is not installed. Desktop and" + " Start Menu entries will not be created." + ) + return + + regPath = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer" + \ + "\\User Shell Folders" + + # 1. create desktop shortcuts + regName = "Desktop" + desktopFolder = os.path.normpath( + os.path.expandvars(getWinregEntry(regName, regPath))) + for linkName, targetPath, iconPath in windowsDesktopEntries(): + linkPath = os.path.join(desktopFolder, linkName) + createWindowsShortcut(linkPath, targetPath, iconPath) + + # 2. create start menu entry and shortcuts + regName = "Programs" + programsEntry = getWinregEntry(regName, regPath) + if programsEntry: + programsFolder = os.path.normpath(os.path.expandvars(programsEntry)) + eric6EntryPath = os.path.join(programsFolder, windowsProgramsEntry()) + if not os.path.exists(eric6EntryPath): + try: + os.makedirs(eric6EntryPath) + except EnvironmentError: + # maybe restrictions prohibited link creation + return + + for linkName, targetPath, iconPath in windowsDesktopEntries(): + linkPath = os.path.join(eric6EntryPath, linkName) + createWindowsShortcut(linkPath, targetPath, iconPath) + + +def createMacAppBundle(pydir): + """ + Create a Mac application bundle. + + @param pydir the name of the directory where the Python script will + eventually be installed + @type str + """ + global cfg, macAppBundleName, macPythonExe, macAppBundlePath, pyqtVariant + + dirs = { + "contents": "{0}/{1}/Contents/".format( + macAppBundlePath, macAppBundleName), + "exe": "{0}/{1}/Contents/MacOS".format( + macAppBundlePath, macAppBundleName), + "icns": "{0}/{1}/Contents/Resources".format( + macAppBundlePath, macAppBundleName) + } + os.makedirs(dirs["contents"]) + os.makedirs(dirs["exe"]) + os.makedirs(dirs["icns"]) + + if macPythonExe == defaultMacPythonExe: + starter = os.path.join(dirs["exe"], "eric") + os.symlink(macPythonExe, starter) + else: + starter = "python{0}".format(sys.version_info.major) + + wname = os.path.join(dirs["exe"], "eric6") + + # determine entry for DYLD_FRAMEWORK_PATH + dyldLine = "" + try: + if pyqtVariant == "PyQt4": + from PyQt4.QtCore import QLibraryInfo + else: + from PyQt5.QtCore import QLibraryInfo + qtLibraryDir = QLibraryInfo.location(QLibraryInfo.LibrariesPath) + except ImportError: + qtLibraryDir = "" + if qtLibraryDir: + dyldLine = "DYLD_FRAMEWORK_PATH={0}\n".format(qtLibraryDir) + + # determine entry for PATH + pathLine = "" + path = os.getenv("PATH", "") + if path: + pybin = os.path.join(sys.exec_prefix, "bin") + pathlist = path.split(os.pathsep) + pathlist_n = [pybin] + for path_ in pathlist: + if path_ and path_ not in pathlist_n: + pathlist_n.append(path_) + pathLine = "PATH={0}\n".format(os.pathsep.join(pathlist_n)) + + # create the wrapper script + wrapper = ('''#!/bin/sh\n''' + '''\n''' + '''{0}''' + '''{1}''' + '''exec "{2}" "{3}/{4}.py" "$@"\n''' + .format(pathLine, dyldLine, starter, pydir, "eric6")) + copyToFile(wname, wrapper) + os.chmod(wname, 0o755) + + shutilCopy(os.path.join(eric6SourceDir, "pixmaps", "eric_2.icns"), + os.path.join(dirs["icns"], "eric.icns")) + + if os.path.exists(os.path.join("eric", "eric6", "UI", "Info.py")): + # Installing from archive + from eric.eric6.UI.Info import Version, CopyrightShort + elif os.path.exists(os.path.join("eric6", "UI", "Info.py")): + # Installing from source tree + from eric6.UI.Info import Version, CopyrightShort + else: + Version = "Unknown" + CopyrightShort = "(c) 2002 - 2019 Detlev Offenbach" + + copyToFile( + os.path.join(dirs["contents"], "Info.plist"), + '''<?xml version="1.0" encoding="UTF-8"?>\n''' + '''<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"\n''' + ''' "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n''' + '''<plist version="1.0">\n''' + '''<dict>\n''' + ''' <key>CFBundleExecutable</key>\n''' + ''' <string>eric6</string>\n''' + ''' <key>CFBundleIconFile</key>\n''' + ''' <string>eric.icns</string>\n''' + ''' <key>CFBundleInfoDictionaryVersion</key>\n''' + ''' <string>{1}</string>\n''' + ''' <key>CFBundleName</key>\n''' + ''' <string>{0}</string>\n''' + ''' <key>CFBundleDisplayName</key>\n''' + ''' <string>{0}</string>\n''' + ''' <key>CFBundlePackageType</key>\n''' + ''' <string>APPL</string>\n''' + ''' <key>CFBundleSignature</key>\n''' + ''' <string>ERIC-IDE</string>\n''' + ''' <key>CFBundleVersion</key>\n''' + ''' <string>{1}</string>\n''' + ''' <key>CFBundleGetInfoString</key>\n''' + ''' <string>{1}, {2}</string>\n''' + ''' <key>CFBundleIdentifier</key>\n''' + ''' <string>org.python-projects.eric-ide</string>\n''' + '''</dict>\n''' + '''</plist>\n'''.format( + macAppBundleName.replace(".app", ""), + Version.split(None, 1)[0], + CopyrightShort)) + + +def createInstallConfig(): + """ + Create the installation config dictionary. + """ + global modDir, platBinDir, cfg, apisDir, installApis + + ericdir = os.path.join(modDir, "eric6") + cfg = { + 'ericDir': ericdir, + 'ericPixDir': os.path.join(ericdir, "pixmaps"), + 'ericIconDir': os.path.join(ericdir, "icons"), + 'ericDTDDir': os.path.join(ericdir, "DTDs"), + 'ericCSSDir': os.path.join(ericdir, "CSSs"), + 'ericStylesDir': os.path.join(ericdir, "Styles"), + 'ericDocDir': os.path.join(ericdir, "Documentation"), + 'ericExamplesDir': os.path.join(ericdir, "Examples"), + 'ericTranslationsDir': os.path.join(ericdir, "i18n"), + 'ericTemplatesDir': os.path.join(ericdir, "DesignerTemplates"), + 'ericCodeTemplatesDir': os.path.join(ericdir, 'CodeTemplates'), + 'ericOthersDir': ericdir, + 'bindir': platBinDir, + 'mdir': modDir, + } + if installApis: + if apisDir: + cfg['apidir'] = apisDir + else: + cfg['apidir'] = os.path.join(ericdir, "api") + else: + cfg['apidir'] = "" +configLength = 15 + + +def createConfig(): + """ + Create a config file with the respective config entries. + """ + global cfg, macAppBundlePath, configName + + apis = [] + if installApis: + for progLanguage in progLanguages: + for apiName in sorted(glob.glob( + os.path.join(eric6SourceDir, "APIs", progLanguage, "*.api"))): + apis.append(os.path.basename(apiName)) + if progLanguage == "Python": + # treat Python3 API files the same as Python API files + for apiName in sorted(glob.glob( + os.path.join(eric6SourceDir, "APIs", "Python3", "*.api"))): + apis.append(os.path.basename(apiName)) + + config = ( + """# -*- coding: utf-8 -*-\n""" + """#\n""" + """# This module contains the configuration of the individual eric6""" + """ installation\n""" + """#\n""" + """\n""" + """_pkg_config = {{\n""" + """ 'ericDir': r'{0}',\n""" + """ 'ericPixDir': r'{1}',\n""" + """ 'ericIconDir': r'{2}',\n""" + """ 'ericDTDDir': r'{3}',\n""" + """ 'ericCSSDir': r'{4}',\n""" + """ 'ericStylesDir': r'{5}',\n""" + """ 'ericDocDir': r'{6}',\n""" + """ 'ericExamplesDir': r'{7}',\n""" + """ 'ericTranslationsDir': r'{8}',\n""" + """ 'ericTemplatesDir': r'{9}',\n""" + """ 'ericCodeTemplatesDir': r'{10}',\n""" + """ 'ericOthersDir': r'{11}',\n""" + """ 'bindir': r'{12}',\n""" + """ 'mdir': r'{13}',\n""" + """ 'apidir': r'{14}',\n""" + """ 'apis': {15},\n""" + """ 'macAppBundlePath': r'{16}',\n""" + """ 'macAppBundleName': r'{17}',\n""" + """}}\n""" + """\n""" + """def getConfig(name):\n""" + """ '''\n""" + """ Module function to get a configuration value.\n""" + """\n""" + """ @param name name of the configuration value""" + """ @type str\n""" + """ @exception AttributeError raised to indicate an invalid""" + """ config entry\n""" + """ '''\n""" + """ try:\n""" + """ return _pkg_config[name]\n""" + """ except KeyError:\n""" + """ pass\n""" + """\n""" + """ raise AttributeError(\n""" + """ '"{{0}}" is not a valid configuration value'""" + """.format(name))\n""" + ).format( + cfg['ericDir'], cfg['ericPixDir'], cfg['ericIconDir'], + cfg['ericDTDDir'], cfg['ericCSSDir'], + cfg['ericStylesDir'], cfg['ericDocDir'], + cfg['ericExamplesDir'], cfg['ericTranslationsDir'], + cfg['ericTemplatesDir'], + cfg['ericCodeTemplatesDir'], cfg['ericOthersDir'], + cfg['bindir'], cfg['mdir'], + cfg['apidir'], sorted(apis), + macAppBundlePath, macAppBundleName, + ) + copyToFile(configName, config) + + +def pipInstall(packageName, message): + """ + Install the given package via pip. + + @param packageName name of the package to be installed + @type str + @param message message to be shown to the user + @type str + @return flag indicating a successful installation + @rtype bool + """ + ok = False + print("{0}\n\nShall '{1}' be installed using pip? (Y/n)" + .format(message, packageName), end=" ") + if sys.version_info[0] == 2: + answer = raw_input() # __IGNORE_WARNING__ + else: + answer = input() + if answer in ("", "Y", "y"): + exitCode = subprocess.call( + [sys.executable, "-m", "pip", "install", packageName]) + ok = (exitCode == 0) + + return ok + + +def doDependancyChecks(): + """ + Perform some dependency checks. + """ + print('Checking dependencies') + + # perform dependency checks + print("Python Version: {0:d}.{1:d}.{2:d}".format(*sys.version_info[:3])) + if sys.version_info < (2, 7, 10): + print('Sorry, you must have Python 2.7.10 or higher or ' + 'Python 3.5.0 or higher.') + exit(5) + elif sys.version_info < (3, 5, 0) and sys.version_info[0] == 3: + print('Sorry, you must have Python 3.5.0 or higher.') + exit(5) + if sys.version_info[0] > 3: + print('Sorry, eric6 requires Python 3 or Python 2 for running.') + exit(5) + + try: + import xml.etree # __IGNORE_WARNING__ + except ImportError: + print('Your Python installation is missing the XML module.') + print('Please install it and try again.') + exit(5) + + if pyqtVariant == "PyQt4": + try: + from PyQt4.QtCore import qVersion + except ImportError as msg: + print('Sorry, please install PyQt4.') + print('Error: {0}'.format(msg)) + exit(1) + print("Found PyQt4") + else: + try: + from PyQt5.QtCore import qVersion + except ImportError as msg: + if sys.version_info[0] == 2: + # no PyQt5 wheels available for Python 2 + installed = False + else: + installed = pipInstall( + "PyQt5", + "PyQt5 could not be detected.\nError: {0}".format(msg) + ) + if installed: + # try to import it again + try: + from PyQt5.QtCore import qVersion + except ImportError as msg: + print('Sorry, please install PyQt5.') + print('Error: {0}'.format(msg)) + exit(1) + else: + print('Sorry, please install PyQt5.') + print('Error: {0}'.format(msg)) + exit(1) + print("Found PyQt5") + + try: + if pyqtVariant == "PyQt4": + pyuic = "pyuic4" + from PyQt4 import uic # __IGNORE_WARNING__ + else: + pyuic = "pyuic5" + from PyQt5 import uic # __IGNORE_WARNING__ + except ImportError as msg: + print("Sorry, {0} is not installed.".format(pyuic)) + print('Error: {0}'.format(msg)) + exit(1) + print("Found {0}".format(pyuic)) + + if pyqtVariant != "PyQt4": + try: + from PyQt5 import QtWebEngineWidgets # __IGNORE_WARNING__ + except ImportError as msg: + from PyQt5.QtCore import PYQT_VERSION + if PYQT_VERSION >= 0x50c00: + # PyQt 5.12 separated QtWebEngine into a separate wheel + installed = pipInstall( + "PyQtWebEngine", + "PyQtWebEngine could not be detected.\nError: {0}" + .format(msg) + ) + + try: + if pyqtVariant == "PyQt4": + from PyQt4 import Qsci # __IGNORE_WARNING__ + else: + from PyQt5 import Qsci # __IGNORE_WARNING__ + except ImportError as msg: + if pyqtVariant == "PyQt4": + message = str(msg) + else: + installed = pipInstall( + "QScintilla", + "QScintilla could not be detected.\nError: {0}".format(msg) + ) + if installed: + # try to import it again + try: + from PyQt5 import Qsci # __IGNORE_WARNING__ + message = None + except ImportError as msg: + message = str(msg) + else: + message = "QScintilla could not be installed." + if message: + print("Sorry, please install QScintilla2 and") + print("its PyQt5/PyQt4 wrapper.") + print('Error: {0}'.format(message)) + exit(1) + print("Found QScintilla2") + + if pyqtVariant == "PyQt4": + impModulesList = [ + "PyQt4.QtGui", "PyQt4.QtNetwork", "PyQt4.QtSql", + "PyQt4.QtSvg", "PyQt4.QtWebKit", + ] + altModulesList = [] + else: + impModulesList = [ + "PyQt5.QtGui", "PyQt5.QtNetwork", "PyQt5.QtPrintSupport", + "PyQt5.QtSql", "PyQt5.QtSvg", "PyQt5.QtWidgets", + ] + altModulesList = [ + # Tuple with alternatives, flag indicating it's ok to not be + # available (e.g. for 32-Bit Windows) + (("PyQt5.QtWebEngineWidgets", "PyQt5.QtWebKitWidgets"), + sys.maxsize <= 2**32), + ] + # check mandatory modules + modulesOK = True + for impModule in impModulesList: + name = impModule.split(".")[1] + try: + __import__(impModule) + print("Found", name) + except ImportError as msg: + print('Sorry, please install {0}.'.format(name)) + print('Error: {0}'.format(msg)) + modulesOK = False + if not modulesOK: + exit(1) + # check mandatory modules with alternatives + if altModulesList: + altModulesOK = True + for altModules, forcedOk in altModulesList: + modulesOK = False + for altModule in altModules: + name = altModule.split(".")[1] + try: + __import__(altModule) + print("Found", name) + modulesOK = True + break + except ImportError: + pass + if not modulesOK and not forcedOk: + altModulesOK = False + print('Sorry, please install {0}.' + .format(" or ".join(altModules))) + if not altModulesOK: + exit(1) + + # determine the platform dependent black list + if sys.platform.startswith(("win", "cygwin")): + PlatformBlackLists = PlatformsBlackLists["windows"] + elif sys.platform.startswith("linux"): + PlatformBlackLists = PlatformsBlackLists["linux"] + else: + PlatformBlackLists = PlatformsBlackLists["mac"] + + # check version of Qt + qtMajor = int(qVersion().split('.')[0]) + qtMinor = int(qVersion().split('.')[1]) + print("Qt Version: {0}".format(qVersion().strip())) + if qtMajor < 4 or \ + (qtMajor == 4 and qtMinor < 8) or \ + (qtMajor == 5 and qtMinor < 9): + print('Sorry, you must have Qt version 4.8.0 or better or') + print('5.9.0 or better.') + exit(2) + + # check version of sip + try: + try: + from PyQt5 import sip + except ImportError: + import sip + sipVersion = sip.SIP_VERSION_STR + print("sip Version:", sipVersion.strip()) + # always assume, that snapshots or dev versions are new enough + if "snapshot" not in sipVersion and "dev" not in sipVersion: + while sipVersion.count('.') < 2: + sipVersion += '.0' + (major, minor, pat) = sipVersion.split('.') + major = int(major) + minor = int(minor) + pat = int(pat) + if major < 4 or (major == 4 and minor < 14) or \ + (major == 4 and minor == 14 and pat < 2): + print('Sorry, you must have sip 4.14.2 or higher or' + ' a recent snapshot release.') + exit(3) + # check for blacklisted versions + for vers in BlackLists["sip"] + PlatformBlackLists["sip"]: + if vers == sipVersion: + print( + 'Sorry, sip version {0} is not compatible with eric6.' + .format(vers)) + print('Please install another version.') + exit(3) + except (ImportError, AttributeError): + pass + + # check version of PyQt + if pyqtVariant == "PyQt4": + from PyQt4.QtCore import PYQT_VERSION_STR + else: + from PyQt5.QtCore import PYQT_VERSION_STR + pyqtVersion = PYQT_VERSION_STR + print("PyQt Version:", pyqtVersion.strip()) + # always assume, that snapshots or dev versions are new enough + if "snapshot" not in pyqtVersion and "dev" not in pyqtVersion: + while pyqtVersion.count('.') < 2: + pyqtVersion += '.0' + (major, minor, pat) = pyqtVersion.split('.') + major = int(major) + minor = int(minor) + pat = int(pat) + if major < 4 or \ + (major == 4 and minor < 10) or \ + (major == 5 and minor < 9): + print('Sorry, you must have PyQt 4.10.0 or better or') + print('PyQt 5.9.0 or better or' + ' a recent snapshot release.') + exit(4) + # check for blacklisted versions + for vers in BlackLists[pyqtVariant] + PlatformBlackLists[pyqtVariant]: + if vers == pyqtVersion: + print('Sorry, PyQt version {0} is not compatible with eric6.' + .format(vers)) + print('Please install another version.') + exit(4) + + # check version of QScintilla + if pyqtVariant == "PyQt4": + from PyQt4.Qsci import QSCINTILLA_VERSION_STR + else: + from PyQt5.Qsci import QSCINTILLA_VERSION_STR + scintillaVersion = QSCINTILLA_VERSION_STR + print("QScintilla Version:", QSCINTILLA_VERSION_STR.strip()) + # always assume, that snapshots or dev versions are new enough + if "snapshot" not in scintillaVersion and "dev" not in scintillaVersion: + while scintillaVersion.count('.') < 2: + scintillaVersion += '.0' + (major, minor, pat) = scintillaVersion.split('.') + major = int(major) + minor = int(minor) + pat = int(pat) + if major < 2 or (major == 2 and minor < 10): + print('Sorry, you must have QScintilla 2.10.0 or higher or' + ' a recent snapshot release.') + exit(5) + # check for blacklisted versions + for vers in BlackLists["QScintilla2"] + \ + PlatformBlackLists["QScintilla2"]: + if vers == scintillaVersion: + print( + 'Sorry, QScintilla2 version {0} is not compatible with' + ' eric6.'.format(vers)) + print('Please install another version.') + exit(5) + + print("All dependencies ok.") + print() + + +def __pyName(py_dir, py_file): + """ + Local function to create the Python source file name for the compiled + .ui file. + + @param py_dir suggested name of the directory (string) + @param py_file suggested name for the compile source file (string) + @return tuple of directory name (string) and source file name (string) + """ + return py_dir, "Ui_{0}".format(py_file) + + +def compileUiFiles(): + """ + Compile the .ui files to Python sources. + """ + if pyqtVariant == "PyQt4": + from PyQt4.uic import compileUiDir + else: + from PyQt5.uic import compileUiDir + compileUiDir(eric6SourceDir, True, __pyName) + + +def prepareInfoFile(fileName): + """ + Function to prepare an Info.py file when installing from source. + + @param fileName name of the Python file containing the info (string) + """ + if not fileName: + return + + try: + os.rename(fileName, fileName + ".orig") + except EnvironmentError: + pass + try: + hgOut = subprocess.check_output(["hg", "identify", "-i"]) + if sys.version_info[0] == 3: + hgOut = hgOut.decode() + except (OSError, subprocess.CalledProcessError): + hgOut = "" + if hgOut: + hgOut = hgOut.strip() + if hgOut.endswith("+"): + hgOut = hgOut[:-1] + if sys.version_info[0] == 2: + f = codecs.open(fileName + ".orig", "r", "utf-8") + else: + f = open(fileName + ".orig", "r", encoding="utf-8") + text = f.read() + f.close() + text = text.replace("@@REVISION@@", hgOut)\ + .replace("@@VERSION@@", "rev_" + hgOut) + copyToFile(fileName, text) + else: + shutil.copy(fileName + ".orig", fileName) + + +def getWinregEntry(name, path): + """ + Function to get an entry from the Windows Registry. + + @param name variable name + @type str + @param path registry path of the variable + @type str + @return value of requested registry variable + @rtype any + """ + try: + import _winreg as winreg + except ImportError: + try: + import winreg + except ImportError: + return None + + try: + registryKey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0, + winreg.KEY_READ) + value, _ = winreg.QueryValueEx(registryKey, name) + winreg.CloseKey(registryKey) + return value + except WindowsError: + return None + + +def createWindowsShortcut(linkPath, targetPath, iconPath): + """ + Create Windows shortcut. + + @param linkPath path of the shortcut file + @type str + @param targetPath path the shortcut shall point to + @type str + @param iconPath path of the icon file + @type str + """ + from win32com.client import Dispatch + from pywintypes import com_error + + try: + shell = Dispatch('WScript.Shell') + shortcut = shell.CreateShortCut(linkPath) + shortcut.Targetpath = targetPath + shortcut.WorkingDirectory = os.path.dirname(targetPath) + shortcut.IconLocation = iconPath + shortcut.save() + except com_error: + # maybe restrictions prohibited link creation + pass + + +def windowsDesktopNames(): + """ + Function to generate the link names for the Windows Desktop. + + @return list of desktop link names + @rtype list of str + """ + return [e[0] for e in windowsDesktopEntries()] + + +def windowsDesktopEntries(): + """ + Function to generate data for the Windows Desktop links. + + @return list of tuples containing the desktop link name, + the link target and the icon target + @rtype list of tuples of (str, str, str) + """ + global cfg, includePythonVariant + + if includePythonVariant: + marker = PythonMarkers[sys.version_info.major] + else: + marker = "" + + majorVersion, minorVersion = sys.version_info[:2] + entriesTemplates = [ + ("eric6 (Python {0}.{1}).lnk", + os.path.join(cfg["bindir"], "eric6" + marker + ".cmd"), + os.path.join(cfg["ericPixDir"], "eric6.ico")), + ] + if sys.version_info.major == 2: + entriesTemplates.append(( + "eric6 Browser (Python {0}.{1}).lnk", + os.path.join(cfg["bindir"], "eric6_webbrowser" + marker + ".cmd"), + os.path.join(cfg["ericPixDir"], "ericWeb48.ico") + )) + else: + entriesTemplates.append(( + "eric6 Browser (Python {0}.{1}).lnk", + os.path.join(cfg["bindir"], "eric6_browser" + marker + ".cmd"), + os.path.join(cfg["ericPixDir"], "ericWeb48.ico") + )) + + return [ + (e[0].format(majorVersion, minorVersion), e[1], e[2]) + for e in entriesTemplates + ] + + +def windowsProgramsEntry(): + """ + Function to generate the name of the Start Menu top entry. + + @return name of the Start Menu top entry + @rtype str + """ + majorVersion, minorVersion = sys.version_info[:2] + return "eric6 (Python {0}.{1})".format(majorVersion, minorVersion) + + +def main(argv): + """ + The main function of the script. + + @param argv list of command line arguments + @type list of str + """ + import getopt + + # Parse the command line. + global progName, modDir, doCleanup, doCompile, distDir, cfg, apisDir + global sourceDir, eric6SourceDir, configName, includePythonVariant + global macAppBundlePath, macAppBundleName, macPythonExe + global pyqtVariant, pyqtOverride, installApis, doCleanDesktopLinks + + if sys.version_info < (2, 7, 0) or sys.version_info > (3, 9, 9): + print('Sorry, eric6 requires at least Python 2.7 or ' + 'Python 3 for running.') + exit(5) + + progName = os.path.basename(argv[0]) + + if os.path.dirname(argv[0]): + os.chdir(os.path.dirname(argv[0])) + + determinePyQtVariant() + initGlobals() + + try: + if sys.platform.startswith(("win", "cygwin")): + optlist, args = getopt.getopt( + argv[1:], "chxyza:b:d:f:", + ["help", "pyqt=", "no-apis"]) + elif sys.platform == "darwin": + optlist, args = getopt.getopt( + argv[1:], "chxyza:b:d:f:i:m:n:p:", + ["help", "pyqt=", "no-apis"]) + else: + optlist, args = getopt.getopt( + argv[1:], "chxyza:b:d:f:i:", + ["help", "pyqt=", "no-apis"]) + except getopt.GetoptError as err: + print(err) + usage() + + global platBinDir + + depChecks = True + + for opt, arg in optlist: + if opt in ["-h", "--help"]: + usage(0) + elif opt == "-a": + apisDir = arg + elif opt == "-b": + platBinDir = arg + elif opt == "-d": + modDir = arg + elif opt == "-i": + distDir = os.path.normpath(arg) + elif opt == "-x": + depChecks = False + elif opt == "-c": + doCleanup = False + elif opt == "-z": + doCompile = False + elif opt == "-y": + includePythonVariant = True + elif opt == "-f": + try: + exec(compile(open(arg).read(), arg, 'exec'), globals()) + if len(cfg) != configLength: + print("The configuration dictionary in '{0}' is incorrect." + " Aborting".format(arg)) + exit(6) + except Exception: + cfg = {} + elif opt == "-m": + macAppBundleName = arg + elif opt == "-n": + macAppBundlePath = arg + elif opt == "-p": + macPythonExe = arg + elif opt == "--pyqt": + if arg not in ["4", "5"]: + print("Invalid PyQt version given; should be 4 or 5. Aborting") + exit(6) + pyqtVariant = "PyQt{0}".format(arg) + pyqtOverride = True + elif opt == "--no-apis": + installApis = False + elif opt == "--clean-desktop": + doCleanDesktopLinks = True + + infoName = "" + installFromSource = not os.path.isdir(sourceDir) + + # check dependencies + if depChecks: + doDependancyChecks() + + # cleanup source if installing from source + if installFromSource: + print("Cleaning up source ...") + sourceDir = os.path.abspath("..") + eric6SourceDir = os.path.join(sourceDir, "eric6") + cleanupSource(eric6SourceDir) + print() + + if installFromSource: + sourceDir = os.path.abspath("..") + eric6SourceDir = os.path.join(sourceDir, "eric6") + configName = os.path.join(eric6SourceDir, "eric6config.py") + if os.path.exists(os.path.join(sourceDir, ".hg")): + # we are installing from source with repo + infoName = os.path.join(eric6SourceDir, "UI", "Info.py") + prepareInfoFile(infoName) + + if len(cfg) == 0: + createInstallConfig() + + # get rid of development config file, if it exists + try: + if installFromSource: + os.rename(configName, configName + ".orig") + configNameC = configName + 'c' + if os.path.exists(configNameC): + os.remove(configNameC) + os.remove(configName) + except EnvironmentError: + pass + + # cleanup old installation + print("Cleaning up old installation ...") + try: + if doCleanup: + if distDir: + shutil.rmtree(distDir, True) + else: + cleanUp() + except (IOError, OSError) as msg: + sys.stderr.write('Error: {0}\nTry install as root.\n'.format(msg)) + exit(7) + + # Create a config file and delete the default one + print("\nCreating configuration file ...") + createConfig() + + # Compile .ui files + print("\nCompiling user interface files ...") + # step 1: remove old Ui_*.py files + for root, _, files in os.walk(sourceDir): + for file in [f for f in files if fnmatch.fnmatch(f, 'Ui_*.py')]: + os.remove(os.path.join(root, file)) + # step 2: compile the forms + compileUiFiles() + + if doCompile: + print("\nCompiling source files ...") + # Hide compile errors (mainly because of Py2/Py3 differences) + skipRe = re.compile(r"DebugClients[\\/]Python[\\/]") + sys.stdout = io.StringIO() + if distDir: + compileall.compile_dir( + eric6SourceDir, + ddir=os.path.join(distDir, modDir, cfg['ericDir']), + rx=skipRe, + quiet=True) + py_compile.compile( + configName, + dfile=os.path.join(distDir, modDir, "eric6config.py")) + else: + compileall.compile_dir( + eric6SourceDir, + ddir=os.path.join(modDir, cfg['ericDir']), + rx=skipRe, + quiet=True) + py_compile.compile(configName, + dfile=os.path.join(modDir, "eric6config.py")) + sys.stdout = sys.__stdout__ + print("\nInstalling eric6 ...") + res = installEric() + + # do some cleanup + try: + if installFromSource: + os.remove(configName) + configNameC = configName + 'c' + if os.path.exists(configNameC): + os.remove(configNameC) + os.rename(configName + ".orig", configName) + except EnvironmentError: + pass + try: + if installFromSource and infoName: + os.remove(infoName) + infoNameC = infoName + 'c' + if os.path.exists(infoNameC): + os.remove(infoNameC) + os.rename(infoName + ".orig", infoName) + except EnvironmentError: + pass + + print("\nInstallation complete.") + print() + + exit(res) + + +if __name__ == "__main__": + try: + main(sys.argv) + except SystemExit: + raise + except Exception: + print("""An internal error occured. Please report all the output""" + """ of the program,\nincluding the following traceback, to""" + """ eric-bugs@eric-ide.python-projects.org.\n""") + raise + +# +# eflag: noqa = M801