You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

msvc.py 40KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301
  1. """
  2. Improved support for Microsoft Visual C++ compilers.
  3. Known supported compilers:
  4. --------------------------
  5. Microsoft Visual C++ 9.0:
  6. Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64)
  7. Microsoft Windows SDK 6.1 (x86, x64, ia64)
  8. Microsoft Windows SDK 7.0 (x86, x64, ia64)
  9. Microsoft Visual C++ 10.0:
  10. Microsoft Windows SDK 7.1 (x86, x64, ia64)
  11. Microsoft Visual C++ 14.0:
  12. Microsoft Visual C++ Build Tools 2015 (x86, x64, arm)
  13. Microsoft Visual Studio 2017 (x86, x64, arm, arm64)
  14. Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64)
  15. """
  16. import os
  17. import sys
  18. import platform
  19. import itertools
  20. import distutils.errors
  21. from setuptools.extern.packaging.version import LegacyVersion
  22. from setuptools.extern.six.moves import filterfalse
  23. from .monkey import get_unpatched
  24. if platform.system() == 'Windows':
  25. from setuptools.extern.six.moves import winreg
  26. safe_env = os.environ
  27. else:
  28. """
  29. Mock winreg and environ so the module can be imported
  30. on this platform.
  31. """
  32. class winreg:
  33. HKEY_USERS = None
  34. HKEY_CURRENT_USER = None
  35. HKEY_LOCAL_MACHINE = None
  36. HKEY_CLASSES_ROOT = None
  37. safe_env = dict()
  38. _msvc9_suppress_errors = (
  39. # msvc9compiler isn't available on some platforms
  40. ImportError,
  41. # msvc9compiler raises DistutilsPlatformError in some
  42. # environments. See #1118.
  43. distutils.errors.DistutilsPlatformError,
  44. )
  45. try:
  46. from distutils.msvc9compiler import Reg
  47. except _msvc9_suppress_errors:
  48. pass
  49. def msvc9_find_vcvarsall(version):
  50. """
  51. Patched "distutils.msvc9compiler.find_vcvarsall" to use the standalone
  52. compiler build for Python (VCForPython). Fall back to original behavior
  53. when the standalone compiler is not available.
  54. Redirect the path of "vcvarsall.bat".
  55. Known supported compilers
  56. -------------------------
  57. Microsoft Visual C++ 9.0:
  58. Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64)
  59. Parameters
  60. ----------
  61. version: float
  62. Required Microsoft Visual C++ version.
  63. Return
  64. ------
  65. vcvarsall.bat path: str
  66. """
  67. VC_BASE = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f'
  68. key = VC_BASE % ('', version)
  69. try:
  70. # Per-user installs register the compiler path here
  71. productdir = Reg.get_value(key, "installdir")
  72. except KeyError:
  73. try:
  74. # All-user installs on a 64-bit system register here
  75. key = VC_BASE % ('Wow6432Node\\', version)
  76. productdir = Reg.get_value(key, "installdir")
  77. except KeyError:
  78. productdir = None
  79. if productdir:
  80. vcvarsall = os.path.os.path.join(productdir, "vcvarsall.bat")
  81. if os.path.isfile(vcvarsall):
  82. return vcvarsall
  83. return get_unpatched(msvc9_find_vcvarsall)(version)
  84. def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs):
  85. """
  86. Patched "distutils.msvc9compiler.query_vcvarsall" for support extra
  87. compilers.
  88. Set environment without use of "vcvarsall.bat".
  89. Known supported compilers
  90. -------------------------
  91. Microsoft Visual C++ 9.0:
  92. Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64)
  93. Microsoft Windows SDK 6.1 (x86, x64, ia64)
  94. Microsoft Windows SDK 7.0 (x86, x64, ia64)
  95. Microsoft Visual C++ 10.0:
  96. Microsoft Windows SDK 7.1 (x86, x64, ia64)
  97. Parameters
  98. ----------
  99. ver: float
  100. Required Microsoft Visual C++ version.
  101. arch: str
  102. Target architecture.
  103. Return
  104. ------
  105. environment: dict
  106. """
  107. # Try to get environement from vcvarsall.bat (Classical way)
  108. try:
  109. orig = get_unpatched(msvc9_query_vcvarsall)
  110. return orig(ver, arch, *args, **kwargs)
  111. except distutils.errors.DistutilsPlatformError:
  112. # Pass error if Vcvarsall.bat is missing
  113. pass
  114. except ValueError:
  115. # Pass error if environment not set after executing vcvarsall.bat
  116. pass
  117. # If error, try to set environment directly
  118. try:
  119. return EnvironmentInfo(arch, ver).return_env()
  120. except distutils.errors.DistutilsPlatformError as exc:
  121. _augment_exception(exc, ver, arch)
  122. raise
  123. def msvc14_get_vc_env(plat_spec):
  124. """
  125. Patched "distutils._msvccompiler._get_vc_env" for support extra
  126. compilers.
  127. Set environment without use of "vcvarsall.bat".
  128. Known supported compilers
  129. -------------------------
  130. Microsoft Visual C++ 14.0:
  131. Microsoft Visual C++ Build Tools 2015 (x86, x64, arm)
  132. Microsoft Visual Studio 2017 (x86, x64, arm, arm64)
  133. Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64)
  134. Parameters
  135. ----------
  136. plat_spec: str
  137. Target architecture.
  138. Return
  139. ------
  140. environment: dict
  141. """
  142. # Try to get environment from vcvarsall.bat (Classical way)
  143. try:
  144. return get_unpatched(msvc14_get_vc_env)(plat_spec)
  145. except distutils.errors.DistutilsPlatformError:
  146. # Pass error Vcvarsall.bat is missing
  147. pass
  148. # If error, try to set environment directly
  149. try:
  150. return EnvironmentInfo(plat_spec, vc_min_ver=14.0).return_env()
  151. except distutils.errors.DistutilsPlatformError as exc:
  152. _augment_exception(exc, 14.0)
  153. raise
  154. def msvc14_gen_lib_options(*args, **kwargs):
  155. """
  156. Patched "distutils._msvccompiler.gen_lib_options" for fix
  157. compatibility between "numpy.distutils" and "distutils._msvccompiler"
  158. (for Numpy < 1.11.2)
  159. """
  160. if "numpy.distutils" in sys.modules:
  161. import numpy as np
  162. if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'):
  163. return np.distutils.ccompiler.gen_lib_options(*args, **kwargs)
  164. return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs)
  165. def _augment_exception(exc, version, arch=''):
  166. """
  167. Add details to the exception message to help guide the user
  168. as to what action will resolve it.
  169. """
  170. # Error if MSVC++ directory not found or environment not set
  171. message = exc.args[0]
  172. if "vcvarsall" in message.lower() or "visual c" in message.lower():
  173. # Special error message if MSVC++ not installed
  174. tmpl = 'Microsoft Visual C++ {version:0.1f} is required.'
  175. message = tmpl.format(**locals())
  176. msdownload = 'www.microsoft.com/download/details.aspx?id=%d'
  177. if version == 9.0:
  178. if arch.lower().find('ia64') > -1:
  179. # For VC++ 9.0, if IA64 support is needed, redirect user
  180. # to Windows SDK 7.0
  181. message += ' Get it with "Microsoft Windows SDK 7.0": '
  182. message += msdownload % 3138
  183. else:
  184. # For VC++ 9.0 redirect user to Vc++ for Python 2.7 :
  185. # This redirection link is maintained by Microsoft.
  186. # Contact vspython@microsoft.com if it needs updating.
  187. message += ' Get it from http://aka.ms/vcpython27'
  188. elif version == 10.0:
  189. # For VC++ 10.0 Redirect user to Windows SDK 7.1
  190. message += ' Get it with "Microsoft Windows SDK 7.1": '
  191. message += msdownload % 8279
  192. elif version >= 14.0:
  193. # For VC++ 14.0 Redirect user to Visual C++ Build Tools
  194. message += (' Get it with "Microsoft Visual C++ Build Tools": '
  195. r'https://visualstudio.microsoft.com/downloads/')
  196. exc.args = (message, )
  197. class PlatformInfo:
  198. """
  199. Current and Target Architectures informations.
  200. Parameters
  201. ----------
  202. arch: str
  203. Target architecture.
  204. """
  205. current_cpu = safe_env.get('processor_architecture', '').lower()
  206. def __init__(self, arch):
  207. self.arch = arch.lower().replace('x64', 'amd64')
  208. @property
  209. def target_cpu(self):
  210. return self.arch[self.arch.find('_') + 1:]
  211. def target_is_x86(self):
  212. return self.target_cpu == 'x86'
  213. def current_is_x86(self):
  214. return self.current_cpu == 'x86'
  215. def current_dir(self, hidex86=False, x64=False):
  216. """
  217. Current platform specific subfolder.
  218. Parameters
  219. ----------
  220. hidex86: bool
  221. return '' and not '\x86' if architecture is x86.
  222. x64: bool
  223. return '\x64' and not '\amd64' if architecture is amd64.
  224. Return
  225. ------
  226. subfolder: str
  227. '\target', or '' (see hidex86 parameter)
  228. """
  229. return (
  230. '' if (self.current_cpu == 'x86' and hidex86) else
  231. r'\x64' if (self.current_cpu == 'amd64' and x64) else
  232. r'\%s' % self.current_cpu
  233. )
  234. def target_dir(self, hidex86=False, x64=False):
  235. r"""
  236. Target platform specific subfolder.
  237. Parameters
  238. ----------
  239. hidex86: bool
  240. return '' and not '\x86' if architecture is x86.
  241. x64: bool
  242. return '\x64' and not '\amd64' if architecture is amd64.
  243. Return
  244. ------
  245. subfolder: str
  246. '\current', or '' (see hidex86 parameter)
  247. """
  248. return (
  249. '' if (self.target_cpu == 'x86' and hidex86) else
  250. r'\x64' if (self.target_cpu == 'amd64' and x64) else
  251. r'\%s' % self.target_cpu
  252. )
  253. def cross_dir(self, forcex86=False):
  254. r"""
  255. Cross platform specific subfolder.
  256. Parameters
  257. ----------
  258. forcex86: bool
  259. Use 'x86' as current architecture even if current acritecture is
  260. not x86.
  261. Return
  262. ------
  263. subfolder: str
  264. '' if target architecture is current architecture,
  265. '\current_target' if not.
  266. """
  267. current = 'x86' if forcex86 else self.current_cpu
  268. return (
  269. '' if self.target_cpu == current else
  270. self.target_dir().replace('\\', '\\%s_' % current)
  271. )
  272. class RegistryInfo:
  273. """
  274. Microsoft Visual Studio related registry informations.
  275. Parameters
  276. ----------
  277. platform_info: PlatformInfo
  278. "PlatformInfo" instance.
  279. """
  280. HKEYS = (winreg.HKEY_USERS,
  281. winreg.HKEY_CURRENT_USER,
  282. winreg.HKEY_LOCAL_MACHINE,
  283. winreg.HKEY_CLASSES_ROOT)
  284. def __init__(self, platform_info):
  285. self.pi = platform_info
  286. @property
  287. def visualstudio(self):
  288. """
  289. Microsoft Visual Studio root registry key.
  290. """
  291. return 'VisualStudio'
  292. @property
  293. def sxs(self):
  294. """
  295. Microsoft Visual Studio SxS registry key.
  296. """
  297. return os.path.join(self.visualstudio, 'SxS')
  298. @property
  299. def vc(self):
  300. """
  301. Microsoft Visual C++ VC7 registry key.
  302. """
  303. return os.path.join(self.sxs, 'VC7')
  304. @property
  305. def vs(self):
  306. """
  307. Microsoft Visual Studio VS7 registry key.
  308. """
  309. return os.path.join(self.sxs, 'VS7')
  310. @property
  311. def vc_for_python(self):
  312. """
  313. Microsoft Visual C++ for Python registry key.
  314. """
  315. return r'DevDiv\VCForPython'
  316. @property
  317. def microsoft_sdk(self):
  318. """
  319. Microsoft SDK registry key.
  320. """
  321. return 'Microsoft SDKs'
  322. @property
  323. def windows_sdk(self):
  324. """
  325. Microsoft Windows/Platform SDK registry key.
  326. """
  327. return os.path.join(self.microsoft_sdk, 'Windows')
  328. @property
  329. def netfx_sdk(self):
  330. """
  331. Microsoft .NET Framework SDK registry key.
  332. """
  333. return os.path.join(self.microsoft_sdk, 'NETFXSDK')
  334. @property
  335. def windows_kits_roots(self):
  336. """
  337. Microsoft Windows Kits Roots registry key.
  338. """
  339. return r'Windows Kits\Installed Roots'
  340. def microsoft(self, key, x86=False):
  341. """
  342. Return key in Microsoft software registry.
  343. Parameters
  344. ----------
  345. key: str
  346. Registry key path where look.
  347. x86: str
  348. Force x86 software registry.
  349. Return
  350. ------
  351. str: value
  352. """
  353. node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node'
  354. return os.path.join('Software', node64, 'Microsoft', key)
  355. def lookup(self, key, name):
  356. """
  357. Look for values in registry in Microsoft software registry.
  358. Parameters
  359. ----------
  360. key: str
  361. Registry key path where look.
  362. name: str
  363. Value name to find.
  364. Return
  365. ------
  366. str: value
  367. """
  368. KEY_READ = winreg.KEY_READ
  369. openkey = winreg.OpenKey
  370. ms = self.microsoft
  371. for hkey in self.HKEYS:
  372. try:
  373. bkey = openkey(hkey, ms(key), 0, KEY_READ)
  374. except (OSError, IOError):
  375. if not self.pi.current_is_x86():
  376. try:
  377. bkey = openkey(hkey, ms(key, True), 0, KEY_READ)
  378. except (OSError, IOError):
  379. continue
  380. else:
  381. continue
  382. try:
  383. return winreg.QueryValueEx(bkey, name)[0]
  384. except (OSError, IOError):
  385. pass
  386. class SystemInfo:
  387. """
  388. Microsoft Windows and Visual Studio related system inormations.
  389. Parameters
  390. ----------
  391. registry_info: RegistryInfo
  392. "RegistryInfo" instance.
  393. vc_ver: float
  394. Required Microsoft Visual C++ version.
  395. """
  396. # Variables and properties in this class use originals CamelCase variables
  397. # names from Microsoft source files for more easy comparaison.
  398. WinDir = safe_env.get('WinDir', '')
  399. ProgramFiles = safe_env.get('ProgramFiles', '')
  400. ProgramFilesx86 = safe_env.get('ProgramFiles(x86)', ProgramFiles)
  401. def __init__(self, registry_info, vc_ver=None):
  402. self.ri = registry_info
  403. self.pi = self.ri.pi
  404. self.vc_ver = vc_ver or self._find_latest_available_vc_ver()
  405. def _find_latest_available_vc_ver(self):
  406. try:
  407. return self.find_available_vc_vers()[-1]
  408. except IndexError:
  409. err = 'No Microsoft Visual C++ version found'
  410. raise distutils.errors.DistutilsPlatformError(err)
  411. def find_available_vc_vers(self):
  412. """
  413. Find all available Microsoft Visual C++ versions.
  414. """
  415. ms = self.ri.microsoft
  416. vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs)
  417. vc_vers = []
  418. for hkey in self.ri.HKEYS:
  419. for key in vckeys:
  420. try:
  421. bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ)
  422. except (OSError, IOError):
  423. continue
  424. subkeys, values, _ = winreg.QueryInfoKey(bkey)
  425. for i in range(values):
  426. try:
  427. ver = float(winreg.EnumValue(bkey, i)[0])
  428. if ver not in vc_vers:
  429. vc_vers.append(ver)
  430. except ValueError:
  431. pass
  432. for i in range(subkeys):
  433. try:
  434. ver = float(winreg.EnumKey(bkey, i))
  435. if ver not in vc_vers:
  436. vc_vers.append(ver)
  437. except ValueError:
  438. pass
  439. return sorted(vc_vers)
  440. @property
  441. def VSInstallDir(self):
  442. """
  443. Microsoft Visual Studio directory.
  444. """
  445. # Default path
  446. name = 'Microsoft Visual Studio %0.1f' % self.vc_ver
  447. default = os.path.join(self.ProgramFilesx86, name)
  448. # Try to get path from registry, if fail use default path
  449. return self.ri.lookup(self.ri.vs, '%0.1f' % self.vc_ver) or default
  450. @property
  451. def VCInstallDir(self):
  452. """
  453. Microsoft Visual C++ directory.
  454. """
  455. self.VSInstallDir
  456. guess_vc = self._guess_vc() or self._guess_vc_legacy()
  457. # Try to get "VC++ for Python" path from registry as default path
  458. reg_path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver)
  459. python_vc = self.ri.lookup(reg_path, 'installdir')
  460. default_vc = os.path.join(python_vc, 'VC') if python_vc else guess_vc
  461. # Try to get path from registry, if fail use default path
  462. path = self.ri.lookup(self.ri.vc, '%0.1f' % self.vc_ver) or default_vc
  463. if not os.path.isdir(path):
  464. msg = 'Microsoft Visual C++ directory not found'
  465. raise distutils.errors.DistutilsPlatformError(msg)
  466. return path
  467. def _guess_vc(self):
  468. """
  469. Locate Visual C for 2017
  470. """
  471. if self.vc_ver <= 14.0:
  472. return
  473. default = r'VC\Tools\MSVC'
  474. guess_vc = os.path.join(self.VSInstallDir, default)
  475. # Subdir with VC exact version as name
  476. try:
  477. vc_exact_ver = os.listdir(guess_vc)[-1]
  478. return os.path.join(guess_vc, vc_exact_ver)
  479. except (OSError, IOError, IndexError):
  480. pass
  481. def _guess_vc_legacy(self):
  482. """
  483. Locate Visual C for versions prior to 2017
  484. """
  485. default = r'Microsoft Visual Studio %0.1f\VC' % self.vc_ver
  486. return os.path.join(self.ProgramFilesx86, default)
  487. @property
  488. def WindowsSdkVersion(self):
  489. """
  490. Microsoft Windows SDK versions for specified MSVC++ version.
  491. """
  492. if self.vc_ver <= 9.0:
  493. return ('7.0', '6.1', '6.0a')
  494. elif self.vc_ver == 10.0:
  495. return ('7.1', '7.0a')
  496. elif self.vc_ver == 11.0:
  497. return ('8.0', '8.0a')
  498. elif self.vc_ver == 12.0:
  499. return ('8.1', '8.1a')
  500. elif self.vc_ver >= 14.0:
  501. return ('10.0', '8.1')
  502. @property
  503. def WindowsSdkLastVersion(self):
  504. """
  505. Microsoft Windows SDK last version
  506. """
  507. return self._use_last_dir_name(os.path.join(
  508. self.WindowsSdkDir, 'lib'))
  509. @property
  510. def WindowsSdkDir(self):
  511. """
  512. Microsoft Windows SDK directory.
  513. """
  514. sdkdir = ''
  515. for ver in self.WindowsSdkVersion:
  516. # Try to get it from registry
  517. loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver)
  518. sdkdir = self.ri.lookup(loc, 'installationfolder')
  519. if sdkdir:
  520. break
  521. if not sdkdir or not os.path.isdir(sdkdir):
  522. # Try to get "VC++ for Python" version from registry
  523. path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver)
  524. install_base = self.ri.lookup(path, 'installdir')
  525. if install_base:
  526. sdkdir = os.path.join(install_base, 'WinSDK')
  527. if not sdkdir or not os.path.isdir(sdkdir):
  528. # If fail, use default new path
  529. for ver in self.WindowsSdkVersion:
  530. intver = ver[:ver.rfind('.')]
  531. path = r'Microsoft SDKs\Windows Kits\%s' % (intver)
  532. d = os.path.join(self.ProgramFiles, path)
  533. if os.path.isdir(d):
  534. sdkdir = d
  535. if not sdkdir or not os.path.isdir(sdkdir):
  536. # If fail, use default old path
  537. for ver in self.WindowsSdkVersion:
  538. path = r'Microsoft SDKs\Windows\v%s' % ver
  539. d = os.path.join(self.ProgramFiles, path)
  540. if os.path.isdir(d):
  541. sdkdir = d
  542. if not sdkdir:
  543. # If fail, use Platform SDK
  544. sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK')
  545. return sdkdir
  546. @property
  547. def WindowsSDKExecutablePath(self):
  548. """
  549. Microsoft Windows SDK executable directory.
  550. """
  551. # Find WinSDK NetFx Tools registry dir name
  552. if self.vc_ver <= 11.0:
  553. netfxver = 35
  554. arch = ''
  555. else:
  556. netfxver = 40
  557. hidex86 = True if self.vc_ver <= 12.0 else False
  558. arch = self.pi.current_dir(x64=True, hidex86=hidex86)
  559. fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-'))
  560. # liste all possibles registry paths
  561. regpaths = []
  562. if self.vc_ver >= 14.0:
  563. for ver in self.NetFxSdkVersion:
  564. regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)]
  565. for ver in self.WindowsSdkVersion:
  566. regpaths += [os.path.join(self.ri.windows_sdk, 'v%sA' % ver, fx)]
  567. # Return installation folder from the more recent path
  568. for path in regpaths:
  569. execpath = self.ri.lookup(path, 'installationfolder')
  570. if execpath:
  571. break
  572. return execpath
  573. @property
  574. def FSharpInstallDir(self):
  575. """
  576. Microsoft Visual F# directory.
  577. """
  578. path = r'%0.1f\Setup\F#' % self.vc_ver
  579. path = os.path.join(self.ri.visualstudio, path)
  580. return self.ri.lookup(path, 'productdir') or ''
  581. @property
  582. def UniversalCRTSdkDir(self):
  583. """
  584. Microsoft Universal CRT SDK directory.
  585. """
  586. # Set Kit Roots versions for specified MSVC++ version
  587. if self.vc_ver >= 14.0:
  588. vers = ('10', '81')
  589. else:
  590. vers = ()
  591. # Find path of the more recent Kit
  592. for ver in vers:
  593. sdkdir = self.ri.lookup(self.ri.windows_kits_roots,
  594. 'kitsroot%s' % ver)
  595. if sdkdir:
  596. break
  597. return sdkdir or ''
  598. @property
  599. def UniversalCRTSdkLastVersion(self):
  600. """
  601. Microsoft Universal C Runtime SDK last version
  602. """
  603. return self._use_last_dir_name(os.path.join(
  604. self.UniversalCRTSdkDir, 'lib'))
  605. @property
  606. def NetFxSdkVersion(self):
  607. """
  608. Microsoft .NET Framework SDK versions.
  609. """
  610. # Set FxSdk versions for specified MSVC++ version
  611. if self.vc_ver >= 14.0:
  612. return ('4.6.1', '4.6')
  613. else:
  614. return ()
  615. @property
  616. def NetFxSdkDir(self):
  617. """
  618. Microsoft .NET Framework SDK directory.
  619. """
  620. for ver in self.NetFxSdkVersion:
  621. loc = os.path.join(self.ri.netfx_sdk, ver)
  622. sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder')
  623. if sdkdir:
  624. break
  625. return sdkdir or ''
  626. @property
  627. def FrameworkDir32(self):
  628. """
  629. Microsoft .NET Framework 32bit directory.
  630. """
  631. # Default path
  632. guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework')
  633. # Try to get path from registry, if fail use default path
  634. return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw
  635. @property
  636. def FrameworkDir64(self):
  637. """
  638. Microsoft .NET Framework 64bit directory.
  639. """
  640. # Default path
  641. guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64')
  642. # Try to get path from registry, if fail use default path
  643. return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw
  644. @property
  645. def FrameworkVersion32(self):
  646. """
  647. Microsoft .NET Framework 32bit versions.
  648. """
  649. return self._find_dot_net_versions(32)
  650. @property
  651. def FrameworkVersion64(self):
  652. """
  653. Microsoft .NET Framework 64bit versions.
  654. """
  655. return self._find_dot_net_versions(64)
  656. def _find_dot_net_versions(self, bits):
  657. """
  658. Find Microsoft .NET Framework versions.
  659. Parameters
  660. ----------
  661. bits: int
  662. Platform number of bits: 32 or 64.
  663. """
  664. # Find actual .NET version in registry
  665. reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits)
  666. dot_net_dir = getattr(self, 'FrameworkDir%d' % bits)
  667. ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or ''
  668. # Set .NET versions for specified MSVC++ version
  669. if self.vc_ver >= 12.0:
  670. frameworkver = (ver, 'v4.0')
  671. elif self.vc_ver >= 10.0:
  672. frameworkver = ('v4.0.30319' if ver.lower()[:2] != 'v4' else ver,
  673. 'v3.5')
  674. elif self.vc_ver == 9.0:
  675. frameworkver = ('v3.5', 'v2.0.50727')
  676. if self.vc_ver == 8.0:
  677. frameworkver = ('v3.0', 'v2.0.50727')
  678. return frameworkver
  679. def _use_last_dir_name(self, path, prefix=''):
  680. """
  681. Return name of the last dir in path or '' if no dir found.
  682. Parameters
  683. ----------
  684. path: str
  685. Use dirs in this path
  686. prefix: str
  687. Use only dirs startings by this prefix
  688. """
  689. matching_dirs = (
  690. dir_name
  691. for dir_name in reversed(os.listdir(path))
  692. if os.path.isdir(os.path.join(path, dir_name)) and
  693. dir_name.startswith(prefix)
  694. )
  695. return next(matching_dirs, None) or ''
  696. class EnvironmentInfo:
  697. """
  698. Return environment variables for specified Microsoft Visual C++ version
  699. and platform : Lib, Include, Path and libpath.
  700. This function is compatible with Microsoft Visual C++ 9.0 to 14.0.
  701. Script created by analysing Microsoft environment configuration files like
  702. "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ...
  703. Parameters
  704. ----------
  705. arch: str
  706. Target architecture.
  707. vc_ver: float
  708. Required Microsoft Visual C++ version. If not set, autodetect the last
  709. version.
  710. vc_min_ver: float
  711. Minimum Microsoft Visual C++ version.
  712. """
  713. # Variables and properties in this class use originals CamelCase variables
  714. # names from Microsoft source files for more easy comparaison.
  715. def __init__(self, arch, vc_ver=None, vc_min_ver=0):
  716. self.pi = PlatformInfo(arch)
  717. self.ri = RegistryInfo(self.pi)
  718. self.si = SystemInfo(self.ri, vc_ver)
  719. if self.vc_ver < vc_min_ver:
  720. err = 'No suitable Microsoft Visual C++ version found'
  721. raise distutils.errors.DistutilsPlatformError(err)
  722. @property
  723. def vc_ver(self):
  724. """
  725. Microsoft Visual C++ version.
  726. """
  727. return self.si.vc_ver
  728. @property
  729. def VSTools(self):
  730. """
  731. Microsoft Visual Studio Tools
  732. """
  733. paths = [r'Common7\IDE', r'Common7\Tools']
  734. if self.vc_ver >= 14.0:
  735. arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
  736. paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow']
  737. paths += [r'Team Tools\Performance Tools']
  738. paths += [r'Team Tools\Performance Tools%s' % arch_subdir]
  739. return [os.path.join(self.si.VSInstallDir, path) for path in paths]
  740. @property
  741. def VCIncludes(self):
  742. """
  743. Microsoft Visual C++ & Microsoft Foundation Class Includes
  744. """
  745. return [os.path.join(self.si.VCInstallDir, 'Include'),
  746. os.path.join(self.si.VCInstallDir, r'ATLMFC\Include')]
  747. @property
  748. def VCLibraries(self):
  749. """
  750. Microsoft Visual C++ & Microsoft Foundation Class Libraries
  751. """
  752. if self.vc_ver >= 15.0:
  753. arch_subdir = self.pi.target_dir(x64=True)
  754. else:
  755. arch_subdir = self.pi.target_dir(hidex86=True)
  756. paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir]
  757. if self.vc_ver >= 14.0:
  758. paths += [r'Lib\store%s' % arch_subdir]
  759. return [os.path.join(self.si.VCInstallDir, path) for path in paths]
  760. @property
  761. def VCStoreRefs(self):
  762. """
  763. Microsoft Visual C++ store references Libraries
  764. """
  765. if self.vc_ver < 14.0:
  766. return []
  767. return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')]
  768. @property
  769. def VCTools(self):
  770. """
  771. Microsoft Visual C++ Tools
  772. """
  773. si = self.si
  774. tools = [os.path.join(si.VCInstallDir, 'VCPackages')]
  775. forcex86 = True if self.vc_ver <= 10.0 else False
  776. arch_subdir = self.pi.cross_dir(forcex86)
  777. if arch_subdir:
  778. tools += [os.path.join(si.VCInstallDir, 'Bin%s' % arch_subdir)]
  779. if self.vc_ver == 14.0:
  780. path = 'Bin%s' % self.pi.current_dir(hidex86=True)
  781. tools += [os.path.join(si.VCInstallDir, path)]
  782. elif self.vc_ver >= 15.0:
  783. host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else
  784. r'bin\HostX64%s')
  785. tools += [os.path.join(
  786. si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))]
  787. if self.pi.current_cpu != self.pi.target_cpu:
  788. tools += [os.path.join(
  789. si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))]
  790. else:
  791. tools += [os.path.join(si.VCInstallDir, 'Bin')]
  792. return tools
  793. @property
  794. def OSLibraries(self):
  795. """
  796. Microsoft Windows SDK Libraries
  797. """
  798. if self.vc_ver <= 10.0:
  799. arch_subdir = self.pi.target_dir(hidex86=True, x64=True)
  800. return [os.path.join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)]
  801. else:
  802. arch_subdir = self.pi.target_dir(x64=True)
  803. lib = os.path.join(self.si.WindowsSdkDir, 'lib')
  804. libver = self._sdk_subdir
  805. return [os.path.join(lib, '%sum%s' % (libver , arch_subdir))]
  806. @property
  807. def OSIncludes(self):
  808. """
  809. Microsoft Windows SDK Include
  810. """
  811. include = os.path.join(self.si.WindowsSdkDir, 'include')
  812. if self.vc_ver <= 10.0:
  813. return [include, os.path.join(include, 'gl')]
  814. else:
  815. if self.vc_ver >= 14.0:
  816. sdkver = self._sdk_subdir
  817. else:
  818. sdkver = ''
  819. return [os.path.join(include, '%sshared' % sdkver),
  820. os.path.join(include, '%sum' % sdkver),
  821. os.path.join(include, '%swinrt' % sdkver)]
  822. @property
  823. def OSLibpath(self):
  824. """
  825. Microsoft Windows SDK Libraries Paths
  826. """
  827. ref = os.path.join(self.si.WindowsSdkDir, 'References')
  828. libpath = []
  829. if self.vc_ver <= 9.0:
  830. libpath += self.OSLibraries
  831. if self.vc_ver >= 11.0:
  832. libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')]
  833. if self.vc_ver >= 14.0:
  834. libpath += [
  835. ref,
  836. os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'),
  837. os.path.join(
  838. ref,
  839. 'Windows.Foundation.UniversalApiContract',
  840. '1.0.0.0',
  841. ),
  842. os.path.join(
  843. ref,
  844. 'Windows.Foundation.FoundationContract',
  845. '1.0.0.0',
  846. ),
  847. os.path.join(
  848. ref,
  849. 'Windows.Networking.Connectivity.WwanContract',
  850. '1.0.0.0',
  851. ),
  852. os.path.join(
  853. self.si.WindowsSdkDir,
  854. 'ExtensionSDKs',
  855. 'Microsoft.VCLibs',
  856. '%0.1f' % self.vc_ver,
  857. 'References',
  858. 'CommonConfiguration',
  859. 'neutral',
  860. ),
  861. ]
  862. return libpath
  863. @property
  864. def SdkTools(self):
  865. """
  866. Microsoft Windows SDK Tools
  867. """
  868. return list(self._sdk_tools())
  869. def _sdk_tools(self):
  870. """
  871. Microsoft Windows SDK Tools paths generator
  872. """
  873. if self.vc_ver < 15.0:
  874. bin_dir = 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86'
  875. yield os.path.join(self.si.WindowsSdkDir, bin_dir)
  876. if not self.pi.current_is_x86():
  877. arch_subdir = self.pi.current_dir(x64=True)
  878. path = 'Bin%s' % arch_subdir
  879. yield os.path.join(self.si.WindowsSdkDir, path)
  880. if self.vc_ver == 10.0 or self.vc_ver == 11.0:
  881. if self.pi.target_is_x86():
  882. arch_subdir = ''
  883. else:
  884. arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
  885. path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir
  886. yield os.path.join(self.si.WindowsSdkDir, path)
  887. elif self.vc_ver >= 15.0:
  888. path = os.path.join(self.si.WindowsSdkDir, 'Bin')
  889. arch_subdir = self.pi.current_dir(x64=True)
  890. sdkver = self.si.WindowsSdkLastVersion
  891. yield os.path.join(path, '%s%s' % (sdkver, arch_subdir))
  892. if self.si.WindowsSDKExecutablePath:
  893. yield self.si.WindowsSDKExecutablePath
  894. @property
  895. def _sdk_subdir(self):
  896. """
  897. Microsoft Windows SDK version subdir
  898. """
  899. ucrtver = self.si.WindowsSdkLastVersion
  900. return ('%s\\' % ucrtver) if ucrtver else ''
  901. @property
  902. def SdkSetup(self):
  903. """
  904. Microsoft Windows SDK Setup
  905. """
  906. if self.vc_ver > 9.0:
  907. return []
  908. return [os.path.join(self.si.WindowsSdkDir, 'Setup')]
  909. @property
  910. def FxTools(self):
  911. """
  912. Microsoft .NET Framework Tools
  913. """
  914. pi = self.pi
  915. si = self.si
  916. if self.vc_ver <= 10.0:
  917. include32 = True
  918. include64 = not pi.target_is_x86() and not pi.current_is_x86()
  919. else:
  920. include32 = pi.target_is_x86() or pi.current_is_x86()
  921. include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64'
  922. tools = []
  923. if include32:
  924. tools += [os.path.join(si.FrameworkDir32, ver)
  925. for ver in si.FrameworkVersion32]
  926. if include64:
  927. tools += [os.path.join(si.FrameworkDir64, ver)
  928. for ver in si.FrameworkVersion64]
  929. return tools
  930. @property
  931. def NetFxSDKLibraries(self):
  932. """
  933. Microsoft .Net Framework SDK Libraries
  934. """
  935. if self.vc_ver < 14.0 or not self.si.NetFxSdkDir:
  936. return []
  937. arch_subdir = self.pi.target_dir(x64=True)
  938. return [os.path.join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)]
  939. @property
  940. def NetFxSDKIncludes(self):
  941. """
  942. Microsoft .Net Framework SDK Includes
  943. """
  944. if self.vc_ver < 14.0 or not self.si.NetFxSdkDir:
  945. return []
  946. return [os.path.join(self.si.NetFxSdkDir, r'include\um')]
  947. @property
  948. def VsTDb(self):
  949. """
  950. Microsoft Visual Studio Team System Database
  951. """
  952. return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')]
  953. @property
  954. def MSBuild(self):
  955. """
  956. Microsoft Build Engine
  957. """
  958. if self.vc_ver < 12.0:
  959. return []
  960. elif self.vc_ver < 15.0:
  961. base_path = self.si.ProgramFilesx86
  962. arch_subdir = self.pi.current_dir(hidex86=True)
  963. else:
  964. base_path = self.si.VSInstallDir
  965. arch_subdir = ''
  966. path = r'MSBuild\%0.1f\bin%s' % (self.vc_ver, arch_subdir)
  967. build = [os.path.join(base_path, path)]
  968. if self.vc_ver >= 15.0:
  969. # Add Roslyn C# & Visual Basic Compiler
  970. build += [os.path.join(base_path, path, 'Roslyn')]
  971. return build
  972. @property
  973. def HTMLHelpWorkshop(self):
  974. """
  975. Microsoft HTML Help Workshop
  976. """
  977. if self.vc_ver < 11.0:
  978. return []
  979. return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')]
  980. @property
  981. def UCRTLibraries(self):
  982. """
  983. Microsoft Universal C Runtime SDK Libraries
  984. """
  985. if self.vc_ver < 14.0:
  986. return []
  987. arch_subdir = self.pi.target_dir(x64=True)
  988. lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib')
  989. ucrtver = self._ucrt_subdir
  990. return [os.path.join(lib, '%sucrt%s' % (ucrtver, arch_subdir))]
  991. @property
  992. def UCRTIncludes(self):
  993. """
  994. Microsoft Universal C Runtime SDK Include
  995. """
  996. if self.vc_ver < 14.0:
  997. return []
  998. include = os.path.join(self.si.UniversalCRTSdkDir, 'include')
  999. return [os.path.join(include, '%sucrt' % self._ucrt_subdir)]
  1000. @property
  1001. def _ucrt_subdir(self):
  1002. """
  1003. Microsoft Universal C Runtime SDK version subdir
  1004. """
  1005. ucrtver = self.si.UniversalCRTSdkLastVersion
  1006. return ('%s\\' % ucrtver) if ucrtver else ''
  1007. @property
  1008. def FSharp(self):
  1009. """
  1010. Microsoft Visual F#
  1011. """
  1012. if self.vc_ver < 11.0 and self.vc_ver > 12.0:
  1013. return []
  1014. return self.si.FSharpInstallDir
  1015. @property
  1016. def VCRuntimeRedist(self):
  1017. """
  1018. Microsoft Visual C++ runtime redistribuable dll
  1019. """
  1020. arch_subdir = self.pi.target_dir(x64=True)
  1021. if self.vc_ver < 15:
  1022. redist_path = self.si.VCInstallDir
  1023. vcruntime = 'redist%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll'
  1024. else:
  1025. redist_path = self.si.VCInstallDir.replace('\\Tools', '\\Redist')
  1026. vcruntime = 'onecore%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll'
  1027. # Visual Studio 2017 is still Visual C++ 14.0
  1028. dll_ver = 14.0 if self.vc_ver == 15 else self.vc_ver
  1029. vcruntime = vcruntime % (arch_subdir, self.vc_ver, dll_ver)
  1030. return os.path.join(redist_path, vcruntime)
  1031. def return_env(self, exists=True):
  1032. """
  1033. Return environment dict.
  1034. Parameters
  1035. ----------
  1036. exists: bool
  1037. It True, only return existing paths.
  1038. """
  1039. env = dict(
  1040. include=self._build_paths('include',
  1041. [self.VCIncludes,
  1042. self.OSIncludes,
  1043. self.UCRTIncludes,
  1044. self.NetFxSDKIncludes],
  1045. exists),
  1046. lib=self._build_paths('lib',
  1047. [self.VCLibraries,
  1048. self.OSLibraries,
  1049. self.FxTools,
  1050. self.UCRTLibraries,
  1051. self.NetFxSDKLibraries],
  1052. exists),
  1053. libpath=self._build_paths('libpath',
  1054. [self.VCLibraries,
  1055. self.FxTools,
  1056. self.VCStoreRefs,
  1057. self.OSLibpath],
  1058. exists),
  1059. path=self._build_paths('path',
  1060. [self.VCTools,
  1061. self.VSTools,
  1062. self.VsTDb,
  1063. self.SdkTools,
  1064. self.SdkSetup,
  1065. self.FxTools,
  1066. self.MSBuild,
  1067. self.HTMLHelpWorkshop,
  1068. self.FSharp],
  1069. exists),
  1070. )
  1071. if self.vc_ver >= 14 and os.path.isfile(self.VCRuntimeRedist):
  1072. env['py_vcruntime_redist'] = self.VCRuntimeRedist
  1073. return env
  1074. def _build_paths(self, name, spec_path_lists, exists):
  1075. """
  1076. Given an environment variable name and specified paths,
  1077. return a pathsep-separated string of paths containing
  1078. unique, extant, directories from those paths and from
  1079. the environment variable. Raise an error if no paths
  1080. are resolved.
  1081. """
  1082. # flatten spec_path_lists
  1083. spec_paths = itertools.chain.from_iterable(spec_path_lists)
  1084. env_paths = safe_env.get(name, '').split(os.pathsep)
  1085. paths = itertools.chain(spec_paths, env_paths)
  1086. extant_paths = list(filter(os.path.isdir, paths)) if exists else paths
  1087. if not extant_paths:
  1088. msg = "%s environment variable is empty" % name.upper()
  1089. raise distutils.errors.DistutilsPlatformError(msg)
  1090. unique_paths = self._unique_everseen(extant_paths)
  1091. return os.pathsep.join(unique_paths)
  1092. # from Python docs
  1093. def _unique_everseen(self, iterable, key=None):
  1094. """
  1095. List unique elements, preserving order.
  1096. Remember all elements ever seen.
  1097. _unique_everseen('AAAABBBCCDAABBB') --> A B C D
  1098. _unique_everseen('ABBCcAD', str.lower) --> A B C D
  1099. """
  1100. seen = set()
  1101. seen_add = seen.add
  1102. if key is None:
  1103. for element in filterfalse(seen.__contains__, iterable):
  1104. seen_add(element)
  1105. yield element
  1106. else:
  1107. for element in iterable:
  1108. k = key(element)
  1109. if k not in seen:
  1110. seen_add(k)
  1111. yield element