build.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. #!/usr/bin/env python3
  2. import argparse
  3. import sys
  4. import os
  5. import glob
  6. import subprocess
  7. import shutil
  8. import multiprocessing as mp
  9. SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
  10. INTERPRETER_DIR = os.path.join(SCRIPT_DIR, '..', 'interpreter')
  11. WASM_EXEC = os.path.join(INTERPRETER_DIR, 'wasm')
  12. WAST_TESTS_DIR = os.path.join(SCRIPT_DIR, 'core')
  13. JS_TESTS_DIR = os.path.join(SCRIPT_DIR, 'js-api')
  14. HTML_TESTS_DIR = os.path.join(SCRIPT_DIR, 'html')
  15. HARNESS_DIR = os.path.join(SCRIPT_DIR, 'harness')
  16. HARNESS_FILES = ['testharness.js', 'testharnessreport.js', 'testharness.css']
  17. WPT_URL_PREFIX = '/resources'
  18. # Helpers.
  19. def run(*cmd):
  20. return subprocess.call(cmd,
  21. stdout=subprocess.PIPE,
  22. stderr=subprocess.STDOUT,
  23. universal_newlines=True)
  24. # Preconditions.
  25. def ensure_remove_dir(path):
  26. if os.path.exists(path):
  27. shutil.rmtree(path)
  28. def ensure_empty_dir(path):
  29. ensure_remove_dir(path)
  30. os.mkdir(path)
  31. def compile_wasm_interpreter():
  32. print("Recompiling the wasm interpreter...")
  33. result = run('make', '-C', INTERPRETER_DIR, 'clean', 'default')
  34. if result != 0:
  35. print("Couldn't recompile wasm spec interpreter")
  36. sys.exit(1)
  37. print("Done!")
  38. def ensure_wasm_executable(path_to_wasm):
  39. """
  40. Ensure we have built the wasm spec interpreter.
  41. """
  42. result = run(path_to_wasm, '-v', '-e', '')
  43. if result != 0:
  44. print('Unable to run the wasm executable')
  45. sys.exit(1)
  46. # JS harness.
  47. def convert_one_wast_file(inputs):
  48. wast_file, js_file = inputs
  49. print('Compiling {} to JS...'.format(wast_file))
  50. return run(WASM_EXEC, wast_file, '-h', '-o', js_file)
  51. def convert_wast_to_js(out_js_dir):
  52. """Compile all the wast files to JS and store the results in the JS dir."""
  53. inputs = []
  54. for wast_file in glob.glob(os.path.join(WAST_TESTS_DIR, '*.wast')):
  55. # Don't try to compile tests that are supposed to fail.
  56. if '.fail.' in wast_file:
  57. continue
  58. js_filename = os.path.basename(wast_file) + '.js'
  59. js_file = os.path.join(out_js_dir, js_filename)
  60. inputs.append((wast_file, js_file))
  61. pool = mp.Pool(processes=8)
  62. for result in pool.imap_unordered(convert_one_wast_file, inputs):
  63. if result != 0:
  64. print('Error when compiling {} to JS: {}', wast_file, result.stdout)
  65. def build_js(out_js_dir, include_harness=False):
  66. print('Building JS...')
  67. convert_wast_to_js(out_js_dir)
  68. print('Copying JS tests to the JS out dir...')
  69. for path in os.listdir(JS_TESTS_DIR):
  70. abspath = os.path.join(JS_TESTS_DIR, path)
  71. if os.path.isdir(abspath):
  72. shutil.copytree(abspath, os.path.join(out_js_dir, path))
  73. else:
  74. shutil.copy(abspath, out_js_dir)
  75. harness_dir = os.path.join(out_js_dir, 'harness')
  76. ensure_empty_dir(harness_dir)
  77. print('Copying JS test harness to the JS out dir...')
  78. for js_file in glob.glob(os.path.join(HARNESS_DIR, '*')):
  79. if os.path.basename(js_file) in HARNESS_FILES and not include_harness:
  80. continue
  81. shutil.copy(js_file, harness_dir)
  82. print('Done building JS.')
  83. # HTML harness.
  84. HTML_HEADER = """<!doctype html>
  85. <html>
  86. <head>
  87. <meta charset="UTF-8">
  88. <title>WebAssembly Web Platform Test</title>
  89. </head>
  90. <body>
  91. <script src={WPT_PREFIX}/testharness.js></script>
  92. <script src={WPT_PREFIX}/testharnessreport.js></script>
  93. <script src={PREFIX}/{JS_HARNESS}></script>
  94. <script src={PREFIX}/wasm-constants.js></script>
  95. <script src={PREFIX}/wasm-module-builder.js></script>
  96. <div id=log></div>
  97. """
  98. HTML_BOTTOM = """
  99. </body>
  100. </html>
  101. """
  102. def wrap_single_test(js_file):
  103. test_func_name = os.path.basename(js_file).replace('.', '_').replace('-', '_')
  104. content = ["(function {}() {{".format(test_func_name)]
  105. with open(js_file, 'r') as f:
  106. content += f.readlines()
  107. content.append('reinitializeRegistry();')
  108. content.append('})();')
  109. with open(js_file, 'w') as f:
  110. f.write('\n'.join(content))
  111. def build_html_js(out_dir):
  112. ensure_empty_dir(out_dir)
  113. build_js(out_dir, True)
  114. for js_file in glob.glob(os.path.join(HTML_TESTS_DIR, '*.js')):
  115. shutil.copy(js_file, out_dir)
  116. for js_file in glob.glob(os.path.join(out_dir, '*.js')):
  117. wrap_single_test(js_file)
  118. def build_html_from_js(js_html_dir, html_dir, use_sync):
  119. for js_file in glob.glob(os.path.join(js_html_dir, '*.js')):
  120. js_filename = os.path.basename(js_file)
  121. html_filename = js_filename + '.html'
  122. html_file = os.path.join(html_dir, html_filename)
  123. js_harness = "sync_index.js" if use_sync else "async_index.js"
  124. with open(html_file, 'w+') as f:
  125. content = HTML_HEADER.replace('{PREFIX}', './js/harness') \
  126. .replace('{WPT_PREFIX}', './js/harness') \
  127. .replace('{JS_HARNESS}', js_harness)
  128. content += " <script src=./js/{SCRIPT}></script>".replace('{SCRIPT}', js_filename)
  129. content += HTML_BOTTOM
  130. f.write(content)
  131. def build_html(html_dir, js_dir, use_sync):
  132. print("Building HTML tests...")
  133. js_html_dir = os.path.join(html_dir, 'js')
  134. build_html_js(js_html_dir)
  135. print('Building WPT tests from JS tests...')
  136. build_html_from_js(js_html_dir, html_dir, use_sync)
  137. print("Done building HTML tests.")
  138. # Front page harness.
  139. def build_front_page(out_dir, js_dir, use_sync):
  140. print('Building front page containing all the HTML tests...')
  141. js_out_dir = os.path.join(out_dir, 'js')
  142. build_html_js(js_out_dir)
  143. front_page = os.path.join(out_dir, 'index.html')
  144. js_harness = "sync_index.js" if use_sync else "async_index.js"
  145. with open(front_page, 'w+') as f:
  146. content = HTML_HEADER.replace('{PREFIX}', './js/harness') \
  147. .replace('{WPT_PREFIX}', './js/harness')\
  148. .replace('{JS_HARNESS}', js_harness)
  149. for js_file in glob.glob(os.path.join(js_out_dir, '*.js')):
  150. filename = os.path.basename(js_file)
  151. content += " <script src=./js/{SCRIPT}></script>\n".replace('{SCRIPT}', filename)
  152. content += HTML_BOTTOM
  153. f.write(content)
  154. print('Done building front page!')
  155. # Main program.
  156. def process_args():
  157. parser = argparse.ArgumentParser(description="Helper tool to build the\
  158. multi-stage cross-browser test suite for WebAssembly.")
  159. parser.add_argument('--js',
  160. dest="js_dir",
  161. help="Relative path to the output directory for the pure JS tests.",
  162. type=str)
  163. parser.add_argument('--html',
  164. dest="html_dir",
  165. help="Relative path to the output directory for the Web Platform tests.",
  166. type=str)
  167. parser.add_argument('--front',
  168. dest="front_dir",
  169. help="Relative path to the output directory for the front page.",
  170. type=str)
  171. parser.add_argument('--dont-recompile',
  172. action="store_const",
  173. dest="compile",
  174. help="Don't recompile the wasm spec interpreter (by default, it is)",
  175. const=False,
  176. default=True)
  177. parser.add_argument('--use-sync',
  178. action="store_const",
  179. dest="use_sync",
  180. help="Let the tests use the synchronous JS API (by default, it does not)",
  181. const=True,
  182. default=False)
  183. return parser.parse_args(), parser
  184. if __name__ == '__main__':
  185. args, parser = process_args()
  186. js_dir = args.js_dir
  187. html_dir = args.html_dir
  188. front_dir = args.front_dir
  189. if front_dir is None and js_dir is None and html_dir is None:
  190. print('At least one mode must be selected.\n')
  191. parser.print_help()
  192. sys.exit(1)
  193. if args.compile:
  194. compile_wasm_interpreter()
  195. ensure_wasm_executable(WASM_EXEC)
  196. if js_dir is not None:
  197. ensure_empty_dir(js_dir)
  198. build_js(js_dir)
  199. if html_dir is not None:
  200. ensure_empty_dir(html_dir)
  201. build_html(html_dir, js_dir, args.use_sync)
  202. if front_dir is not None:
  203. ensure_empty_dir(front_dir)
  204. build_front_page(front_dir, js_dir, args.use_sync)
  205. print('Done!')