msvc.py 36 KB

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