unpacking_jtest_runner.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import io
  2. import json
  3. import optparse
  4. import os
  5. import sys
  6. import subprocess
  7. import time
  8. import zipfile
  9. import platform
  10. # This script changes test run classpath by unpacking tests.jar -> tests-dir. The goal
  11. # is to launch tests with the same classpath as maven does.
  12. def parse_args():
  13. parser = optparse.OptionParser()
  14. parser.disable_interspersed_args()
  15. parser.add_option('--trace-file')
  16. parser.add_option('--jar-binary')
  17. parser.add_option('--tests-jar-path')
  18. parser.add_option('--classpath-option-type', choices=('manifest', 'command_file', 'list'), default='manifest')
  19. return parser.parse_args()
  20. # temporary, for jdk8/jdk9+ compatibility
  21. def fix_cmd(cmd):
  22. if not cmd:
  23. return cmd
  24. java = cmd[0]
  25. if not java.endswith('java') and not java.endswith('java.exe'):
  26. return cmd
  27. p = subprocess.Popen([java, '-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  28. out, err = p.communicate()
  29. out, err = out.strip(), err.strip()
  30. if ((out or '').strip().startswith('java version "1.8') or (err or '').strip().startswith('java version "1.8')):
  31. res = []
  32. i = 0
  33. while i < len(cmd):
  34. for option in ('--add-exports', '--add-modules'):
  35. if cmd[i] == option:
  36. i += 1
  37. break
  38. elif cmd[i].startswith(option + '='):
  39. break
  40. else:
  41. res.append(cmd[i])
  42. i += 1
  43. return res
  44. return cmd
  45. def dump_event(etype, data, filename):
  46. event = {
  47. 'timestamp': time.time(),
  48. 'value': data,
  49. 'name': etype,
  50. }
  51. with io.open(filename, 'a', encoding='utf8') as afile:
  52. afile.write(unicode(json.dumps(event) + '\n'))
  53. def dump_chunk_event(data, filename):
  54. return dump_event('chunk-event', data, filename)
  55. def extract_jars(dest, archive):
  56. os.makedirs(dest)
  57. with zipfile.ZipFile(archive) as zf:
  58. zf.extractall(dest)
  59. def make_bfg_from_cp(class_path, out):
  60. class_path = ' '.join(
  61. map(lambda path: ('file:/' + path.lstrip('/')) if os.path.isabs(path) else path, class_path)
  62. )
  63. with zipfile.ZipFile(out, 'w') as zf:
  64. lines = []
  65. while class_path:
  66. lines.append(class_path[:60])
  67. class_path = class_path[60:]
  68. if lines:
  69. zf.writestr('META-INF/MANIFEST.MF', 'Manifest-Version: 1.0\nClass-Path: \n ' + '\n '.join(lines) + ' \n\n')
  70. def make_command_file_from_cp(class_path, out):
  71. with open(out, 'w') as cp_file:
  72. cp_file.write(os.pathsep.join(class_path))
  73. def main():
  74. s = time.time()
  75. opts, args = parse_args()
  76. # unpack tests jar
  77. try:
  78. build_root = args[args.index('--build-root') + 1]
  79. dest = os.path.join(build_root, 'test-classes')
  80. except Exception:
  81. build_root = ''
  82. dest = os.path.abspath('test-classes')
  83. extract_jars(dest, opts.tests_jar_path)
  84. metrics = {
  85. 'suite_jtest_extract_jars_(seconds)': time.time() - s,
  86. }
  87. s = time.time()
  88. # fix java classpath
  89. cp_idx = args.index('-classpath')
  90. if args[cp_idx + 1].startswith('@'):
  91. real_name = args[cp_idx + 1][1:]
  92. mf = os.path.join(os.path.dirname(real_name), 'fixed.bfg.jar')
  93. with open(real_name) as origin:
  94. class_path = [os.path.join(build_root, i.strip()) for i in origin]
  95. if opts.tests_jar_path in class_path:
  96. class_path.remove(opts.tests_jar_path)
  97. if opts.classpath_option_type == 'manifest':
  98. make_bfg_from_cp(class_path, mf)
  99. mf = os.pathsep.join([dest, mf])
  100. elif opts.classpath_option_type == 'command_file':
  101. mf = os.path.splitext(mf)[0] + '.txt'
  102. make_command_file_from_cp([dest] + class_path, mf)
  103. mf = "@" + mf
  104. elif opts.classpath_option_type == 'list':
  105. mf = os.pathsep.join([dest] + class_path)
  106. else:
  107. raise Exception("Unexpected classpath option type: " + opts.classpath_option_type)
  108. args = fix_cmd(args[:cp_idx + 1]) + [mf] + args[cp_idx + 2:]
  109. else:
  110. args[cp_idx + 1] = args[cp_idx + 1].replace(opts.tests_jar_path, dest)
  111. args = fix_cmd(args[:cp_idx]) + args[cp_idx:]
  112. metrics['suite_jtest_fix_classpath_(seconds)'] = time.time() - s
  113. if opts.trace_file:
  114. dump_chunk_event({'metrics': metrics}, opts.trace_file)
  115. # run java cmd
  116. if platform.system() == 'Windows':
  117. sys.exit(subprocess.Popen(args).wait())
  118. else:
  119. os.execv(args[0], args)
  120. if __name__ == '__main__':
  121. main()