VirtualBox

Changeset 103028 in vbox


Ignore:
Timestamp:
Jan 24, 2024 3:53:59 PM (10 months ago)
Author:
vboxsync
Message:

Main/Python: Big revamp to modernize our vboxapi Python package to now use a so-called src-layout, which also can be used with pip directly. Also added compatibility w/ Python 3.12 where distutils are not shipped anymore. We also now do have automatic linting for our code, which hopefully should improve quality in this area. Moved some Python-related files into an own sub folder so that it's more clear to which these belong to. The "sdk/installer" directories also have an own "python" sub directory where the stuff resides now. The Windows installer also uses "sdk/installer" instead of "sdk/install", to match the other platforms. bugref:10579

Location:
trunk/src/VBox
Files:
4 added
16 edited
2 copied
3 moved

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Installer/Config.kmk

    r102342 r103028  
    5656        sdk/bindings/xpcom/python/xpcom/server/policy.py
    5757 VBOXINST_SDK_INSTALLER_PYTHON_FILES = \
    58         sdk/installer/vboxapisetup.py \
    59         sdk/installer/vboxapi/__init__.py \
    60         sdk/installer/vboxapi/VirtualBox_constants.py
     58        sdk/installer/python/vboxapisetup.py \
     59        sdk/installer/python/vboxapi/__init__.py \
     60        sdk/installer/python/vboxapi/VirtualBox_constants.py
    6161endif
    6262
  • trunk/src/VBox/Installer/common/Config.kmk

    r102983 r103028  
    11# $Id$
    22## @file
    3 # kBuild Configuration file for VBoxShell.
     3# kBuild Configuration file for common installer parts.
    44#
    55
     
    2626#
    2727
    28 VBOX_VBOXSHELL_CONFIG_KMK_INCLUDED = 1
     28VBOX_INSTALLER_COMMON_CONFIG_KMK_INCLUDED = 1
    2929
    3030# Include the top-level configure file.
     
    3636# Globals
    3737#
    38 VBOX_PATH_VBOXSHELL_SRC := $(PATH_ROOT)/src/VBox/Frontends/VBoxShell
     38VBOX_PATH_INSTALLER_COMMON_SRC := $(PATH_ROOT)/src/VBox/Installer/common
    3939
    4040#
    4141# List of python sources that should be linted.
    4242#
    43 VBOX_VBOXSHELL_PYTHON_SOURCES    :=
    44 VBOX_VBOXSHELL_PYLINT_TARGETS    :=
     43VBOX_INSTALLER_COMMON_PYTHON_SOURCES :=
     44VBOX_INSTALLER_COMMON_PYLINT_TARGETS :=
    4545
    4646ifdef VBOX_WITH_PYLINT
     
    5151# Process python sources.
    5252#
    53 if1of ($(KBUILD_TARGET), win os2)
    54  VBOX_PYTHONPATH_VBOXSHELL = $(PYTHONPATH);$(VBOX_PATH_VBOXSHELL_SRC)
    55 else
    56  VBOX_PYTHONPATH_VBOXSHELL = $(PYTHONPATH):$(VBOX_PATH_VBOXSHELL_SRC)
    57 endif
     53VBOX_PYTHONPATH_INSTALLER_COMMON = $(PYTHONPATH):$(VBOX_PATH_INSTALLER_COMMON_SRC)
    5854BLDDIRS += $(PATH_TARGET)/pylint
    5955
    60 define def_vbox_vboxshell_py_check
     56define def_vbox_installer_common_py_check
    6157 $(eval name:=$(basename $(notdir $(py))))
    6258
     
    6662 ifdef VBOX_WITH_PYLINT
    6763        $(QUIET2)$(call MSG_L1,Subjecting $(py) to pylint...)
    68         $(QUIET)$(REDIRECT) -C "$(dir $(py))" -E LC_ALL=C -E PYTHONPATH="$(VBOX_PYTHONPATH_VBOXSHELL)" -- \
    69                 $(VBOX_PYLINT) --rcfile=$(VBOX_PATH_VBOXSHELL_SRC)/pylintrc $$(VBOX_PYLINT_FLAGS) $$($(py)_VBOX_PYLINT_FLAGS) ./$(notdir $(py))
     64        $(QUIET)$(REDIRECT) -C "$(dir $(py))" -E LC_ALL=C -E PYTHONPATH="$(VBOX_PYTHONPATH_INSTALLER_COMMON)" -- \
     65                $(VBOX_PYLINT) --rcfile=$(VBOX_PATH_INSTALLER_COMMON_SRC)/pylintrc $$(VBOX_PYLINT_FLAGS) $$($(py)_VBOX_PYLINT_FLAGS) ./$(notdir $(py))
    7066 endif
    7167        $(QUIET)$(APPEND) -t "$(PATH_TARGET)/pylint/$(name).o"
    7268 TESTING += $(name)-py-phony.o
    73  VBOX_VBOXSHELL_PYLINT_TARGETS    += $(PATH_TARGET)/pylint/$(name).o
    74 endef # def_vbox_vboxshell_py_check
     69 VBOX_INSTALLER_COMMON_PYLINT_TARGETS    += $(PATH_TARGET)/pylint/$(name).o
     70endef # def_vbox_installer_common_py_check
    7571
    7672
    77 define def_vbox_vboxshell_process_python_sources
     73define def_vbox_installer_common_process_python_sources
    7874 if $(words $(_SUB_MAKEFILE_STACK)) <= 0 || "$1" == "FORCE"
    79   $(foreach py, $(VBOX_VBOXSHELL_PYTHON_SOURCES), $(eval $(def_vbox_vboxshell_py_check)))
     75  $(foreach py, $(VBOX_INSTALLER_COMMON_PYTHON_SOURCES), $(eval $(def_vbox_installer_common_py_check)))
    8076 endif
    8177endef
  • trunk/src/VBox/Installer/common/Makefile.kmk

    r98429 r103028  
    3737ifdef VBOX_WITH_PYTHON
    3838
    39  INSTALLS += VBox-python-glue-installer
     39 INSTALLS += VBox-python-glue-vboxapisetup
    4040
    41  VBox-python-glue-installer_INST = $(INST_SDK)installer/
    42  VBox-python-glue-installer_MODE = a+r,u+w
    43  VBox-python-glue-installer_SOURCES = vboxapisetup.py
     41 VBox-python-glue-vboxapisetup_INST = $(INST_SDK)installer/python/vboxapi/
     42 VBox-python-glue-vboxapisetup_MODE = a+r,u+w
     43 VBox-python-glue-vboxapisetup_SOURCES = vboxapisetup.py=>setup.py
     44
     45 INSTALLS += VBox-python-glue-vboxapisetup-stub
     46
     47 VBox-python-glue-vboxapisetup-stub_INST = $(INST_SDK)installer/python/
     48 VBox-python-glue-vboxapisetup-stub_MODE = a+r,u+w
     49 VBox-python-glue-vboxapisetup-stub_SOURCES = vboxapisetup-stub.py=>vboxapisetup.py
     50
     51 #
     52 # Automatically lint common installer Python stuff.
     53 #
     54 if defined(VBOX_WITH_PYLINT) && !defined(VBOX_WITHOUT_AUTO_PYLINT)
     55  OTHERS      += $(PATH_TARGET)/pylintInstallerCommon.run
     56  OTHER_CLEAN += $(PATH_TARGET)/pylintInstallerCommon.run
     57  $(PATH_TARGET)/pylintInstallerCommon.run: \
     58        ${PATH_SUB_CURRENT}/vboxapisetup.py ${PATH_SUB_CURRENT}/vboxapisetup-stub.py
     59        $(QUIET)$(APPEND) -t "$@"
     60 endif
     61
     62 VBOX_INSTALLER_COMMON_PYTHON_SOURCES := $(wildcard $(PATH_SUB_CURRENT)/*.py)
     63
     64 $(evalcall def_vbox_installer_common_process_python_sources,FORCE)
    4465
    4566endif # VBOX_WITH_PYTHON
    4667
    4768include $(FILE_KBUILD_SUB_FOOTER)
    48 
  • trunk/src/VBox/Installer/common/vboxapisetup.py

    r102849 r103028  
    3030"""
    3131
    32 import os,sys
    33 from distutils.core import setup
    34 
    35 def cleanupComCache():
     32#
     33# For "modern" Python packages setuptools only is one of many ways
     34# for installing a package on a system and acts as a pure build backend now.
     35# Using a build backend is controlled by the accompanied pyproject.toml file.
     36#
     37# See: https://packaging.python.org/en/latest/discussions/setup-py-deprecated/#setup-py-deprecated
     38#
     39# PEP 518 [1] introduced pyproject.toml.
     40# PEP 632 [2], starting with Python 3.10, the distutils module is marked as being deprecated.
     41# Python 3.12 does not ship with distutils anymore, but we have to still support Python < 3.12.
     42#
     43# [1] https://peps.python.org/pep-0518/
     44# [2] https://peps.python.org/pep-0632/
     45
     46import atexit
     47import io
     48import os
     49import platform
     50import sys
     51
     52g_fVerbose = True
     53
     54def cleanupWinComCacheDir(sPath):
     55    """
     56    Cleans up a Windows COM cache directory by deleting it.
     57    """
     58    if not sPath:
     59        return
    3660    import shutil
    37     from distutils.sysconfig import get_python_lib
    38     comCache1 = os.path.join(get_python_lib(), 'win32com', 'gen_py')
    39     comCache2 = os.path.join(os.environ.get("TEMP", "c:\\tmp"), 'gen_py')
    40     print("Cleaning COM cache at",comCache1,"and",comCache2)
    41     shutil.rmtree(comCache1, True)
    42     shutil.rmtree(comCache2, True)
    43 
    44 def patchWith(file,install,sdk):
    45     newFile=file + ".new"
    46     install=install.replace("\\", "\\\\")
    47     try:
    48         os.remove(newFile)
    49     except:
     61    sDirCache = os.path.join(sPath, 'win32com', 'gen_py')
     62    print("Cleaning COM cache at '%s'" % (sDirCache))
     63    shutil.rmtree(sDirCache, True)
     64
     65def cleanupWinComCache():
     66    """
     67    Cleans up various Windows COM cache directories by deleting them.
     68    """
     69    if sys.version_info >= (3, 2): # Since Python 3.2 we use the site module.
     70        import site
     71        for sSiteDir in site.getsitepackages():
     72            cleanupWinComCacheDir(sSiteDir)
     73    else:
     74        from distutils.sysconfig import get_python_lib # pylint: disable=deprecated-module
     75        cleanupWinComCacheDir(get_python_lib())
     76
     77    # @todo r=andy Do still need/want this? Was there forever. Probably a leftover.
     78    sDirTemp = os.path.join(os.environ.get("TEMP", "c:\\tmp"), 'gen_py')
     79    cleanupWinComCacheDir(sDirTemp)
     80
     81def patchWith(sFile, sVBoxInstallPath, sVBoxSdkPath):
     82    """
     83    Patches a given file with the VirtualBox install path + SDK path.
     84    """
     85    sFileTemp = sFile + ".new"
     86    sVBoxInstallPath = sVBoxInstallPath.replace("\\", "\\\\")
     87    try:
     88        os.remove(sFileTemp)
     89    except Exception as _:
    5090        pass
    51     oldF = open(file, 'r')
    52     newF = open(newFile, 'w')
    53     for line in oldF:
    54         line = line.replace("%VBOX_INSTALL_PATH%", install)
    55         line = line.replace("%VBOX_SDK_PATH%", sdk)
    56         newF.write(line)
    57     newF.close()
    58     oldF.close()
    59     try:
    60         os.remove(file)
    61     except:
     91    # Note: We need io.open() to specify the file encoding on Python <= 2.7.
     92    try:
     93        with io.open(sFile, 'r', encoding='utf-8') as fileSrc:
     94            with io.open(sFileTemp, 'w', encoding='utf-8') as fileDst:
     95                for line in fileSrc:
     96                    line = line.replace("%VBOX_INSTALL_PATH%", sVBoxInstallPath)
     97                    line = line.replace("%VBOX_SDK_PATH%", sVBoxSdkPath)
     98                    fileDst.write(line)
     99                fileDst.close()
     100            fileSrc.close()
     101    except IOError as exc:
     102        print("ERROR: Opening VirtualBox Python source file '%s' failed: %s" % (sFile, exc))
     103        return False
     104    try:
     105        os.remove(sFile)
     106    except Exception as _:
    62107        pass
    63     os.rename(newFile, file)
    64 
    65 # See http://docs.python.org/distutils/index.html
    66 def main(argv):
    67     vboxDest = os.environ.get("VBOX_MSI_INSTALL_PATH", None)
    68     if vboxDest is None:
    69         vboxDest = os.environ.get('VBOX_INSTALL_PATH', None)
    70         if vboxDest is None:
    71             raise Exception("No VBOX_INSTALL_PATH defined, exiting")
    72 
    73     vboxVersion = os.environ.get("VBOX_VERSION", None)
    74     if vboxVersion is None:
     108    os.rename(sFileTemp, sFile)
     109    return True
     110
     111def testVBoxAPI():
     112    """
     113    Performs various VirtualBox API tests.
     114    """
     115
     116    # Give the user a hint where we gonna install stuff into.
     117    if  g_fVerbose \
     118    and sys.version_info.major >= 3:
     119        import site
     120        print("Global site packages directories are:")
     121        for sPath in site.getsitepackages():
     122            print("\t%s" % (sPath))
     123        print("User site packages directories are:")
     124        print("\t%s" % (site.getusersitepackages()))
     125        print("Module search path is:")
     126        for sPath in sys.path:
     127            print("\t%s" % (sPath))
     128
     129    #
     130    # Test using the just installed VBox API module by calling some (simpler) APIs
     131    # where now kernel drivers are other fancy stuff is needed.
     132    #
     133    try:
     134        from vboxapi import VirtualBoxManager
     135        oVBoxMgr = VirtualBoxManager()
     136        oVBox    = oVBoxMgr.getVirtualBox()
     137        oHost    = oVBox.host
     138        if oHost.architecture not in (oVBoxMgr.constants.PlatformArchitecture_x86, \
     139                                      oVBoxMgr.constants.PlatformArchitecture_ARM):
     140            raise Exception('Host platform invalid!')
     141        print("Testing VirtualBox Python bindings successful: Detected VirtualBox %s (%d)" % (oVBox.version, oHost.architecture))
     142        _ = oVBox.getMachines()
     143        oVBoxMgr.deinit()
     144        del oVBoxMgr
     145    except ImportError as exc:
     146        print("ERROR: Testing VirtualBox Python bindings failed: %s" % (exc))
     147        return False
     148
     149    print("Installation of VirtualBox Python bindings for Python %d.%d successful." \
     150          % (sys.version_info.major, sys.version_info.minor))
     151    return True
     152
     153def findModulePathHelper(sModule = 'vboxapi', aDir = sys.path):
     154    """
     155    Helper function for findModulePath.
     156
     157    Returns the path found, or None if not found.
     158    """
     159    for sPath in aDir:
     160        if g_fVerbose:
     161            print('Searching for "%s" in path "%s" ...' % (sModule, sPath))
     162        if os.path.isdir(sPath):
     163            aDirEntries = os.listdir(sPath)
     164            if g_fVerbose:
     165                print(aDirEntries)
     166            if sModule in aDirEntries:
     167                return os.path.join(sPath, sModule)
     168    return None
     169
     170def findModulePath(sModule = 'vboxapi'):
     171    """
     172    Finds a module in the system path.
     173
     174    Returns the path found, or None if not found.
     175    """
     176    sPath = findModulePathHelper(sModule)
     177    if not sPath:
     178        try:
     179            import site # Might not available everywhere.
     180            sPath = findModulePathHelper(sModule, site.getsitepackages())
     181        except:
     182            pass
     183    return sPath
     184
     185try:
     186    from distutils.command.install import install # Only for < Python 3.12.
     187except:
     188    pass
     189
     190class setupInstallClass(install):
     191    """
     192    Class which overrides the "install" command of the setup so that we can
     193    run post-install actions.
     194    """
     195
     196    def run(self):
     197        def _post_install():
     198            if findModulePath():
     199                testVBoxAPI()
     200        atexit.register(_post_install)
     201        install.run(self)
     202
     203def main():
     204    """
     205    Main function for the setup script.
     206    """
     207
     208    print("Installing VirtualBox bindings for Python %d.%d ..." % (sys.version_info.major, sys.version_info.minor))
     209
     210    # Deprecation warning for older Python stuff (< Python 3.x).
     211    if sys.version_info.major < 3:
     212        print("\nWarning: Running VirtualBox with Python %d.%d is marked as being deprecated.\n" \
     213              "Please upgrade your Python installation to avoid breakage.\n" \
     214              % (sys.version_info.major, sys.version_info.minor))
     215
     216    sVBoxInstallPath = os.environ.get("VBOX_MSI_INSTALL_PATH", None)
     217    if sVBoxInstallPath is None:
     218        sVBoxInstallPath = os.environ.get('VBOX_INSTALL_PATH', None)
     219        if sVBoxInstallPath is None:
     220            print("No VBOX_INSTALL_PATH defined, exiting")
     221            return 1
     222
     223    sVBoxVersion = os.environ.get("VBOX_VERSION", None)
     224    if sVBoxVersion is None:
    75225        # Should we use VBox version for binding module versioning?
    76         vboxVersion = "1.0"
    77 
    78     import platform
     226        sVBoxVersion = "1.0"
     227
     228    if g_fVerbose:
     229        print("VirtualBox installation directory is: %s" % (sVBoxInstallPath))
    79230
    80231    if platform.system() == 'Windows':
    81         cleanupComCache()
     232        cleanupWinComCache()
     233
     234    # Make sure that we always are in the directory where this script resides.
     235    # Otherwise installing packages below won't work.
     236    sCurDir = os.path.dirname(os.path.abspath(__file__))
     237    if g_fVerbose:
     238        print("Current directory is: %s" % (sCurDir))
     239    try:
     240        os.chdir(sCurDir)
     241    except OSError as exc:
     242        print("Changing to current directory failed: %s" % (exc))
    82243
    83244    # Darwin: Patched before installation. Modifying bundle is not allowed, breaks signing and upsets gatekeeper.
    84245    if platform.system() != 'Darwin':
    85         vboxSdkDest = os.path.join(vboxDest, "sdk")
    86         patchWith(os.path.join(os.path.dirname(sys.argv[0]), 'vboxapi', '__init__.py'), vboxDest, vboxSdkDest)
    87 
    88     setup(name='vboxapi',
    89           version=vboxVersion,
    90           description='Python interface to VirtualBox',
    91           author='Oracle Corp.',
    92           author_email='[email protected]',
    93           url='https://www.virtualbox.org',
    94           packages=['vboxapi']
    95           )
     246        # @todo r=andy This *will* break the script if VirtualBox installation files will be moved.
     247        #              Better would be patching the *installed* module instead of the original module.
     248        sVBoxSdkPath = os.path.join(sVBoxInstallPath, "sdk")
     249        fRc = patchWith(os.path.join(sCurDir, 'src', 'vboxapi', '__init__.py'), \
     250                        sVBoxInstallPath, sVBoxSdkPath)
     251        if not fRc:
     252            return 1
     253
     254    try:
     255        #
     256        # Detect which installation method is being used.
     257        #
     258        # This is a bit messy due the fact that we want to support a broad range of older and newer
     259        # Python versions, along with distributions which maintain their own Python packages (e.g. newer Ubuntus).
     260        #
     261        fInvokeSetupTools = False
     262        if sys.version_info >= (3, 12): # Since Python 3.12 there are no distutils anymore. See PEP632.
     263            try:
     264                from setuptools import setup
     265            except ImportError:
     266                print("ERROR: setuptools package not installed, can't continue. Exiting.")
     267                return 1
     268            setup(cmdclass={"install": setupInstallClass})
     269        else:
     270            if sys.version_info >= (3, 3): # Starting with Python 3.3 we use pyproject.toml by using setup().
     271                try:
     272                    from distutils.core import setup # pylint: disable=deprecated-module
     273                    setup(cmdclass={"install": setupInstallClass})
     274                except ImportError:
     275                    print("distutils[.core] package not installed/available, falling back to legacy setuptools ...")
     276                    fInvokeSetupTools = True # Invoke setuptools as a last resort.
     277            else: # Python 2.7.x + Python < 3.6 legacy cruft.
     278                fInvokeSetupTools = True
     279
     280            if fInvokeSetupTools:
     281                if g_fVerbose:
     282                    print("Invoking setuptools directly ...")
     283                setupTool = setup(name='vboxapi',
     284                            version=sVBoxVersion,
     285                            description='Python interface to VirtualBox',
     286                            author='Oracle Corp.',
     287                            author_email='[email protected]',
     288                            url='https://www.virtualbox.org',
     289                            package_dir={'': 'src'},
     290                            packages=['vboxapi'])
     291                if setupTool:
     292                    sPathInstalled = setupTool.command_obj['install'].install_lib
     293                    if sPathInstalled not in sys.path:
     294                        print("\nWARNING: Installation path is not in current module search path!")
     295                        print("         This might happen on OSes / distributions which only maintain packages by")
     296                        print("         a vendor-specific method.")
     297                        print("Hints:")
     298                        print("- Check how the distribution handles user-installable Python packages.")
     299                        print("- Using setuptools directly might be deprecated on the distribution.")
     300                        print("- Using \"pip install ./vboxapi\" within a virtual environment (virtualenv)")
     301                        print("  might fix this.\n")
     302                        sys.path.append(sPathInstalled)
     303
     304                print("Installed to: %s" % (sPathInstalled))
     305
     306    except RuntimeError as exc:
     307        print("ERROR: Installation of VirtualBox Python bindings failed: %s" % (exc))
     308        return 1
     309
     310    if  fInvokeSetupTools \
     311    and not testVBoxAPI():
     312        return 1
     313    return 0
    96314
    97315if __name__ == '__main__':
    98     main(sys.argv)
     316    sys.exit(main())
  • trunk/src/VBox/Installer/darwin/Makefile.kmk

    r101552 r103028  
    721721                        $(VBOX_PATH_VBOX_APP_TMP)/Contents/MacOS/sdk \
    722722                        $(VBOX_PATH_VBOX_APP_TMP)/Contents/MacOS/sdk/installer \
    723                         $(VBOX_PATH_VBOX_APP_TMP)/Contents/MacOS/sdk/installer/vboxapi \
     723                        $(VBOX_PATH_VBOX_APP_TMP)/Contents/MacOS/sdk/installer/python/vboxapi \
    724724                        $(VBOX_PATH_VBOX_APP_TMP)/Contents/MacOS/sdk/bindings/ \
    725725                        $(VBOX_PATH_VBOX_APP_TMP)/Contents/MacOS/sdk/bindings/xpcom \
  • trunk/src/VBox/Installer/darwin/VirtualBox/postflight

    r98103 r103028  
    3434# Install the Python bindings
    3535#
     36## @todo r=andy Merge this code with linux/routines.sh!
     37#
    3638VBOX_INSTALL_PATH=/Applications/VirtualBox.app/Contents/MacOS
    37 PYTHON="python python2.3 python2.5 python2.6 python2.7"
    38 if [ -e "${VBOX_INSTALL_PATH}/sdk/installer/vboxapisetup.py" ]; then
    39     for p in $PYTHON; do
     39PYTHON_INSTALLER_PATH="$VBOX_INSTALL_PATH/sdk/installer/python"
     40PYTHON_BINARIES="\
     41    python2.7  \
     42    python2    \
     43    python3.3  \
     44    python3.4  \
     45    python3.5  \
     46    python3.6  \
     47    python3.7  \
     48    python3.8  \
     49    python3.9  \
     50    python3.10 \
     51    python3.11 \
     52    python3.12 \
     53    python3    \
     54    python"
     55
     56if [ -e "$PYTHON_INSTALLER_PATH/vboxapisetup.py" ]; then
     57    for PYTHON_BIN in $PYTHON_BINARIES; do
    4058        # Install the python bindings if python is in the path
    41         if [ "`\${p} -c 'print "test"' 2> /dev/null`" = "test" ]; then
    42                 echo  1>&2 "Python found: ${p}, installing bindings..."
    43                 # Pass install path via environment
    44                 export VBOX_INSTALL_PATH
    45                 /bin/sh -c "cd $VBOX_INSTALL_PATH/sdk/installer && ${p} vboxapisetup.py install"
    46                 /bin/sh -c "cd $VBOX_INSTALL_PATH/sdk/installer && ${p} vboxapisetup.py clean --all"
     59        if [ "`\${PYTHON_BIN} -c 'print "test"' 2> /dev/null`" = "test" ]; then
     60            echo  1>&2 "Python found: ${PYTHON_BIN}, installing bindings..."
     61            # Pass install path via environment
     62            export VBOX_INSTALL_PATH
     63            # Check if python has working distutils
     64            "$PYTHON_BIN" -c "from distutils.core import setup" > /dev/null 2>&1
     65            if test "$?" -ne 0; then
     66                echo 1>&2 "Python $PYTHON_VER does not have package 'distutils', checking for 'setuptools'..."
     67                # Since Python 3.12 there are no distutils anymore. See PEP632.
     68                "$PYTHON_BIN" -c "from setuptools import setup" > /dev/null 2>&1
     69                if test "$?" -ne 0; then
     70                    echo 1>&2 "Python $PYTHON_VER also does not have package 'setuptools'. Skipping installation."
     71                    return 0
     72                fi
     73                # When we reach here, we have to use 'pip' in order to install our bindings (Python >= 3.12).
     74                if test -x "`which pip 2>/dev/null`"; then
     75                    PYTHON_PIP_BIN=$(which pip)
     76                else
     77                    echo 1>&2 "Python package manager 'pip' not found. Skipping installation."
     78                fi
     79            fi
     80
     81            if [ -n "$PYTHON_PIP_BIN" ]; then
     82                # Note: We use '-v' to show verbose output of our setup.py script on error.
     83                $SHELL -c "cd ${PYTHON_INSTALLER_PATH} && ${PYTHON_PIP_BIN} -v install ./vboxapi"
     84            else
     85                $SHELL -c "cd ${PYTHON_INSTALLER_PATH} && ${PYTHON_BIN} vboxapisetup.py install"
     86                $SHELL -c "cd ${PYTHON_INSTALLER_PATH} && ${PYTHON_BIN} vboxapisetup.py clean --all"
     87            fi
    4788        fi
    4889    done
  • trunk/src/VBox/Installer/linux/Makefile.kmk

    r101365 r103028  
    532532                $(if $(VBOX_WITH_PYTHON), \
    533533                        archive/sdk/installer \
    534                         archive/sdk/installer/vboxapi \
     534                        archive/sdk/installer/python/vboxapi \
    535535                        archive/sdk/bindings/xpcom/python/xpcom \
    536536                        archive/sdk/bindings/xpcom/python/xpcom/client \
  • trunk/src/VBox/Installer/linux/debian/rules

    r101364 r103028  
    309309        fi
    310310        export VBOX_INSTALL_PATH=/usr/lib/$(package) && \
    311             cd $(builddir)/bin/sdk/installer && \
     311            cd $(builddir)/bin/sdk/installer/python && \
    312312            $(PYTHON) ./vboxapisetup.py install --root $(prefix)
    313313        rm -rf $(prefix)/usr/lib/$(package)/sdk/installer
  • trunk/src/VBox/Installer/linux/deffiles

    r98103 r103028  
    511511    sdk/installer/build/lib/vboxapi/VirtualBox_constants.py
    512512    sdk/installer/build/lib/vboxapi/__init__.py
     513    sdk/installer/python/build/lib/vboxapi/VirtualBox_constants.py
     514    sdk/installer/python/build/lib/vboxapi/__init__.py
    513515    sdk/idl/ \
    514516    sdk/idl/nsIDebug.idl \
  • trunk/src/VBox/Installer/linux/routines.sh

    r102880 r103028  
    401401
    402402
    403 # install_python_bindings(pythonbin pythondesc)
     403# install_python_bindings(PYTHON_BIN PYTHON_VER)
    404404# failure: non fatal
     405#
     406## @todo r=andy Merge this code with darwin/VirtualBox/postflight!
    405407install_python_bindings()
    406408{
    407     pythonbin="$1"
    408     pythondesc="$2"
     409    PYTHON_BIN="$1"
     410    PYTHON_VER="$2"
    409411
    410412    # The python binary might not be there, so just exit silently
    411     if test -z "$pythonbin"; then
     413    if test -z "$PYTHON_BIN"; then
    412414        return 0
    413415    fi
    414416
    415     if test -z "$pythondesc"; then
     417    if test -z "$PYTHON_VER"; then
    416418        echo 1>&2 "missing argument to install_python_bindings"
    417419        return 1
    418420    fi
    419421
    420     echo 1>&2 "Python found: $pythonbin, installing bindings..."
    421 
    422     # check if python has working distutils
    423     "$pythonbin" -c "from distutils.core import setup" > /dev/null 2>&1
     422    echo 1>&2 "Python found: $PYTHON_BIN, installing bindings..."
     423
     424    # Check if python has working distutils
     425    "$PYTHON_BIN" -c "from distutils.core import setup" > /dev/null 2>&1
    424426    if test "$?" -ne 0; then
    425         echo 1>&2 "Skipped: $pythondesc install is unusable, missing package 'distutils'"
    426         return 0
    427     fi
     427        echo 1>&2 "Python $PYTHON_VER does not have package 'distutils', checking for 'setuptools'..."
     428        # Since Python 3.12 there are no distutils anymore. See PEP632.
     429        "$PYTHON_BIN" -c "from setuptools import setup" > /dev/null 2>&1
     430        if test "$?" -ne 0; then
     431            echo 1>&2 "Python $PYTHON_VER also does not have package 'setuptools'. Skipping installation."
     432            return 0
     433        fi
     434        # When we reach here, we have to use 'pip' in order to install our bindings (Python >= 3.12).
     435        if test -x "`which pip 2>/dev/null`"; then
     436            PYTHON_PIP_BIN=$(which pip)
     437        else
     438            echo 1>&2 "Python package manager 'pip' not found. Skipping installation."
     439        fi
     440    fi
     441
     442    PYTHON_INSTALLER_PATH="$VBOX_INSTALL_PATH/sdk/installer/python"
    428443
    429444    # Pass install path via environment
    430445    export VBOX_INSTALL_PATH
    431     $SHELL -c "cd $VBOX_INSTALL_PATH/sdk/installer && $pythonbin vboxapisetup.py install \
    432         --record $CONFIG_DIR/python-$CONFIG_FILES"
    433     cat $CONFIG_DIR/python-$CONFIG_FILES >> $CONFIG_DIR/$CONFIG_FILES
    434     rm -f $CONFIG_DIR/python-$CONFIG_FILES
     446
     447    if [ -n "$PYTHON_PIP_BIN" ]; then
     448        # Note: We use '-v' to show verbose output of our setup.py script on error.
     449        $SHELL -c "cd ${PYTHON_INSTALLER_PATH} && ${PYTHON_PIP_BIN} -v install ./vboxapi"
     450    else
     451        $SHELL -c "cd ${PYTHON_INSTALLER_PATH} && ${PYTHON_BIN} ./vboxapisetup.py install \
     452                   --record $CONFIG_DIR/python-$CONFIG_FILES"
     453        cat "$CONFIG_DIR/python-$CONFIG_FILES" >> "$CONFIG_DIR/$CONFIG_FILES"
     454        rm -f "$CONFIG_DIR/python-$CONFIG_FILES"
     455    fi
    435456
    436457    # Remove files created by Python API setup.
    437     rm -rf $VBOX_INSTALL_PATH/sdk/installer/build
    438 }
    439 
     458    rm -rf "$PYTHON_INSTALLER_PATH/build"
     459}
     460
     461## @todo r=andy Merge this code with darwin/VirtualBox/postflight!
    440462maybe_run_python_bindings_installer() {
    441463    VBOX_INSTALL_PATH="${1}"
     
    444466    # the VirtualBox API bindings. Needs to prevent double installs which waste
    445467    # quite a bit of time.
    446     PYTHONS=""
    447     for p in python2.6 python2.7 python2 python3.3 python3.4 python3.5 python3.6 python3.7 python3.8 python3.9 python3.10 python3.11 python3 python; do
    448         if [ "`$p -c 'import sys
     468    PYTHON_VER_INSTALLED=""
     469    PYTHON_BINARIES="\
     470        python2.7 \
     471        python2   \
     472        python3.3 \
     473        python3.4 \
     474        python3.5 \
     475        python3.6 \
     476        python3.7 \
     477        python3.8 \
     478        python3.9 \
     479        python3.10 \
     480        python3.11 \
     481        python3.12 \
     482        python3 \
     483        python"
     484
     485    for PYTHON_BIN in $PYTHON_BINARIES; do
     486        if [ "`$PYTHON_BIN -c 'import sys
    449487if sys.version_info >= (2, 6) and (sys.version_info < (3, 0) or sys.version_info >= (3, 3)):
    450488    print(\"test\")' 2> /dev/null`" != "test" ]; then
     
    453491        # Get python major/minor version, and skip if it was already covered.
    454492        # Uses grep -F to avoid trouble with '.' matching any char.
    455         pyvers="`$p -c 'import sys
     493        PYTHON_VER="`$PYTHON_BIN -c 'import sys
    456494print("%s.%s" % (sys.version_info[0], sys.version_info[1]))' 2> /dev/null`"
    457         if echo "$PYTHONS" | grep -Fq ":$pyvers:"; then
     495        if echo "$PYTHON_VER_INSTALLED" | grep -Fq ":$PYTHON_VER:"; then
    458496            continue
    459497        fi
    460498        # Record which version will be installed. If it fails there is no point
    461499        # trying with different executable/symlink reporting the same version.
    462         PYTHONS="$PYTHONS:$pyvers:"
    463         install_python_bindings "$p" "Python $pyvers"
     500        PYTHON_VER_INSTALLED="$PYTHON_VER_INSTALLED:$PYTHON_VER:"
     501        install_python_bindings "$PYTHON_BIN" "$PYTHON_VER"
    464502    done
    465     if [ -z "$PYTHONS" ]; then
    466         echo 1>&2 "Python (2.6, 2.7 or 3.3 and later) unavailable, skipping bindings installation."
     503    if [ -z "$PYTHON_VER_INSTALLED" ]; then
     504        echo 1>&2 "Python (2.7 or 3.3 and later) unavailable, skipping bindings installation."
    467505        return 1
    468506    fi
     
    470508    return 0
    471509}
     510
  • trunk/src/VBox/Installer/linux/rpm/VirtualBox.tmpl.spec

    r102907 r103028  
    108108%if %{?with_python:1}%{!?with_python:0}
    109109(export VBOX_INSTALL_PATH=/usr/lib/virtualbox && \
    110   cd ./sdk/installer && \
     110  cd ./sdk/installer/python && \
    111111  %{vbox_python} ./vboxapisetup.py install --prefix %{_prefix} --root $RPM_BUILD_ROOT)
    112112%endif
  • trunk/src/VBox/Installer/solaris/vboxconfig.sh

    r102880 r103028  
    903903}
    904904
    905 # install_python_bindings(pythonbin pythondesc)
     905# install_python_bindings(PYTHON_BIN PYTHON_VER)
    906906# failure: non fatal
     907#
     908## @todo r=andy Merge this code with linux/routines.sh!
    907909install_python_bindings()
    908910{
    909     pythonbin="$1"
    910     pythondesc="$2"
     911    PYTHON_BIN="$1"
     912    PYTHON_VER="$2"
    911913
    912914    # The python binary might not be there, so just exit silently
    913     if test -z "$pythonbin"; then
     915    if test -z "$PYTHON_BIN"; then
    914916        return 0
    915917    fi
    916918
    917     if test -z "$pythondesc"; then
     919    if test -z "$PYTHON_VER"; then
    918920        errorprint "missing argument to install_python_bindings"
    919921        return 1
    920922    fi
    921923
    922     infoprint "Python found: $pythonbin, installing bindings..."
     924    infoprint "Python found: $PYTHON_BIN, installing bindings..."
    923925
    924926    # check if python has working distutils
    925     "$pythonbin" -c "from distutils.core import setup" > /dev/null 2>&1
     927    "$PYTHON_BIN" -c "from distutils.core import setup" > /dev/null 2>&1
    926928    if test "$?" -ne 0; then
    927         subprint "Skipped: $pythondesc install is unusable, missing package 'distutils'"
     929        subprint "Skipped: Python $PYTHON_VER does not have package 'distutils', skipping..."
    928930        return 0
    929931    fi
     932
     933    MY_PYTHON_INSTALLER_PATH="$VBOX_INSTALL_PATH/sdk/installer/python"
    930934
    931935    # Pass install path via environment
     
    933937    mkdir -p "$CONFIG_DIR"
    934938    rm -f "$CONFIG_DIR/python-$CONFIG_FILES"
    935     $SHELL -c "cd \"$VBOX_INSTALL_PATH\"/sdk/installer && \"$pythonbin\" ./vboxapisetup.py install \
     939
     940    $SHELL -c "cd ${MY_PYTHON_INSTALLER_PATH} && ${PYTHON_BIN} ./vboxapisetup.py install \
    936941        --record \"$CONFIG_DIR/python-$CONFIG_FILES\"" > /dev/null 2>&1
    937942    if test "$?" -eq 0; then
    938943        cat "$CONFIG_DIR/python-$CONFIG_FILES" >> "$CONFIG_DIR/$CONFIG_FILES"
    939944    else
    940         errorprint "Failed to install bindings for $pythondesc"
     945        errorprint "Failed to install bindings for Python $PYTHON_VER"
    941946    fi
    942947    rm "$CONFIG_DIR/python-$CONFIG_FILES"
    943948
    944949    # Remove files created by Python API setup.
    945     rm -rf $VBOX_INSTALL_PATH/sdk/installer/build
     950    rm -rf "$MY_PYTHON_INSTALLER_PATH/build"
     951    return 0
     952}
     953
     954## @todo r=andy Merge this code with linux/routines.sh!
     955maybe_run_python_bindings_installer() {
     956    MY_PYTHON_INSTALLER_PATH="$VBOX_INSTALL_PATH/sdk/installer/python"
     957
     958    if test -f "$MY_PYTHON_INSTALLER_PATH/vboxapisetup.py" || test -h "$MY_PYTHON_INSTALLER_PATH/vboxapisetup.py"; then
     959        # Install python bindings for non-remote installs
     960        if test "$REMOTEINST" -eq 0; then
     961            infoprint "Installing Python bindings..."
     962
     963            # Loop over all usual suspect Python executable names and try installing
     964            # the VirtualBox API bindings. Needs to prevent double installs which waste
     965            # quite a bit of time.
     966            PYTHON_VER_INSTALLED=""
     967            PYTHON_BINARIES="\
     968                python2.4  \
     969                python2.5  \
     970                python2.6  \
     971                python2.7  \
     972                python2    \
     973                python3.3  \
     974                python3.4  \
     975                python3.5  \
     976                python3.6  \
     977                python3.7  \
     978                python3.8  \
     979                python3.9  \
     980                python3.10 \
     981                python3.11 \
     982                python3    \
     983                python     \
     984                "
     985
     986            for PYTHON_BIN in $PYTHON_BINARIES; do
     987                if [ "`$PYTHON_BIN -c 'import sys
     988if sys.version_info >= (2, 6) and (sys.version_info < (3, 0) or sys.version_info >= (3, 3)):
     989print(\"test\")' 2> /dev/null`" != "test" ]; then
     990                    continue
     991                fi
     992                # Get python major/minor version, and skip if it was
     993                # already covered.  Uses grep -F to avoid trouble with '.'
     994                # matching any char.
     995                PYTHON_VER="`$PYTHON_BIN -c 'import sys
     996print("%s.%s" % (sys.version_info[0], sys.version_info[1]))' 2> /dev/null`"
     997                if echo "$PYTHON_VER_INSTALLED" | /usr/xpg4/bin/grep -Fq ":$PYTHON_VER:"; then
     998                    continue
     999                fi
     1000                # Record which version will be installed. If it fails there
     1001                # is no point trying with different executable/symlink
     1002                # reporting the same version.
     1003                PYTHON_VER_INSTALLED="$PYTHON_VER_INSTALLED:$PYTHON_VER:"
     1004                install_python_bindings "$PYTHON_BIN" "$PYTHON_VER"
     1005            done
     1006            if [ -z "$PYTHON_VER_INSTALLED" ]; then
     1007                warnprint "Python (2.4 to 2.7 or 3.3 and later) unavailable, skipping bindings installation."
     1008            fi
     1009        else
     1010            warnprint "Skipped installing Python bindings. Run, as root, 'vboxapisetup.py install' manually from the booted system."
     1011        fi
     1012    fi
     1013
    9461014    return 0
    9471015}
     
    13401408        fi
    13411409
    1342         if test -f "$VBOX_INSTALL_PATH/sdk/installer/vboxapisetup.py" || test -h "$VBOX_INSTALL_PATH/sdk/installer/vboxapisetup.py"; then
    1343             # Install python bindings for non-remote installs
    1344             if test "$REMOTEINST" -eq 0; then
    1345                 infoprint "Installing Python bindings..."
    1346 
    1347                 # Loop over all usual suspect Python executable names and try
    1348                 # installing the VirtualBox API bindings. Needs to prevent
    1349                 # double installs which waste quite a bit of time.
    1350                 PYTHONS=""
    1351                 for p in python2.4 python2.5 python2.6 python2.7 python2 python3.3 python3.4 python3.5 python3.6 python3.7 python3.8 python3.9 python3.10 python3.11 python3 python; do
    1352                     if [ "`$p -c 'import sys
    1353 if sys.version_info >= (2, 4) and (sys.version_info < (3, 0) or sys.version_info >= (3, 3)):
    1354     print(\"test\")' 2> /dev/null`" != "test" ]; then
    1355                         continue
    1356                     fi
    1357                     # Get python major/minor version, and skip if it was
    1358                     # already covered.  Uses grep -F to avoid trouble with '.'
    1359                     # matching any char.
    1360                     pyvers="`$p -c 'import sys
    1361 print("%s.%s" % (sys.version_info[0], sys.version_info[1]))' 2> /dev/null`"
    1362                     if echo "$PYTHONS" | /usr/xpg4/bin/grep -Fq ":$pyvers:"; then
    1363                         continue
    1364                     fi
    1365                     # Record which version will be installed. If it fails there
    1366                     # is no point trying with different executable/symlink
    1367                     # reporting the same version.
    1368                     PYTHONS="$PYTHONS:$pyvers:"
    1369                     install_python_bindings "$p" "Python $pyvers"
    1370                 done
    1371                 if [ -z "$PYTHONS" ]; then
    1372                     warnprint "Python (2.4 to 2.7 or 3.3 and later) unavailable, skipping bindings installation."
    1373                 fi
    1374             else
    1375                 warnprint "Skipped installing Python bindings. Run, as root, 'vboxapisetup.py install' manually from the booted system."
    1376             fi
    1377         fi
    1378 
     1410        maybe_run_python_bindings_installer
    13791411        update_boot_archive
    13801412
  • trunk/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp

    r100687 r103028  
    608608     */
    609609    /* Get the VBox API setup string. */
    610     WCHAR wszVBoxSDKPath[RTPATH_MAX];
    611     rcWin = VBoxGetMsiProp(hModule, L"CustomActionData", wszVBoxSDKPath, RT_ELEMENTS(wszVBoxSDKPath));
     610    WCHAR wszVBoxPythonInstallerPath[RTPATH_MAX];
     611    rcWin = VBoxGetMsiProp(hModule, L"CustomActionData", wszVBoxPythonInstallerPath, RT_ELEMENTS(wszVBoxPythonInstallerPath));
    612612    if (rcWin == ERROR_SUCCESS)
    613613    {
    614614        /* Make sure our current working directory is the VBox installation path. */
    615         if (SetCurrentDirectoryW(wszVBoxSDKPath))
     615        if (SetCurrentDirectoryW(wszVBoxPythonInstallerPath))
    616616        {
    617617            /* Set required environment variables. */
    618             if (SetEnvironmentVariableW(L"VBOX_INSTALL_PATH", wszVBoxSDKPath))
    619             {
    620                 logStringF(hModule, "InstallPythonAPI: Invoking vboxapisetup.py in \"%ls\" ...", wszVBoxSDKPath);
     618            if (SetEnvironmentVariableW(L"VBOX_INSTALL_PATH", wszVBoxPythonInstallerPath)) /** @todo BUGBUG r=andy That can't be right! */
     619            {
     620                logStringF(hModule, "InstallPythonAPI: Invoking vboxapisetup.py in \"%ls\" ...", wszVBoxPythonInstallerPath);
    621621
    622622                rcWin = procRun(hModule, wszPythonExe, L"vboxapisetup.py install");
     
    650650        else
    651651            logStringF(hModule, "InstallPythonAPI: Could set working directory to \"%ls\": LastError=%u",
    652                        wszVBoxSDKPath, GetLastError());
     652                       wszVBoxPythonInstallerPath, GetLastError());
    653653    }
    654654    else
  • trunk/src/VBox/Installer/win/VBoxMergePython.wxi

    r98103 r103028  
    3333    <Component Id="cp_VBoxPyInst" Guid="C9A40306-5102-11DE-A7BA-C3C555D89593" Win64="$(var.Property_Win64)">
    3434         <File Id="file_vboxapisetup.py" Name="vboxapisetup.py"
    35                Source="$(env.PATH_OUT)\bin\sdk\installer\vboxapisetup.py"
     35               Source="$(env.PATH_OUT)\bin\sdk\installer\python\vboxapisetup.py"
    3636               DiskId="$(var.Property_DiskIdCommon)" />
    3737    </Component>
     
    3939        <Component Id="cp_VBoxPyMod" Guid="DF19CB76-5102-11DE-943B-13C755D89593" Win64="$(var.Property_Win64)">
    4040            <File Id="file___init__.py" Name="__init__.py"
    41                   Source="$(env.PATH_OUT)\bin\sdk\installer\vboxapi\__init__.py"
     41                  Source="$(env.PATH_OUT)\bin\sdk\installer\python\vboxapi\__init__.py"
    4242                  DiskId="$(var.Property_DiskIdCommon)" />
    4343            <File Id="file_VirtualBox_constants.py" Name="VirtualBox_constants.py"
    44                   Source="$(env.PATH_OUT)\bin\sdk\installer\vboxapi\VirtualBox_constants.py"
     44                  Source="$(env.PATH_OUT)\bin\sdk\installer\python\vboxapi\VirtualBox_constants.py"
    4545                  DiskId="$(var.Property_DiskIdCommon)" />
    4646        </Component>
  • trunk/src/VBox/Installer/win/VirtualBox.wxs

    r98103 r103028  
    384384                <Directory Id="dir_SDK" Name="sdk">
    385385<?if $(env.VBOX_WITH_PYTHON) = "yes" ?>
    386                     <Directory Id="dir_SDKInstall" Name="install">
    387     <?if $(env.VBOX_WITH_MSM_INSTALL) = "yes" ?>
    388                         <Merge Id="msm_VBoxPython" Language="!(loc.LANG)" SourceFile="$(var.Property_VBoxMergePython)" DiskId="1" />
    389     <?else ?>
    390                         <Directory Id="msm_VBoxPythonFolder" FileSource=".">
    391                             <?include VBoxMergePython.wxi ?>
     386                    <Directory Id="dir_SDKInstaller" Name="installer"> <!-- Note: For < VBox 7.1 this folder was called 'install'. -->
     387                        <Directory Id="dir_SDKInstallerPython" Name="python">
     388        <?if $(env.VBOX_WITH_MSM_INSTALL) = "yes" ?>
     389                            <Merge Id="msm_VBoxPython" Language="!(loc.LANG)" SourceFile="$(var.Property_VBoxMergePython)" DiskId="1" />
     390        <?else ?>
     391                            <Directory Id="msm_VBoxPythonFolder" FileSource=".">
     392                                <?include VBoxMergePython.wxi ?>
     393                            </Directory>
     394        <?endif ?>
    392395                        </Directory>
    393     <?endif ?>
    394396                    </Directory>
    395397<?endif ?>
  • trunk/src/VBox/Main/Makefile.kmk

    r102517 r103028  
    5353   include $(PATH_SUB_CURRENT)/src-helper-apps/VBoxVolInfo/Makefile.kmk
    5454  endif
     55 endif
     56 ifdef VBOX_WITH_PYTHON
     57  include $(PATH_SUB_CURRENT)/glue/python/Makefile.kmk
    5558 endif
    5659endif
     
    400403
    401404#
    402 # Some SDK bit.
     405# Some SDK bits.
    403406#
    404407INSTALLS += VBox-main-xidl
     
    406409VBox-main-xidl_MODE = a+r,u+w
    407410VBox-main-xidl_SOURCES = $(VBOX_XIDL_FILE_SRC)
    408 
    409 if defined(VBOX_WITH_PYTHON) && !defined(VBOX_ONLY_EXTPACKS)
    410  INSTALLS += VBox-python-glue
    411  VBox-python-glue_INST = $(INST_SDK)installer/vboxapi/
    412  VBox-python-glue_MODE = a+r,u+w
    413  VBox-python-glue_SOURCES = \
    414         glue/vboxapi.py=>__init__.py \
    415         $(VBox-python-glue_0_OUTDIR)/VirtualBox_constants.py
    416  VBox-python-glue_CLEAN = \
    417         $(VBox-python-glue_0_OUTDIR)/VirtualBox_constants.py \
    418         $(VBox-python-glue_0_OUTDIR)/VirtualBox_constants_err_h_1.txt
    419  $(call KB_FN_DO_PASS0_ON_TARGET,VBox-python-glue)
    420  $(call KB_FN_AUTO_CMD_DEPS,$(VBox-python-glue_0_OUTDIR)/VirtualBox_constants.py)
    421  $(VBox-python-glue_0_OUTDIR)/VirtualBox_constants.py \
    422  + $(VBox-python-glue_0_OUTDIR)/VirtualBox_constants_err_h_1.txt: \
    423                 $(VBOX_PATH_MAIN_SRC)/glue/constants-python.xsl \
    424                 $(VBOX_PATH_MAIN_SRC)/glue/vbox-err-consts.sed \
    425                 $(PATH_ROOT)/include/iprt/err.h \
    426                 $(PATH_ROOT)/include/VBox/err.h \
    427                 $(VBOX_XIDL_FILE) \
    428                 | $$(dir $$@)
    429         $(call KB_FN_AUTO_CMD_DEPS_COMMANDS)
    430         $(call MSG_TOOL,xsltproc,Python constants,$<,$@)
    431         $(SED) -f $(VBOX_PATH_MAIN_SRC)/glue/vbox-err-consts.sed \
    432         --output $(VBox-python-glue_0_OUTDIR)/VirtualBox_constants_err_h_1.txt \
    433                 $(PATH_ROOT)/include/iprt/err.h \
    434                 $(PATH_ROOT)/include/VBox/err.h
    435         $(QUIET)$(VBOX_XSLTPROC) -o $@ \
    436                 --stringparam "g_sErrHFile" "$(VBox-python-glue_0_OUTDIR)/VirtualBox_constants_err_h_1.txt" \
    437                 $(VBOX_PATH_MAIN_SRC)/glue/constants-python.xsl $(VBOX_XIDL_FILE)
    438 endif # VBOX_WITH_PYTHON && !VBOX_ONLY_EXTPACKS
    439411
    440412if !defined(VBOX_ONLY_SDK) && !defined(VBOX_ONLY_EXTPACKS) # Note this goes on for *very* long
  • trunk/src/VBox/Main/glue/python/Config.kmk

    r102983 r103028  
    11# $Id$
    22## @file
    3 # kBuild Configuration file for VBoxShell.
     3# kBuild Configuration file for Main glue for Python.
    44#
    55
     
    2626#
    2727
    28 VBOX_VBOXSHELL_CONFIG_KMK_INCLUDED = 1
     28VBOX_MAIN_GLUE_PYTHON_CONFIG_KMK_INCLUDED = 1
    2929
    3030# Include the top-level configure file.
     
    3636# Globals
    3737#
    38 VBOX_PATH_VBOXSHELL_SRC := $(PATH_ROOT)/src/VBox/Frontends/VBoxShell
     38VBOX_PATH_MAIN_GLUE_PYTHON_SRC := $(PATH_ROOT)/src/VBox/Main/glue/python
    3939
    4040#
    4141# List of python sources that should be linted.
    4242#
    43 VBOX_VBOXSHELL_PYTHON_SOURCES    :=
    44 VBOX_VBOXSHELL_PYLINT_TARGETS    :=
     43VBOX_MAIN_GLUE_PYTHON_PYTHON_SOURCES :=
     44VBOX_MAIN_GLUE_PYTHON_PYLINT_TARGETS :=
    4545
    4646ifdef VBOX_WITH_PYLINT
     
    5151# Process python sources.
    5252#
    53 if1of ($(KBUILD_TARGET), win os2)
    54  VBOX_PYTHONPATH_VBOXSHELL = $(PYTHONPATH);$(VBOX_PATH_VBOXSHELL_SRC)
    55 else
    56  VBOX_PYTHONPATH_VBOXSHELL = $(PYTHONPATH):$(VBOX_PATH_VBOXSHELL_SRC)
    57 endif
     53VBOX_PYTHONPATH_MAIN_GLUE_PYTHON = $(PYTHONPATH):$(VBOX_PATH_MAIN_GLUE_PYTHON_SRC)
    5854BLDDIRS += $(PATH_TARGET)/pylint
    5955
    60 define def_vbox_vboxshell_py_check
     56define def_vbox_main_glue_python_py_check
    6157 $(eval name:=$(basename $(notdir $(py))))
    6258
     
    6662 ifdef VBOX_WITH_PYLINT
    6763        $(QUIET2)$(call MSG_L1,Subjecting $(py) to pylint...)
    68         $(QUIET)$(REDIRECT) -C "$(dir $(py))" -E LC_ALL=C -E PYTHONPATH="$(VBOX_PYTHONPATH_VBOXSHELL)" -- \
    69                 $(VBOX_PYLINT) --rcfile=$(VBOX_PATH_VBOXSHELL_SRC)/pylintrc $$(VBOX_PYLINT_FLAGS) $$($(py)_VBOX_PYLINT_FLAGS) ./$(notdir $(py))
     64        $(QUIET)$(REDIRECT) -C "$(dir $(py))" -E LC_ALL=C -E PYTHONPATH="$(VBOX_PYTHONPATH_MAIN_GLUE_PYTHON)" -- \
     65                $(VBOX_PYLINT) --rcfile=$(VBOX_PATH_MAIN_GLUE_PYTHON_SRC)/pylintrc $$(VBOX_PYLINT_FLAGS) $$($(py)_VBOX_PYLINT_FLAGS) ./$(notdir $(py))
    7066 endif
    7167        $(QUIET)$(APPEND) -t "$(PATH_TARGET)/pylint/$(name).o"
    7268 TESTING += $(name)-py-phony.o
    73  VBOX_VBOXSHELL_PYLINT_TARGETS    += $(PATH_TARGET)/pylint/$(name).o
    74 endef # def_vbox_vboxshell_py_check
     69 VBOX_MAIN_GLUE_PYTHON_PYLINT_TARGETS    += $(PATH_TARGET)/pylint/$(name).o
     70endef # def_vbox_main_glue_python_py_check
    7571
    7672
    77 define def_vbox_vboxshell_process_python_sources
     73define def_vbox_main_glue_python_process_python_sources
    7874 if $(words $(_SUB_MAKEFILE_STACK)) <= 0 || "$1" == "FORCE"
    79   $(foreach py, $(VBOX_VBOXSHELL_PYTHON_SOURCES), $(eval $(def_vbox_vboxshell_py_check)))
     75  $(foreach py, $(VBOX_MAIN_GLUE_PYTHON_PYTHON_SOURCES), $(eval $(def_vbox_main_glue_python_py_check)))
    8076 endif
    8177endef
  • trunk/src/VBox/Main/glue/python/vboxapi.py

    r103027 r103028  
    11# -*- coding: utf-8 -*-
    22# $Id$
     3# pylint: disable=import-error -- for cross-platform Win32 imports
     4# pylint: disable=unused-import
     5# pylint: disable=protected-access -- for XPCOM _xpcom member
    36"""
    47VirtualBox Python API Glue.
     
    4952
    5053if sys.version_info >= (3, 0):
    51     xrange = range
    52     long = int
     54    xrange = range # pylint: disable=invalid-name
     55    long = int     # pylint: disable=invalid-name
    5356
    5457#
     
    5659#
    5760import platform
    58 VBoxBinDir = os.environ.get("VBOX_PROGRAM_PATH", None)
    59 VBoxSdkDir = os.environ.get("VBOX_SDK_PATH", None)
    60 
    61 if VBoxBinDir is None:
     61g_sVBoxBinDir = os.environ.get("VBOX_PROGRAM_PATH", None)
     62g_sVBoxSdkDir = os.environ.get("VBOX_SDK_PATH", None)
     63
     64if g_sVBoxBinDir is None:
    6265    if platform.system() == 'Darwin':
    63         VBoxBinDir = '/Applications/VirtualBox.app/Contents/MacOS'
     66        g_sVBoxBinDir = '/Applications/VirtualBox.app/Contents/MacOS'
    6467    else: # Will be set by the installer
    65         VBoxBinDir = "%VBOX_INSTALL_PATH%"
     68        g_sVBoxBinDir = "%VBOX_INSTALL_PATH%"
    6669else:
    67     VBoxBinDir = os.path.abspath(VBoxBinDir)
    68 
    69 if VBoxSdkDir is None:
     70    g_sVBoxBinDir = os.path.abspath(g_sVBoxBinDir)
     71
     72if g_sVBoxSdkDir is None:
    7073    if platform.system() == 'Darwin':
    71         VBoxSdkDir = '/Applications/VirtualBox.app/Contents/MacOS/sdk'
     74        g_sVBoxSdkDir = '/Applications/VirtualBox.app/Contents/MacOS/sdk'
    7275    else: # Will be set by the installer
    73         VBoxSdkDir = "%VBOX_SDK_PATH%"
     76        g_sVBoxSdkDir = "%VBOX_SDK_PATH%"
    7477else:
    75     VBoxSdkDir = os.path.abspath(VBoxSdkDir)
    76 
    77 os.environ["VBOX_PROGRAM_PATH"] = VBoxBinDir
    78 os.environ["VBOX_SDK_PATH"] = VBoxSdkDir
    79 sys.path.append(VBoxBinDir)
     78    g_sVBoxSdkDir = os.path.abspath(g_sVBoxSdkDir)
     79
     80os.environ["VBOX_PROGRAM_PATH"] = g_sVBoxBinDir
     81os.environ["VBOX_SDK_PATH"] = g_sVBoxSdkDir
     82sys.path.append(g_sVBoxBinDir)
    8083
    8184
     
    104107        """
    105108        self.mgr = mgr
    106         self.isMscom = (mgr.type == 'MSCOM')
     109        self.isMscom = mgr.type == 'MSCOM'
    107110        self.collector = vbox.performanceCollector
    108111
     
    138141        # parameters (see #3953) for MSCOM.
    139142        if self.isMscom:
    140             (values, names, objects, names_out, objects_out, units, scales, sequence_numbers,
     143            (values, names, objects, names_out, objects_out, units, scales, _sequence_numbers,
    141144             indices, lengths) = self.collector.queryMetricsData(names, objects)
    142145        else:
    143             (values, names_out, objects_out, units, scales, sequence_numbers,
     146            (values, names_out, objects_out, units, scales, _sequence_numbers,
    144147             indices, lengths) = self.collector.queryMetricsData(names, objects)
    145148        out = []
    146         for i in xrange(0, len(names_out)):
     149        for i in enumerate(names_out):
    147150            scale = int(scales[i])
    148151            if scale != 1:
     
    165168# Attribute hacks.
    166169#
    167 def ComifyName(name):
     170def comifyName(name):
    168171    return name[0].capitalize() + name[1:]
    169172
     
    171174## This is for saving the original DispatchBaseClass __getattr__ and __setattr__
    172175#  method references.
    173 _g_dCOMForward = {
    174     'getattr': None,
    175     'setattr': None,
    176 }
     176_g_dCOMForward = {}
    177177
    178178
     
    193193    # Slow path.
    194194    try:
    195         return _g_dCOMForward['getattr'](self, ComifyName(sAttr))
     195        return _g_dCOMForward['getattr'](self, comifyName(sAttr))
    196196    except AttributeError:
    197197        return _g_dCOMForward['getattr'](self, sAttr)
     
    201201    """ Our setattr replacement for DispatchBaseClass. """
    202202    try:
    203         return _g_dCOMForward['setattr'](self, ComifyName(sAttr), oValue)
     203        return _g_dCOMForward['setattr'](self, comifyName(sAttr), oValue)
    204204    except AttributeError:
    205205        return _g_dCOMForward['setattr'](self, sAttr, oValue)
     
    331331        return None
    332332
    333     def queryInterface(self, oIUnknown, sClassName):
     333    def queryInterface(self, _oIUnknown, _sClassName):
    334334        """
    335335        IUnknown::QueryInterface wrapper.
     
    344344    #
    345345
    346     def xcptGetStatus(self, oXcpt):
     346    def xcptGetStatus(self, _oXcpt):
    347347        """
    348348        Returns the COM status code from the VBox API given exception.
    349349        """
    350         return None
    351 
    352     def xcptIsDeadInterface(self, oXcpt):
     350        raise AttributeError
     351
     352    def xcptIsDeadInterface(self, _oXcpt):
    353353        """
    354354        Returns True if the exception indicates that the interface is dead, False if not.
     
    379379        return False
    380380
    381     def xcptGetMessage(self, oXcpt):
     381    def xcptGetMessage(self, _oXcpt):
    382382        """
    383383        Returns the best error message found in the COM-like exception.
     
    407407            if sAttr[0].isupper() and (sAttr[1].isupper() or sAttr[1] == '_'):
    408408                oAttr = getattr(oSrc, sAttr)
    409                 if type(oAttr) is int:
     409                if isinstance(oAttr, int):
    410410                    setattr(oDst, sAttr, oAttr)
    411411        return oDst
     
    447447
    448448        self.winerror = winerror
     449        self.oHandle  = None;
    449450
    450451        # Setup client impersonation in COM calls.
     
    472473        # Hack the COM dispatcher base class so we can modify method and
    473474        # attribute names to match those in xpcom.
    474         if _g_dCOMForward['setattr'] is None:
     475        if 'getattr' not in _g_dCOMForward:
    475476            _g_dCOMForward['getattr'] = DispatchBaseClass.__dict__['__getattr__']
     477            setattr(DispatchBaseClass, '__getattr__', _CustomGetAttr)
     478
     479        if 'setattr' not in _g_dCOMForward:
    476480            _g_dCOMForward['setattr'] = DispatchBaseClass.__dict__['__setattr__']
    477             setattr(DispatchBaseClass, '__getattr__', _CustomGetAttr)
    478481            setattr(DispatchBaseClass, '__setattr__', _CustomSetAttr)
    479482
     
    543546
    544547    def getArray(self, oInterface, sAttrib):
    545         return oInterface.__getattr__(sAttrib)
     548        return getattr(oInterface, sAttrib)
    546549
    547550    def setArray(self, oInterface, sAttrib, aoArray):
     
    558561        aPropMapGet = getattr(oInterface, '_prop_map_get_')
    559562        aPropMapPut = getattr(oInterface, '_prop_map_put_')
    560         sComAttrib  = sAttrib if sAttrib in aPropMapGet else ComifyName(sAttrib)
     563        sComAttrib  = sAttrib if sAttrib in aPropMapGet else comifyName(sAttrib)
    561564        try:
    562             aArgs, aDefaultArgs = aPropMapPut[sComAttrib]
    563             aGetArgs            = aPropMapGet[sComAttrib]
     565            aArgs, _aDefaultArgs = aPropMapPut[sComAttrib]
     566            aGetArgs             = aPropMapGet[sComAttrib]
    564567        except KeyError: # fallback.
    565             return oInterface.__setattr__(sAttrib, aoArray)
     568            return setattr(oInterface, sAttrib, aoArray)
    566569
    567570        import pythoncom
     
    572575                            (aGetArgs[2],),             # argTypes - trick: we get the type from the getter.
    573576                            aoArray,)                   # The array
     577        return True
    574578
    575579    def initPerThread(self):
     
    582586
    583587    def createListener(self, oImplClass, dArgs):
    584         if True:
    585             raise Exception('no active listeners on Windows as PyGatewayBase::QueryInterface() '
    586                             'returns new gateway objects all the time, thus breaking EventQueue '
    587                             'assumptions about the listener interface pointer being constants between calls ')
    588         # Did this code ever really work?
    589         d = {}
    590         d['BaseClass'] = oImplClass
    591         d['dArgs'] = dArgs
    592         d['tlb_guid'] = PlatformMSCOM.VBOX_TLB_GUID
    593         d['tlb_major'] = PlatformMSCOM.VBOX_TLB_MAJOR
    594         d['tlb_minor'] = PlatformMSCOM.VBOX_TLB_MINOR
    595         str_ = ""
    596         str_ += "import win32com.server.util\n"
    597         str_ += "import pythoncom\n"
    598 
    599         str_ += "class ListenerImpl(BaseClass):\n"
    600         str_ += "   _com_interfaces_ = ['IEventListener']\n"
    601         str_ += "   _typelib_guid_ = tlb_guid\n"
    602         str_ += "   _typelib_version_ = tlb_major, tlb_minor\n"
    603         str_ += "   _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER\n"
    604         # Maybe we'd better implement Dynamic invoke policy, to be more flexible here
    605         str_ += "   _reg_policy_spec_ = 'win32com.server.policy.EventHandlerPolicy'\n"
    606 
    607         # capitalized version of listener method
    608         str_ += "   HandleEvent=BaseClass.handleEvent\n"
    609         str_ += "   def __init__(self): BaseClass.__init__(self, dArgs)\n"
    610         str_ += "result = win32com.server.util.wrap(ListenerImpl())\n"
    611         exec(str_, d, d)
    612         return d['result']
    613 
    614     def waitForEvents(self, timeout):
     588        raise Exception('no active listeners on Windows as PyGatewayBase::QueryInterface() '
     589                        'returns new gateway objects all the time, thus breaking EventQueue '
     590                        'assumptions about the listener interface pointer being constants between calls ')
     591
     592    def waitForEvents(self, cMsTimeout):
    615593        from win32api import GetCurrentThreadId
    616594        from win32event import INFINITE
     
    619597        import types
    620598
    621         if not isinstance(timeout, int):
     599        if not isinstance(cMsTimeout, int):
    622600            raise TypeError("The timeout argument is not an integer")
    623601        if self.tid != GetCurrentThreadId():
    624602            raise Exception("wait for events from the same thread you inited!")
    625603
    626         if timeout < 0:
     604        if cMsTimeout < 0:
    627605            cMsTimeout = INFINITE
    628         else:
    629             cMsTimeout = timeout
    630606        rc = MsgWaitForMultipleObjects(self.aoHandles, 0, cMsTimeout, QS_ALLINPUT)
    631607        if WAIT_OBJECT_0 <= rc < WAIT_OBJECT_0 + len(self.aoHandles):
     
    757733    def __init__(self, dParams):
    758734        PlatformBase.__init__(self, dParams)
    759         sys.path.append(VBoxSdkDir + '/bindings/xpcom/python/')
     735        sys.path.append(g_sVBoxSdkDir + '/bindings/xpcom/python/')
    760736        import xpcom.vboxxpcom
    761737        import xpcom
     
    776752
    777753    def getArray(self, oInterface, sAttrib):
    778         return oInterface.__getattr__('get' + ComifyName(sAttrib))()
     754        return getattr(oInterface, 'get' + comifyName(sAttrib));
    779755
    780756    def setArray(self, oInterface, sAttrib, aoArray):
    781         return oInterface.__getattr__('set' + ComifyName(sAttrib))(aoArray)
     757        return setattr(oInterface, 'set' + comifyName(sAttrib), aoArray)
    782758
    783759    def initPerThread(self):
     
    790766
    791767    def createListener(self, oImplClass, dArgs):
    792         d = {}
    793         d['BaseClass'] = oImplClass
    794         d['dArgs'] = dArgs
    795         str = ""
    796         str += "import xpcom.components\n"
    797         str += "class ListenerImpl(BaseClass):\n"
    798         str += "   _com_interfaces_ = xpcom.components.interfaces.IEventListener\n"
    799         str += "   def __init__(self): BaseClass.__init__(self, dArgs)\n"
    800         str += "result = ListenerImpl()\n"
    801         exec(str, d, d)
    802         return d['result']
    803 
    804     def waitForEvents(self, timeout):
     768        notDocumentedDict = {}
     769        notDocumentedDict['BaseClass'] = oImplClass
     770        notDocumentedDict['dArgs'] = dArgs
     771        sEval = ""
     772        sEval += "import xpcom.components\n"
     773        sEval += "class ListenerImpl(BaseClass):\n"
     774        sEval += "   _com_interfaces_ = xpcom.components.interfaces.IEventListener\n"
     775        sEval += "   def __init__(self): BaseClass.__init__(self, dArgs)\n"
     776        sEval += "result = ListenerImpl()\n"
     777        exec(sEval, notDocumentedDict, notDocumentedDict) # pylint: disable=exec-used
     778        return notDocumentedDict['result']
     779
     780    def waitForEvents(self, cMsTimeout):
    805781        import xpcom
    806         return xpcom._xpcom.WaitForEvents(timeout)
     782        return xpcom._xpcom.WaitForEvents(cMsTimeout)
    807783
    808784    def interruptWaitEvents(self):
     
    868844        PlatformBase.__init__(self, dParams)
    869845        # Import web services stuff.  Fix the sys.path the first time.
    870         sWebServLib = os.path.join(VBoxSdkDir, 'bindings', 'webservice', 'python', 'lib')
     846        sWebServLib = os.path.join(g_sVBoxSdkDir, 'bindings', 'webservice', 'python', 'lib')
    871847        if sWebServLib not in sys.path:
    872848            sys.path.append(sWebServLib)
     
    904880
    905881    def getArray(self, oInterface, sAttrib):
    906         return oInterface.__getattr__(sAttrib)
     882        return getattr(oInterface, sAttrib)
    907883
    908884    def setArray(self, oInterface, sAttrib, aoArray):
    909         return oInterface.__setattr__(sAttrib, aoArray)
    910 
    911     def waitForEvents(self, timeout):
     885        return setattr(oInterface, sAttrib, aoArray)
     886
     887    def waitForEvents(self, _timeout):
    912888        # Webservices cannot do that yet
    913889        return 2
    914890
    915     def interruptWaitEvents(self, timeout):
     891    def interruptWaitEvents(self):
    916892        # Webservices cannot do that yet
    917893        return False
     
    924900
    925901    def queryInterface(self, oIUnknown, sClassName):
    926         d = {}
    927         d['oIUnknown'] = oIUnknown
    928         str = ""
    929         str += "from VirtualBox_wrappers import " + sClassName + "\n"
    930         str += "result = " + sClassName + "(oIUnknown.mgr, oIUnknown.handle)\n"
     902        notDocumentedDict = {}
     903        notDocumentedDict['oIUnknown'] = oIUnknown
     904        sEval = ""
     905        sEval += "from VirtualBox_wrappers import " + sClassName + "\n"
     906        sEval += "result = " + sClassName + "(oIUnknown.mgr, oIUnknown.handle)\n"
    931907        # wrong, need to test if class indeed implements this interface
    932         exec(str, d, d)
    933         return d['result']
     908        exec(sEval, notDocumentedDict, notDocumentedDict) # pylint: disable=exec-used
     909        return notDocumentedDict['result']
    934910
    935911    #
     
    970946# platforms at the same time, so this should be sufficent for most uses and
    971947# be way simpler to use than VirtualBoxManager::oXcptClass.
    972 CurXcptClass = None
     948g_curXcptClass = None
    973949
    974950
     
    10291005        ## The exception class for the selected platform.
    10301006        self.oXcptClass = self.platform.xcptGetBaseXcpt()
    1031         global CurXcptClass
    1032         CurXcptClass = self.oXcptClass
     1007        global g_curXcptClass
     1008        g_curXcptClass = self.oXcptClass
    10331009
    10341010        # Get the virtualbox singleton.
    10351011        try:
    1036             vbox = self.platform.getVirtualBox()
     1012            self.platform.getVirtualBox()
    10371013        except NameError:
    10381014            print("Installation problem: check that appropriate libs in place")
     
    11571133        Returns the VirtualBox binary directory.
    11581134        """
    1159         global VBoxBinDir
    1160         return VBoxBinDir
     1135        return g_sVBoxBinDir
    11611136
    11621137    def getSdkDir(self):
     
    11641139        Returns the VirtualBox SDK directory.
    11651140        """
    1166         global VBoxSdkDir
    1167         return VBoxSdkDir
     1141        return g_sVBoxSdkDir
    11681142
    11691143    def getEnumValueName(self, sEnumTypeNm, oEnumValue, fTypePrefix = False):
     
    11821156            if len(dNamedValues) > 0:
    11831157
    1184                 dValueNames = dict();
     1158                dValueNames = {};
    11851159                for sName in dNamedValues:
    11861160                    dValueNames[dNamedValues[sName]] = sName;
     
    12661240        # Build the dictionary on demand.
    12671241        if self._dErrorValToName is None:
    1268             dErrorValToName = dict()
     1242            dErrorValToName = {}
    12691243            for sKey in dir(self.statuses):
    12701244                if sKey[0].isupper():
  • trunk/src/VBox/ValidationKit/testdriver/vbox.py

    r102910 r103028  
    13941394        if self.oBuild.sSdkPath is not None:
    13951395            sys.path.insert(0, os.path.join(self.oBuild.sSdkPath, 'installer'))
    1396             sys.path.insert(1, os.path.join(self.oBuild.sSdkPath, 'install')); # stupid stupid windows installer!
     1396            sys.path.insert(1, os.path.join(self.oBuild.sSdkPath, 'install')); # stupid stupid windows installer (VBox < 7.1)!
    13971397            sys.path.insert(2, os.path.join(self.oBuild.sSdkPath, 'bindings', 'xpcom', 'python'))
    13981398        os.environ['VBOX_PROGRAM_PATH'] = self.oBuild.sInstallPath;
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette