windows_toolchain.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. #!/usr/bin/env python2.7
  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. # This script is used to create and manipulate archives containing
  6. # files necessary to build Firefox on Windows (referred to as the
  7. # "Windows toolchain").
  8. #
  9. # When updating behavior of this script, remember to update the docs
  10. # in ``build/docs/toolchains.rst``.
  11. from __future__ import absolute_import, unicode_literals
  12. import hashlib
  13. import os
  14. import sys
  15. from mozpack.files import (
  16. FileFinder,
  17. )
  18. from mozpack.mozjar import (
  19. JarWriter,
  20. )
  21. import mozpack.path as mozpath
  22. # mozpack.match patterns for files under "Microsoft Visual Studio 14.0".
  23. VS_PATTERNS = [
  24. {
  25. 'pattern': 'DIA SDK/bin/**',
  26. 'ignore': (
  27. 'DIA SDK/bin/arm/**',
  28. ),
  29. },
  30. {
  31. 'pattern': 'DIA SDK/idl/**',
  32. },
  33. {
  34. 'pattern': 'DIA SDK/include/**',
  35. },
  36. {
  37. 'pattern': 'DIA SDK/lib/**',
  38. 'ignore': (
  39. 'DIA SDK/lib/arm/**',
  40. ),
  41. },
  42. # ATL is needed by Breakpad.
  43. {
  44. 'pattern': 'VC/atlmfc/include/**',
  45. },
  46. {
  47. 'pattern': 'VC/atlmfc/lib/atls.*',
  48. },
  49. {
  50. 'pattern': 'VC/atlmfc/lib/amd64/atls.*',
  51. },
  52. {
  53. 'pattern': 'VC/bin/**',
  54. # We only care about compiling on amd64 for amd64 or x86 targets.
  55. 'ignore': (
  56. 'VC/bin/amd64_arm/**',
  57. 'VC/bin/arm/**',
  58. 'VC/bin/x86_arm/**',
  59. 'VC/bin/x86_amd64/**',
  60. ),
  61. },
  62. {
  63. 'pattern': 'VC/include/**',
  64. },
  65. {
  66. 'pattern': 'VC/lib/**',
  67. 'ignore': (
  68. 'VC/lib/arm/**',
  69. 'VC/lib/onecore/**',
  70. 'VC/lib/store/**',
  71. ),
  72. },
  73. {
  74. 'pattern': 'VC/redist/x64/Microsoft.VC140.CRT/**',
  75. },
  76. {
  77. 'pattern': 'VC/redist/x86/Microsoft.VC140.CRT/**',
  78. },
  79. ]
  80. SDK_RELEASE = '10.0.14393.0'
  81. # Files from the Windows 10 SDK to install.
  82. SDK_PATTERNS = [
  83. {
  84. 'pattern': 'bin/x64/**',
  85. },
  86. {
  87. 'pattern': 'Include/%s/**' % SDK_RELEASE,
  88. },
  89. {
  90. 'pattern': 'Lib/%s/ucrt/x64/**' % SDK_RELEASE,
  91. },
  92. {
  93. 'pattern': 'Lib/%s/ucrt/x86/**' % SDK_RELEASE,
  94. },
  95. {
  96. 'pattern': 'Lib/%s/um/x64/**' % SDK_RELEASE,
  97. },
  98. {
  99. 'pattern': 'Lib/%s/um/x86/**' % SDK_RELEASE,
  100. },
  101. {
  102. 'pattern': 'Redist/D3D/**',
  103. },
  104. {
  105. 'pattern': 'Redist/ucrt/DLLs/x64/**',
  106. },
  107. {
  108. 'pattern': 'Redist/ucrt/DLLs/x86/**',
  109. },
  110. ]
  111. def find_vs_paths():
  112. """Resolve source locations of files.
  113. Returns a 2-tuple of (Visual Studio Path, SDK Path).
  114. """
  115. pf = os.environ.get('ProgramFiles(x86)')
  116. if not pf:
  117. raise Exception('No "ProgramFiles(x86)" environment variable. '
  118. 'Not running on 64-bit Windows?')
  119. vs_path = os.path.join(pf, 'Microsoft Visual Studio 14.0')
  120. if not os.path.exists(vs_path):
  121. raise Exception('%s does not exist; Visual Studio 2015 not installed?' %
  122. vs_path)
  123. sdk_path = os.path.join(pf, 'Windows Kits', '10')
  124. if not os.path.exists(sdk_path):
  125. raise Exception('%s does not exist; Windows 10 SDK not installed?' %
  126. sdk_path)
  127. return vs_path, sdk_path
  128. def resolve_files():
  129. """Resolve the files that constitute a standalone toolchain.
  130. This is a generator of (dest path, file) where the destination
  131. path is relative and the file instance is a BaseFile from mozpack.
  132. """
  133. vs_path, sdk_path = find_vs_paths()
  134. for entry in VS_PATTERNS:
  135. finder = FileFinder(vs_path, find_executables=False,
  136. ignore=entry.get('ignore', []))
  137. for p, f in finder.find(entry['pattern']):
  138. assert p.startswith(('VC/', 'DIA SDK/'))
  139. yield p.encode('utf-8'), f
  140. for entry in SDK_PATTERNS:
  141. finder = FileFinder(sdk_path, find_executables=False,
  142. ignore=entry.get('ignore', []))
  143. for p, f in finder.find(entry['pattern']):
  144. relpath = 'SDK/%s' % p
  145. yield relpath.encode('utf-8'), f
  146. def resolve_files_and_hash(manifest):
  147. """Resolve files and hash their data.
  148. This is a generator of 3-tuples of (relpath, data, mode).
  149. As data is read, the manifest is populated with metadata.
  150. Keys are set to the relative file path. Values are 2-tuples
  151. of (data length, sha-256).
  152. """
  153. assert manifest == {}
  154. for p, f in resolve_files():
  155. data = f.read()
  156. sha256 = hashlib.sha256()
  157. sha256.update(data)
  158. manifest[p] = (len(data), sha256.hexdigest())
  159. yield p, data, f.mode
  160. def format_manifest(manifest):
  161. """Return formatted SHA-256 manifests as a byte strings."""
  162. sha256_lines = []
  163. for path, (length, sha256) in sorted(manifest.items()):
  164. sha256_lines.append(b'%s\t%d\t%s' % (sha256, length, path))
  165. # Trailing newline.
  166. sha256_lines.append(b'')
  167. return b'\n'.join(sha256_lines)
  168. def write_zip(zip_path, prefix=None):
  169. """Write toolchain data to a zip file."""
  170. if isinstance(prefix, unicode):
  171. prefix = prefix.encode('utf-8')
  172. with JarWriter(file=zip_path, optimize=False, compress=5) as zip:
  173. manifest = {}
  174. for p, data, mode in resolve_files_and_hash(manifest):
  175. print(p)
  176. if prefix:
  177. p = mozpath.join(prefix, p)
  178. zip.add(p, data, mode=mode)
  179. sha256_manifest = format_manifest(manifest)
  180. sdk_path = b'SDK_VERSION'
  181. sha256_path = b'MANIFEST.SHA256'
  182. if prefix:
  183. sdk_path = mozpath.join(prefix, sdk_path)
  184. sha256_path = mozpath.join(prefix, sha256_path)
  185. zip.add(sdk_path, SDK_RELEASE.encode('utf-8'))
  186. zip.add(sha256_path, sha256_manifest)
  187. if __name__ == '__main__':
  188. if len(sys.argv) != 3:
  189. print('usage: %s create-zip <path-prefix>' % sys.argv[0])
  190. sys.exit(1)
  191. assert sys.argv[1] == 'create-zip'
  192. prefix = os.path.basename(sys.argv[2])
  193. destzip = '%s.zip' % sys.argv[2]
  194. write_zip(destzip, prefix=prefix)
  195. sha1 = hashlib.sha1()
  196. sha256 = hashlib.sha256()
  197. sha512 = hashlib.sha512()
  198. with open(destzip, 'rb') as fh:
  199. data = fh.read()
  200. sha1.update(data)
  201. sha256.update(data)
  202. sha512.update(data)
  203. print('Hashes of %s (size=%d)' % (destzip, len(data)))
  204. print('SHA-1: %s' % sha1.hexdigest())
  205. print('SHA-256: %s' % sha256.hexdigest())
  206. print('SHA-512: %s' % sha512.hexdigest())