msvc.py 41 KB

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