windows.configure 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
  2. # This Source Code Form is subject to the terms of the Mozilla Public
  3. # License, v. 2.0. If a copy of the MPL was not distributed with this
  4. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  5. option('--with-windows-version', nargs=1, default='603',
  6. help='Windows SDK version to target. Win 8.1 (603) is currently'
  7. 'the minimum supported version.')
  8. @depends(target)
  9. def is_windows(target):
  10. return target.kernel == 'WINNT'
  11. @template
  12. def depends_win(*args):
  13. return depends_when(*args, when=is_windows)
  14. @depends_win('--with-windows-version')
  15. @imports(_from='__builtin__', _import='ValueError')
  16. def valid_windows_version(value):
  17. if not value:
  18. die('Cannot build with --without-windows-version')
  19. try:
  20. version = int(value[0], 16)
  21. if version in (0x603,):
  22. return version
  23. except ValueError:
  24. pass
  25. die('Invalid value for --with-windows-version (%s)', value[0])
  26. option(env='WINDOWSSDKDIR', nargs=1,
  27. help='Directory containing the Windows SDK')
  28. @depends_win('WINDOWSSDKDIR', host)
  29. def windows_sdk_dir(value, host):
  30. if value:
  31. return value
  32. if host.kernel != 'WINNT':
  33. return ()
  34. return tuple(x[1] for x in get_registry_values(
  35. r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots'
  36. r'\KitsRoot*'))
  37. # The Windows SDK 8.1 and 10 have different layouts. The former has
  38. # $SDK/include/$subdir, while the latter has $SDK/include/$version/$subdir.
  39. # The vcvars* scripts don't actually care about the version, they just take
  40. # the last alphanumerically.
  41. # The $SDK/lib directories always have version subdirectories, but while the
  42. # versions match the one in $SDK/include for SDK 10, it's "winv6.3" for SDK
  43. # 8.1.
  44. @imports('os')
  45. @imports('re')
  46. @imports(_from='__builtin__', _import='sorted')
  47. @imports(_from='__builtin__', _import='WindowsError')
  48. def get_sdk_dirs(sdk, subdir):
  49. def get_dirs_containing(sdk, stem, subdir):
  50. base = os.path.join(sdk, stem)
  51. try:
  52. subdirs = [d for d in os.listdir(base)
  53. if os.path.isdir(os.path.join(base, d))]
  54. except WindowsError:
  55. subdirs = []
  56. if not subdirs:
  57. return ()
  58. if subdir in subdirs:
  59. return (base,)
  60. # At this point, either we have an incomplete or invalid SDK directory,
  61. # or we exclusively have version numbers in subdirs.
  62. return tuple(os.path.join(base, s) for s in subdirs
  63. if os.path.isdir(os.path.join(base, s, subdir)))
  64. def categorize(dirs):
  65. return {os.path.basename(d): d for d in dirs}
  66. include_dirs = categorize(get_dirs_containing(sdk, 'include', subdir))
  67. lib_dirs = categorize(get_dirs_containing(sdk, 'lib', subdir))
  68. if 'include' in include_dirs:
  69. include_dirs['winv6.3'] = include_dirs['include']
  70. del include_dirs['include']
  71. valid_versions = sorted(set(include_dirs) & set(lib_dirs), reverse=True)
  72. if valid_versions:
  73. return namespace(
  74. path=sdk,
  75. lib=lib_dirs[valid_versions[0]],
  76. include=include_dirs[valid_versions[0]],
  77. )
  78. @imports(_from='mozbuild.shellutil', _import='quote')
  79. def valid_windows_sdk_dir_result(value):
  80. if value:
  81. return '0x%04x in %s' % (value.version, quote(value.path))
  82. @depends_win(c_compiler, windows_sdk_dir, valid_windows_version,
  83. 'WINDOWSSDKDIR')
  84. @checking('for Windows SDK', valid_windows_sdk_dir_result)
  85. @imports(_from='__builtin__', _import='sorted')
  86. @imports(_from='textwrap', _import='dedent')
  87. def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version,
  88. windows_sdk_dir_env):
  89. if windows_sdk_dir_env:
  90. windows_sdk_dir_env = windows_sdk_dir_env[0]
  91. sdks = {}
  92. for d in windows_sdk_dir:
  93. sdk = get_sdk_dirs(d, 'um')
  94. if sdk:
  95. check = dedent('''\
  96. #include <winsdkver.h>
  97. WINVER_MAXVER
  98. ''')
  99. um_dir = os.path.join(sdk.include, 'um')
  100. shared_dir = os.path.join(sdk.include, 'shared')
  101. result = try_preprocess(compiler.wrapper + [compiler.compiler] +
  102. compiler.flags +
  103. ['-I', um_dir, '-I', shared_dir], 'C',
  104. check)
  105. if result:
  106. maxver = result.splitlines()[-1]
  107. try:
  108. maxver = int(maxver, 0)
  109. except:
  110. pass
  111. else:
  112. sdks[d] = maxver, sdk
  113. continue
  114. if d == windows_sdk_dir_env:
  115. raise FatalCheckError(
  116. 'Error while checking the version of the SDK in '
  117. 'WINDOWSSDKDIR (%s). Please verify it contains a valid and '
  118. 'complete SDK installation.' % windows_sdk_dir_env)
  119. valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True)
  120. if valid_sdks:
  121. biggest_version, sdk = sdks[valid_sdks[0]]
  122. if not valid_sdks or biggest_version < target_version:
  123. if windows_sdk_dir_env:
  124. raise FatalCheckError(
  125. 'You are targeting Windows version 0x%04x, but your SDK only '
  126. 'supports up to version 0x%04x. Install and use an updated SDK, '
  127. 'or target a lower version using --with-windows-version. '
  128. 'Alternatively, try running the Windows SDK Configuration Tool '
  129. 'and selecting a newer SDK. See '
  130. 'https://developer.mozilla.org/En/Windows_SDK_versions for '
  131. 'details on fixing this.' % (target_version, biggest_version))
  132. raise FatalCheckError(
  133. 'Cannot find a Windows SDK for version >= 0x%04x.' % target_version)
  134. return namespace(
  135. path=sdk.path,
  136. include=sdk.include,
  137. lib=sdk.lib,
  138. version=biggest_version,
  139. )
  140. add_old_configure_assignment(
  141. 'WINDOWSSDKDIR',
  142. delayed_getattr(valid_windows_sdk_dir, 'path'))
  143. add_old_configure_assignment(
  144. 'MOZ_WINSDK_MAXVER',
  145. depends(valid_windows_sdk_dir)(
  146. lambda x: '0x%04X0000' % x.version if x else None))
  147. @imports(_from='mozbuild.shellutil', _import='quote')
  148. def valid_ucrt_sdk_dir_result(value):
  149. if value:
  150. return '%s in %s' % (value.version, quote(value.path))
  151. @depends_win(windows_sdk_dir, 'WINDOWSSDKDIR')
  152. @checking('for Universal CRT SDK', valid_ucrt_sdk_dir_result)
  153. @imports('os')
  154. @imports(_from='__builtin__', _import='sorted')
  155. @imports(_import='mozpack.path', _as='mozpath')
  156. def valid_ucrt_sdk_dir(windows_sdk_dir, windows_sdk_dir_env):
  157. if windows_sdk_dir_env:
  158. windows_sdk_dir_env = windows_sdk_dir_env[0]
  159. sdks = {}
  160. for d in windows_sdk_dir:
  161. sdk = get_sdk_dirs(d, 'ucrt')
  162. if sdk:
  163. version = os.path.basename(sdk.include)
  164. # We're supposed to always find a version in the directory, because
  165. # the 8.1 SDK, which doesn't have a version in the directory, doesn't
  166. # contain the Universal CRT SDK. When the main SDK is 8.1, there
  167. # is, however, supposed to be a reduced install of the SDK 10
  168. # with the UCRT.
  169. if version != 'include':
  170. sdks[d] = Version(version), sdk
  171. continue
  172. if d == windows_sdk_dir_env:
  173. # When WINDOWSSDKDIR is set in the environment and we can't find the
  174. # Universal CRT SDK, chances are this is a start-shell-msvc*.bat
  175. # setup, where INCLUDE and LIB already contain the UCRT paths.
  176. ucrt_includes = [
  177. p for p in os.environ.get('INCLUDE', '').split(os.pathsep)
  178. if os.path.basename(p).lower() == 'ucrt'
  179. ]
  180. ucrt_libs = [
  181. p for p in os.environ.get('LIB', '').split(os.pathsep)
  182. if os.path.basename(os.path.dirname(p)).lower() == 'ucrt'
  183. ]
  184. if ucrt_includes and ucrt_libs:
  185. # Pick the first of each, since they are the ones that the
  186. # compiler would look first. Assume they contain the SDK files.
  187. include = os.path.dirname(ucrt_includes[0])
  188. lib = os.path.dirname(os.path.dirname(ucrt_libs[0]))
  189. path = os.path.dirname(os.path.dirname(include))
  190. version = os.path.basename(include)
  191. if version != 'include' and mozpath.basedir(lib, [path]):
  192. sdks[d] = Version(version), namespace(
  193. path=path,
  194. include=include,
  195. lib=lib,
  196. )
  197. continue
  198. raise FatalCheckError(
  199. 'The SDK in WINDOWSSDKDIR (%s) does not contain the Universal '
  200. 'CRT.' % windows_sdk_dir_env)
  201. valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True)
  202. if not valid_sdks:
  203. raise FatalCheckError('Cannot find the Universal CRT SDK. '
  204. 'Please install it.')
  205. version, sdk = sdks[valid_sdks[0]]
  206. return namespace(
  207. path=sdk.path,
  208. include=sdk.include,
  209. lib=sdk.lib,
  210. version=version,
  211. )
  212. @depends_win(c_compiler)
  213. @imports('os')
  214. def vc_path(c_compiler):
  215. if c_compiler.type != 'msvc':
  216. return
  217. # Normally, we'd start from c_compiler.compiler, but for now, it's not the
  218. # ideal full path to the compiler. At least, we're guaranteed find_program
  219. # will get us the one we found in toolchain.configure.
  220. cl = find_program(c_compiler.compiler)
  221. result = os.path.dirname(cl)
  222. while True:
  223. next, p = os.path.split(result)
  224. if next == result:
  225. die('Cannot determine the Visual C++ directory the compiler (%s) '
  226. 'is in' % cl)
  227. result = next
  228. if p.lower() == 'bin':
  229. break
  230. return result
  231. @depends_win(vc_path)
  232. @checking('for the Debug Interface Access SDK', lambda x: x or 'not found')
  233. @imports(_from='os.path', _import='isdir')
  234. def dia_sdk_dir(vc_path):
  235. if vc_path:
  236. path = os.path.join(os.path.dirname(vc_path), 'DIA SDK')
  237. if isdir(path):
  238. return path
  239. @depends_win(vc_path, valid_windows_sdk_dir, valid_ucrt_sdk_dir, dia_sdk_dir)
  240. @imports('os')
  241. def include_path(vc_path, windows_sdk_dir, ucrt_sdk_dir, dia_sdk_dir):
  242. if not vc_path:
  243. return
  244. atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'include')
  245. if not os.path.isdir(atlmfc_dir):
  246. die('Cannot find the ATL/MFC headers in the Visual C++ directory (%s). '
  247. 'Please install them.' % vc_path)
  248. winrt_dir = os.path.join(windows_sdk_dir.include, 'winrt')
  249. if not os.path.isdir(winrt_dir):
  250. die('Cannot find the WinRT headers in the Windows SDK directory (%s). '
  251. 'Please install them.' % windows_sdk_dir.path)
  252. includes = []
  253. include_env = os.environ.get('INCLUDE')
  254. if include_env:
  255. includes.append(include_env)
  256. includes.extend((
  257. os.path.join(vc_path, 'include'),
  258. atlmfc_dir,
  259. os.path.join(windows_sdk_dir.include, 'shared'),
  260. os.path.join(windows_sdk_dir.include, 'um'),
  261. winrt_dir,
  262. os.path.join(ucrt_sdk_dir.include, 'ucrt'),
  263. ))
  264. if dia_sdk_dir:
  265. includes.append(os.path.join(dia_sdk_dir, 'include'))
  266. # Set in the environment for old-configure
  267. includes = os.pathsep.join(includes)
  268. os.environ['INCLUDE'] = includes
  269. return includes
  270. set_config('INCLUDE', include_path)
  271. @depends_win(target, vc_path, valid_windows_sdk_dir, valid_ucrt_sdk_dir, dia_sdk_dir)
  272. @imports('os')
  273. def lib_path(target, vc_path, windows_sdk_dir, ucrt_sdk_dir, dia_sdk_dir):
  274. if not vc_path:
  275. return
  276. vc_target = {
  277. 'x86': '',
  278. 'x86_64': 'amd64',
  279. 'arm': 'arm',
  280. }.get(target.cpu)
  281. if vc_target is None:
  282. return
  283. # As vc_target can be '', and os.path.join will happily use the empty
  284. # string, leading to a string ending with a backslash, that Make will
  285. # interpret as a "string continues on next line" indicator, use variable
  286. # args.
  287. vc_target = (vc_target,) if vc_target else ()
  288. sdk_target = {
  289. 'x86': 'x86',
  290. 'x86_64': 'x64',
  291. 'arm': 'arm',
  292. }.get(target.cpu)
  293. atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'lib', *vc_target)
  294. if not os.path.isdir(atlmfc_dir):
  295. die('Cannot find the ATL/MFC libraries in the Visual C++ directory (%s). '
  296. 'Please install them.' % vc_path)
  297. libs = []
  298. lib_env = os.environ.get('LIB')
  299. if lib_env:
  300. libs.append(lib_env)
  301. libs.extend((
  302. os.path.join(vc_path, 'lib', *vc_target),
  303. atlmfc_dir,
  304. os.path.join(windows_sdk_dir.lib, 'um', sdk_target),
  305. os.path.join(ucrt_sdk_dir.lib, 'ucrt', sdk_target),
  306. ))
  307. if dia_sdk_dir:
  308. libs.append(os.path.join(dia_sdk_dir, 'lib', *vc_target))
  309. # Set in the environment for old-configure
  310. libs = os.pathsep.join(libs)
  311. os.environ['LIB'] = libs
  312. return libs
  313. set_config('LIB', lib_path)
  314. option(env='MT', nargs=1, help='Path to the Microsoft Manifest Tool')
  315. @depends_win(valid_windows_sdk_dir, valid_ucrt_sdk_dir)
  316. @imports(_from='os', _import='environ')
  317. @imports('platform')
  318. def sdk_bin_path(valid_windows_sdk_dir, valid_ucrt_sdk_dir):
  319. if not valid_windows_sdk_dir:
  320. return
  321. vc_host = {
  322. 'x86': 'x86',
  323. 'AMD64': 'x64',
  324. }.get(platform.machine())
  325. # From version 10.0.15063.0 onwards the bin path contains the version number.
  326. versioned_bin = ('bin' if valid_ucrt_sdk_dir.version < '10.0.15063.0'
  327. else os.path.join('bin', str(valid_ucrt_sdk_dir.version)))
  328. result = [
  329. environ['PATH'],
  330. os.path.join(valid_windows_sdk_dir.path, versioned_bin, vc_host)
  331. ]
  332. if vc_host == 'x64':
  333. result.append(
  334. os.path.join(valid_windows_sdk_dir.path, versioned_bin, 'x86'))
  335. return result
  336. mt = check_prog('MT', depends_win()(lambda: ('mt.exe',)), input='MT',
  337. paths=sdk_bin_path)
  338. # Check that MT is not something unexpected like "magnetic tape manipulation
  339. # utility".
  340. @depends_win(mt)
  341. @checking('whether MT is really Microsoft Manifest Tool', lambda x: bool(x))
  342. @imports('subprocess')
  343. def valid_mt(path):
  344. try:
  345. out = subprocess.check_output([path]).splitlines()
  346. out = '\n'.join(l for l in out
  347. if 'Microsoft (R) Manifest Tool' in l)
  348. if out:
  349. return path
  350. except subprocess.CalledProcessError:
  351. pass
  352. raise FatalCheckError('%s is not Microsoft Manifest Tool')
  353. set_config('MSMANIFEST_TOOL', depends(valid_mt)(lambda x: bool(x)))
  354. # Ultimately, this will move to toolchain.configure and be turned into a
  355. # cross-platform check.
  356. option(env='LD', nargs=1, help='Path to the linker')
  357. link = check_prog('LINK', depends_win()(lambda: ('link.exe',)), input='LD',
  358. paths=vc_compiler_path)
  359. add_old_configure_assignment('LD', depends_win(link)(lambda x: x))
  360. # Normally, we'd just have CC, etc. set to absolute paths, but the build system
  361. # doesn't currently handle properly the case where the paths contain spaces.
  362. # Additionally, there's the issue described in toolchain.configure, in
  363. # valid_compiler().
  364. @depends_win(sdk_bin_path)
  365. @imports('os')
  366. def alter_path(sdk_bin_path):
  367. path = os.pathsep.join(sdk_bin_path)
  368. os.environ['PATH'] = path
  369. return path
  370. set_config('PATH', alter_path)