VirtualBox

source: vbox/trunk/src/VBox/Installer/common/vboxapisetup.py

Last change on this file was 109193, checked in by vboxsync, 9 days ago

Installer/vboxapisetup.py: Added a check for installed setuptools and bail out if it isn't available.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.4 KB
Line 
1"""
2Copyright (C) 2009-2024 Oracle and/or its affiliates.
3
4This file is part of VirtualBox base platform packages, as
5available from https://www.215389.xyz.
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9as published by the Free Software Foundation, in version 3 of the
10License.
11
12This program is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program; if not, see <https://www.gnu.org/licenses>.
19
20The contents of this file may alternatively be used under the terms
21of the Common Development and Distribution License Version 1.0
22(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
23in the VirtualBox distribution, in which case the provisions of the
24CDDL are applicable instead of those of the GPL.
25
26You may elect to license modified versions of this file under the
27terms and conditions of either the GPL or the CDDL or both.
28
29SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
30"""
31
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
60 import shutil
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 _:
90 pass
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 _:
107 pass
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
153## @todo r=bird: This is supposed to be publicly visible?
154def findModulePathHelper(sModule = 'vboxapi', asDirs = None):
155 """
156 Helper function for findModulePath.
157
158 Returns the path found, or None if not found.
159 """
160 if asDirs is None:
161 asDirs = sys.path;
162 for sPath in asDirs:
163 if g_fVerbose:
164 print('Searching for "%s" in "%s" ...' % (sModule, sPath))
165 try: print(os.listdir(sPath));
166 except: pass;
167 sCandiate = os.path.join(sPath, sModule);
168 if os.path.exists(sCandiate):
169 return sCandiate;
170 return None
171
172## @todo r=bird: This is supposed to be publicly visible?
173def findModulePath(sModule = 'vboxapi'):
174 """
175 Finds a module in the system path.
176
177 Returns the path found, or None if not found.
178 """
179 sPath = findModulePathHelper(sModule)
180 if not sPath:
181 try:
182 import site # Might not available everywhere.
183 sPath = findModulePathHelper(sModule, site.getsitepackages())
184 except:
185 pass
186 return sPath
187
188#
189# Make sure we have all the stuff to serve VBoxSetupInstallClass below.
190#
191if sys.version_info >= (3, 12): # Since Python >= 3.12 there are no distutils anymore. See PEP632.
192 try:
193 from setuptools import setup
194 from setuptools.command.install import install
195 except ImportError:
196 print("ERROR: setuptools package not installed, can't continue. Exiting.")
197 sys.exit(1)
198else: # Python < 3.12
199 try:
200 from distutils.command.install import install
201 except:
202 print("ERROR: distutils package not installed or too old, can't continue. Exiting.")
203 sys.exit(1)
204
205class VBoxSetupInstallClass(install):
206 """
207 Class which overrides the "install" command of the setup so that we can
208 run post-install actions.
209 """
210
211 def run(self):
212 def _post_install():
213 if findModulePath():
214 testVBoxAPI()
215 atexit.register(_post_install)
216 install.run(self)
217
218def main():
219 """
220 Main function for the setup script.
221 """
222
223 print("Installing VirtualBox bindings for Python %d.%d ..." % (sys.version_info.major, sys.version_info.minor))
224
225 # Deprecation warning for older Python stuff (< Python 3.x).
226 if sys.version_info.major < 3:
227 print("\nWarning: Running VirtualBox with Python %d.%d is marked as being deprecated.\n"
228 "Please upgrade your Python installation to avoid breakage.\n"
229 % (sys.version_info.major, sys.version_info.minor,))
230
231 sVBoxInstallPath = os.environ.get("VBOX_MSI_INSTALL_PATH", None)
232 if sVBoxInstallPath is None:
233 sVBoxInstallPath = os.environ.get('VBOX_INSTALL_PATH', None)
234 if sVBoxInstallPath is None:
235 print("No VBOX_INSTALL_PATH defined, exiting")
236 return 1
237
238 sVBoxVersion = os.environ.get("VBOX_VERSION", None)
239 if sVBoxVersion is None:
240 # Should we use VBox version for binding module versioning?
241 sVBoxVersion = "1.0"
242
243 if g_fVerbose:
244 print("VirtualBox installation directory is: %s" % (sVBoxInstallPath))
245
246 if platform.system() == 'Windows':
247 cleanupWinComCache()
248
249 # Make sure that we always are in the directory where this script resides.
250 # Otherwise installing packages below won't work.
251 sCurDir = os.path.dirname(os.path.abspath(__file__))
252 if g_fVerbose:
253 print("Current directory is: %s" % (sCurDir))
254 try:
255 os.chdir(sCurDir)
256 except OSError as exc:
257 print("Changing to current directory failed: %s" % (exc))
258
259 # Darwin: Patched before installation. Modifying bundle is not allowed, breaks signing and upsets gatekeeper.
260 if platform.system() != 'Darwin':
261 # @todo r=andy This *will* break the script if VirtualBox installation files will be moved.
262 # Better would be patching the *installed* module instead of the original module.
263 sVBoxSdkPath = os.path.join(sVBoxInstallPath, "sdk")
264 fRc = patchWith(os.path.join(sCurDir, 'src', 'vboxapi', '__init__.py'), sVBoxInstallPath, sVBoxSdkPath)
265 if not fRc:
266 return 1
267
268 try:
269 #
270 # Detect which installation method is being used.
271 #
272 # This is a bit messy due the fact that we want to support a broad range of older and newer
273 # Python versions, along with distributions which maintain their own Python packages (e.g. newer Ubuntus).
274 #
275 fInvokeSetupTools = False
276 if sys.version_info >= (3, 12): # Since Python 3.12 there are no distutils anymore. See PEP632.
277 try:
278 from setuptools import setup
279 except ImportError:
280 print("ERROR: setuptools package not installed, can't continue. Exiting.")
281 return 1
282 setup(cmdclass = { "install": VBoxSetupInstallClass, })
283 else:
284 try:
285 from distutils.core import setup # pylint: disable=deprecated-module
286 fInvokeSetupTools = True
287 except ImportError:
288 print("ERROR: distutils.core package not installed/available, can't continue. Exiting.")
289 return 1
290
291 if fInvokeSetupTools:
292 if g_fVerbose:
293 print("Invoking setuptools directly ...")
294 setupTool = setup(name='vboxapi',
295 version=sVBoxVersion,
296 description='Python interface to VirtualBox',
297 author='Oracle Corp.',
298 author_email='[email protected]',
299 url='https://www.215389.xyz',
300 package_dir={'': 'src'},
301 packages=['vboxapi'])
302 if setupTool:
303 sPathInstalled = setupTool.command_obj['install'].install_lib
304 if sPathInstalled not in sys.path:
305 print("");
306 print("WARNING: Installation path is not in current module search path!")
307 print(" This might happen on OSes / distributions which only maintain ")
308 print(" packages by a vendor-specific method.")
309 print("Hints:")
310 print("- Check how the distribution handles user-installable Python packages.")
311 print("- Using setuptools directly might be deprecated on the distribution.")
312 print("- Using \"pip install ./vboxapi\" within a virtual environment (virtualenv)")
313 print(" might fix this.\n")
314 sys.path.append(sPathInstalled)
315
316 print("Installed to: %s" % (sPathInstalled))
317
318 testVBoxAPI() # Testing the VBox API does not affect the exit code.
319
320 except RuntimeError as exc:
321 print("ERROR: Installation of VirtualBox Python bindings failed: %s" % (exc,))
322 return 1
323
324 return 0
325
326if __name__ == '__main__':
327 sys.exit(main())
Note: See TracBrowser for help on using the repository browser.

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