scripts/install.py

changeset 6942
2602857055c5
parent 6939
af0ca76e26fd
child 6956
dfd3b53be436
--- /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

eric ide

mercurial