msvc.py 40 KB

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