go_tool.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  1. from __future__ import absolute_import, unicode_literals
  2. import argparse
  3. import codecs
  4. import copy
  5. import json
  6. import os
  7. import re
  8. import shutil
  9. import subprocess
  10. import sys
  11. import tarfile
  12. import tempfile
  13. import threading
  14. import six
  15. from functools import reduce
  16. import process_command_files as pcf
  17. import process_whole_archive_option as pwa
  18. arc_project_prefix = 'a.yandex-team.ru/'
  19. std_lib_prefix = 'contrib/go/_std_1.18/src/'
  20. vendor_prefix = 'vendor/'
  21. vet_info_ext = '.vet.out'
  22. vet_report_ext = '.vet.txt'
  23. FIXED_CGO1_SUFFIX='.fixed.cgo1.go'
  24. COMPILE_OPTIMIZATION_FLAGS=('-N',)
  25. def get_trimpath_args(args):
  26. return ['-trimpath', args.trimpath] if args.trimpath else []
  27. def preprocess_cgo1(src_path, dst_path, source_root):
  28. with open(src_path, 'r') as f:
  29. content = f.read()
  30. content = content.replace('__ARCADIA_SOURCE_ROOT_PREFIX__', source_root)
  31. with open(dst_path, 'w') as f:
  32. f.write(content)
  33. def preprocess_args(args):
  34. # Temporary work around for noauto
  35. if args.cgo_srcs and len(args.cgo_srcs) > 0:
  36. cgo_srcs_set = set(args.cgo_srcs)
  37. args.srcs = [x for x in args.srcs if x not in cgo_srcs_set]
  38. args.pkg_root = os.path.join(args.toolchain_root, 'pkg')
  39. toolchain_tool_root = os.path.join(args.pkg_root, 'tool', '{}_{}'.format(args.host_os, args.host_arch))
  40. args.go_compile = os.path.join(toolchain_tool_root, 'compile')
  41. args.go_cgo = os.path.join(toolchain_tool_root, 'cgo')
  42. args.go_link = os.path.join(toolchain_tool_root, 'link')
  43. args.go_asm = os.path.join(toolchain_tool_root, 'asm')
  44. args.go_pack = os.path.join(toolchain_tool_root, 'pack')
  45. args.go_vet = os.path.join(toolchain_tool_root, 'vet') if args.vet is True else args.vet
  46. args.output = os.path.normpath(args.output)
  47. args.vet_report_output = vet_report_output_name(args.output, args.vet_report_ext)
  48. args.trimpath = None
  49. if args.debug_root_map:
  50. roots = {'build': args.build_root, 'source': args.source_root, 'tools': args.tools_root}
  51. replaces = []
  52. for root in args.debug_root_map.split(';'):
  53. src, dst = root.split('=', 1)
  54. assert src in roots
  55. replaces.append('{}=>{}'.format(roots[src], dst))
  56. del roots[src]
  57. assert len(replaces) > 0
  58. args.trimpath = ';'.join(replaces)
  59. args.build_root = os.path.normpath(args.build_root)
  60. args.build_root_dir = args.build_root + os.path.sep
  61. args.source_root = os.path.normpath(args.source_root)
  62. args.source_root_dir = args.source_root + os.path.sep
  63. args.output_root = os.path.normpath(args.output_root)
  64. args.import_map = {}
  65. args.module_map = {}
  66. if args.cgo_peers:
  67. args.cgo_peers = [x for x in args.cgo_peers if not x.endswith('.fake.pkg')]
  68. srcs = []
  69. for f in args.srcs:
  70. if f.endswith('.gosrc'):
  71. with tarfile.open(f, 'r') as tar:
  72. srcs.extend(os.path.join(args.output_root, src) for src in tar.getnames())
  73. tar.extractall(path=args.output_root)
  74. else:
  75. srcs.append(f)
  76. args.srcs = srcs
  77. assert args.mode == 'test' or args.test_srcs is None and args.xtest_srcs is None
  78. # add lexical oreder by basename for go sources
  79. args.srcs.sort(key=lambda x: os.path.basename(x))
  80. if args.test_srcs:
  81. args.srcs += sorted(args.test_srcs, key=lambda x: os.path.basename(x))
  82. del args.test_srcs
  83. if args.xtest_srcs:
  84. args.xtest_srcs.sort(key=lambda x: os.path.basename(x))
  85. # compute root relative module dir path
  86. assert args.output is None or args.output_root == os.path.dirname(args.output)
  87. assert args.output_root.startswith(args.build_root_dir)
  88. args.module_path = args.output_root[len(args.build_root_dir):]
  89. args.source_module_dir = os.path.join(args.source_root, args.test_import_path or args.module_path) + os.path.sep
  90. assert len(args.module_path) > 0
  91. args.import_path, args.is_std = get_import_path(args.module_path)
  92. assert args.asmhdr is None or args.word == 'go'
  93. srcs = []
  94. for f in args.srcs:
  95. if f.endswith(FIXED_CGO1_SUFFIX) and f.startswith(args.build_root_dir):
  96. path = os.path.join(args.output_root, '{}.cgo1.go'.format(os.path.basename(f[:-len(FIXED_CGO1_SUFFIX)])))
  97. srcs.append(path)
  98. preprocess_cgo1(f, path, args.source_root)
  99. else:
  100. srcs.append(f)
  101. args.srcs = srcs
  102. if args.extldflags:
  103. args.extldflags = pwa.ProcessWholeArchiveOption(args.targ_os).construct_cmd(args.extldflags)
  104. classify_srcs(args.srcs, args)
  105. def compare_versions(version1, version2):
  106. def last_index(version):
  107. index = version.find('beta')
  108. return len(version) if index < 0 else index
  109. v1 = tuple(x.zfill(8) for x in version1[:last_index(version1)].split('.'))
  110. v2 = tuple(x.zfill(8) for x in version2[:last_index(version2)].split('.'))
  111. if v1 == v2:
  112. return 0
  113. return 1 if v1 < v2 else -1
  114. def get_symlink_or_copyfile():
  115. os_symlink = getattr(os, 'symlink', None)
  116. if os_symlink is None:
  117. os_symlink = shutil.copyfile
  118. return os_symlink
  119. def copy_args(args):
  120. return copy.copy(args)
  121. def get_vendor_index(import_path):
  122. index = import_path.rfind('/' + vendor_prefix)
  123. if index < 0:
  124. index = 0 if import_path.startswith(vendor_prefix) else index
  125. else:
  126. index = index + 1
  127. return index
  128. def get_import_path(module_path):
  129. assert len(module_path) > 0
  130. import_path = module_path.replace('\\', '/')
  131. is_std_module = import_path.startswith(std_lib_prefix)
  132. if is_std_module:
  133. import_path = import_path[len(std_lib_prefix):]
  134. elif import_path.startswith(vendor_prefix):
  135. import_path = import_path[len(vendor_prefix):]
  136. else:
  137. import_path = arc_project_prefix + import_path
  138. assert len(import_path) > 0
  139. return import_path, is_std_module
  140. def call(cmd, cwd, env=None):
  141. # sys.stderr.write('{}\n'.format(' '.join(cmd)))
  142. return subprocess.check_output(cmd, stdin=None, stderr=subprocess.STDOUT, cwd=cwd, env=env)
  143. def classify_srcs(srcs, args):
  144. args.go_srcs = [x for x in srcs if x.endswith('.go')]
  145. args.asm_srcs = [x for x in srcs if x.endswith('.s')]
  146. args.objects = [x for x in srcs if x.endswith('.o') or x.endswith('.obj')]
  147. args.symabis = [x for x in srcs if x.endswith('.symabis')]
  148. args.sysos = [x for x in srcs if x.endswith('.syso')]
  149. def get_import_config_info(peers, gen_importmap, import_map={}, module_map={}):
  150. info = {'importmap': [], 'packagefile': [], 'standard': {}}
  151. if gen_importmap:
  152. for key, value in six.iteritems(import_map):
  153. info['importmap'].append((key, value))
  154. for peer in peers:
  155. peer_import_path, is_std = get_import_path(os.path.dirname(peer))
  156. if gen_importmap:
  157. index = get_vendor_index(peer_import_path)
  158. if index >= 0:
  159. index += len(vendor_prefix)
  160. info['importmap'].append((peer_import_path[index:], peer_import_path))
  161. info['packagefile'].append((peer_import_path, os.path.join(args.build_root, peer)))
  162. if is_std:
  163. info['standard'][peer_import_path] = True
  164. for key, value in six.iteritems(module_map):
  165. info['packagefile'].append((key, value))
  166. return info
  167. def create_import_config(peers, gen_importmap, import_map={}, module_map={}):
  168. lines = []
  169. info = get_import_config_info(peers, gen_importmap, import_map, module_map)
  170. for key in ('importmap', 'packagefile'):
  171. for item in info[key]:
  172. lines.append('{} {}={}'.format(key, *item))
  173. if len(lines) > 0:
  174. lines.append('')
  175. content = '\n'.join(lines)
  176. # sys.stderr.writelines('{}\n'.format(l) for l in lines)
  177. with tempfile.NamedTemporaryFile(delete=False) as f:
  178. f.write(content.encode('UTF-8'))
  179. return f.name
  180. return None
  181. def create_embed_config(args):
  182. data = {
  183. 'Patterns': {},
  184. 'Files': {},
  185. }
  186. for info in args.embed:
  187. pattern = info[0]
  188. if pattern.endswith('/**/*'):
  189. pattern = pattern[:-3]
  190. files = {os.path.relpath(f, args.source_module_dir).replace('\\', '/'): f for f in info[1:]}
  191. data['Patterns'][pattern] = list(files.keys())
  192. data['Files'].update(files)
  193. # sys.stderr.write('{}\n'.format(json.dumps(data, indent=4)))
  194. with tempfile.NamedTemporaryFile(delete=False, suffix='.embedcfg') as f:
  195. f.write(json.dumps(data).encode('UTF-8'))
  196. return f.name
  197. def vet_info_output_name(path, ext=None):
  198. return '{}{}'.format(path, ext or vet_info_ext)
  199. def vet_report_output_name(path, ext=None):
  200. return '{}{}'.format(path, ext or vet_report_ext)
  201. def get_source_path(args):
  202. return args.test_import_path or args.module_path
  203. def gen_vet_info(args):
  204. import_path = args.real_import_path if hasattr(args, 'real_import_path') else args.import_path
  205. info = get_import_config_info(args.peers, True, args.import_map, args.module_map)
  206. import_map = dict(info['importmap'])
  207. # FIXME(snermolaev): it seems that adding import map for 'fake' package
  208. # does't make any harm (it needs to be revised later)
  209. import_map['unsafe'] = 'unsafe'
  210. for (key, _) in info['packagefile']:
  211. if key not in import_map:
  212. import_map[key] = key
  213. data = {
  214. 'ID': import_path,
  215. 'Compiler': 'gc',
  216. 'Dir': os.path.join(args.source_root, get_source_path(args)),
  217. 'ImportPath': import_path,
  218. 'GoFiles': [x for x in args.go_srcs if x.endswith('.go')],
  219. 'NonGoFiles': [x for x in args.go_srcs if not x.endswith('.go')],
  220. 'ImportMap': import_map,
  221. 'PackageFile': dict(info['packagefile']),
  222. 'Standard': dict(info['standard']),
  223. 'PackageVetx': dict((key, vet_info_output_name(value)) for key, value in info['packagefile']),
  224. 'VetxOnly': False,
  225. 'VetxOutput': vet_info_output_name(args.output),
  226. 'SucceedOnTypecheckFailure': False
  227. }
  228. # sys.stderr.write('{}\n'.format(json.dumps(data, indent=4)))
  229. return data
  230. def create_vet_config(args, info):
  231. with tempfile.NamedTemporaryFile(delete=False, suffix='.cfg') as f:
  232. f.write(json.dumps(info).encode('UTF-8'))
  233. return f.name
  234. def decode_vet_report(json_report):
  235. report = ''
  236. if json_report:
  237. try:
  238. full_diags = json.JSONDecoder().decode(json_report.decode('UTF-8'))
  239. except ValueError:
  240. report = json_report
  241. else:
  242. messages = []
  243. for _, module_diags in six.iteritems(full_diags):
  244. for _, type_diags in six.iteritems(module_diags):
  245. for diag in type_diags:
  246. messages.append('{}: {}'.format(diag['posn'], json.dumps(diag['message'])))
  247. report = '\n'.join(messages)
  248. return report
  249. def dump_vet_report(args, report):
  250. if report:
  251. report = report.replace(args.build_root, '$B')
  252. report = report.replace(args.source_root, '$S')
  253. with open(args.vet_report_output, 'w') as f:
  254. f.write(report)
  255. def read_vet_report(args):
  256. assert args
  257. report = ''
  258. if os.path.exists(args.vet_report_output):
  259. with open(args.vet_report_output, 'r') as f:
  260. report += f.read()
  261. return report
  262. def dump_vet_report_for_tests(args, *test_args_list):
  263. dump_vet_report(args, reduce(lambda x, y: x + read_vet_report(y), [_f for _f in test_args_list if _f], ''))
  264. def do_vet(args):
  265. assert args.vet
  266. info = gen_vet_info(args)
  267. vet_config = create_vet_config(args, info)
  268. cmd = [args.go_vet, '-json']
  269. if args.vet_flags:
  270. cmd.extend(args.vet_flags)
  271. cmd.append(vet_config)
  272. # sys.stderr.write('>>>> [{}]\n'.format(' '.join(cmd)))
  273. p_vet = subprocess.Popen(cmd, stdin=None, stderr=subprocess.PIPE, stdout=subprocess.PIPE, cwd=args.source_root)
  274. vet_out, vet_err = p_vet.communicate()
  275. report = decode_vet_report(vet_out) if vet_out else ''
  276. dump_vet_report(args, report)
  277. if p_vet.returncode:
  278. raise subprocess.CalledProcessError(returncode=p_vet.returncode, cmd=cmd, output=vet_err)
  279. def _do_compile_go(args):
  280. import_path, is_std_module = args.import_path, args.is_std
  281. cmd = [
  282. args.go_compile,
  283. '-o',
  284. args.output,
  285. '-p',
  286. import_path,
  287. '-D',
  288. '""',
  289. '-goversion',
  290. 'go{}'.format(args.goversion)
  291. ]
  292. cmd.extend(get_trimpath_args(args))
  293. compiling_runtime = False
  294. if is_std_module:
  295. cmd.append('-std')
  296. if import_path in ('runtime', 'internal/abi', 'internal/bytealg', 'internal/cpu') or import_path.startswith('runtime/internal/'):
  297. cmd.append('-+')
  298. compiling_runtime = True
  299. import_config_name = create_import_config(args.peers, True, args.import_map, args.module_map)
  300. if import_config_name:
  301. cmd += ['-importcfg', import_config_name]
  302. else:
  303. if import_path == 'unsafe' or len(args.objects) > 0 or args.asmhdr:
  304. pass
  305. else:
  306. cmd.append('-complete')
  307. # if compare_versions('1.16', args.goversion) >= 0:
  308. if args.embed:
  309. embed_config_name = create_embed_config(args)
  310. cmd.extend(['-embedcfg', embed_config_name])
  311. if args.asmhdr:
  312. cmd += ['-asmhdr', args.asmhdr]
  313. # Use .symabis (starting from 1.12 version)
  314. if args.symabis:
  315. cmd += ['-symabis'] + args.symabis
  316. # If 1.12 <= version < 1.13 we have to pass -allabis for 'runtime' and 'runtime/internal/atomic'
  317. # if compare_versions('1.13', args.goversion) >= 0:
  318. # pass
  319. # elif import_path in ('runtime', 'runtime/internal/atomic'):
  320. # cmd.append('-allabis')
  321. compile_workers = '4'
  322. if args.compile_flags:
  323. if compiling_runtime:
  324. cmd.extend(x for x in args.compile_flags if x not in COMPILE_OPTIMIZATION_FLAGS)
  325. else:
  326. cmd.extend(args.compile_flags)
  327. if any([x in ('-race', '-shared') for x in args.compile_flags]):
  328. compile_workers = '1'
  329. cmd += ['-pack', '-c={}'.format(compile_workers)]
  330. cmd += args.go_srcs
  331. call(cmd, args.build_root)
  332. class VetThread(threading.Thread):
  333. def __init__(self, target, args):
  334. super(VetThread, self).__init__(target=target, args=args)
  335. self.exc_info = None
  336. def run(self):
  337. try:
  338. super(VetThread, self).run()
  339. except:
  340. self.exc_info = sys.exc_info()
  341. def join_with_exception(self, reraise_exception):
  342. self.join()
  343. if reraise_exception and self.exc_info:
  344. six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
  345. def do_compile_go(args):
  346. raise_exception_from_vet = False
  347. if args.vet:
  348. run_vet = VetThread(target=do_vet, args=(args,))
  349. run_vet.start()
  350. try:
  351. _do_compile_go(args)
  352. raise_exception_from_vet = True
  353. finally:
  354. if args.vet:
  355. run_vet.join_with_exception(raise_exception_from_vet)
  356. def do_compile_asm(args):
  357. def need_compiling_runtime(import_path):
  358. return import_path in ('runtime', 'reflect', 'syscall') or \
  359. import_path.startswith('runtime/internal/') or \
  360. compare_versions('1.17', args.goversion) >= 0 and import_path == 'internal/bytealg'
  361. assert(len(args.srcs) == 1 and len(args.asm_srcs) == 1)
  362. cmd = [args.go_asm]
  363. cmd += get_trimpath_args(args)
  364. cmd += ['-I', args.output_root, '-I', os.path.join(args.pkg_root, 'include')]
  365. cmd += ['-D', 'GOOS_' + args.targ_os, '-D', 'GOARCH_' + args.targ_arch, '-o', args.output]
  366. # if compare_versions('1.16', args.goversion) >= 0:
  367. cmd += ['-p', args.import_path]
  368. if need_compiling_runtime(args.import_path):
  369. cmd += ['-compiling-runtime']
  370. if args.asm_flags:
  371. cmd += args.asm_flags
  372. cmd += args.asm_srcs
  373. call(cmd, args.build_root)
  374. def do_link_lib(args):
  375. if len(args.asm_srcs) > 0:
  376. asmargs = copy_args(args)
  377. asmargs.asmhdr = os.path.join(asmargs.output_root, 'go_asm.h')
  378. do_compile_go(asmargs)
  379. for src in asmargs.asm_srcs:
  380. asmargs.srcs = [src]
  381. asmargs.asm_srcs = [src]
  382. asmargs.output = os.path.join(asmargs.output_root, os.path.basename(src) + '.o')
  383. do_compile_asm(asmargs)
  384. args.objects.append(asmargs.output)
  385. else:
  386. do_compile_go(args)
  387. if args.objects or args.sysos:
  388. cmd = [args.go_pack, 'r', args.output] + args.objects + args.sysos
  389. call(cmd, args.build_root)
  390. def do_link_exe(args):
  391. assert args.extld is not None
  392. assert args.non_local_peers is not None
  393. compile_args = copy_args(args)
  394. compile_args.output = os.path.join(args.output_root, 'main.a')
  395. compile_args.real_import_path = compile_args.import_path
  396. compile_args.import_path = 'main'
  397. if args.vcs and os.path.isfile(compile_args.vcs):
  398. build_info = os.path.join('library', 'go', 'core', 'buildinfo')
  399. if any([x.startswith(build_info) for x in compile_args.peers]):
  400. compile_args.go_srcs.append(compile_args.vcs)
  401. do_link_lib(compile_args)
  402. cmd = [args.go_link, '-o', args.output]
  403. import_config_name = create_import_config(args.peers + args.non_local_peers, False, args.import_map, args.module_map)
  404. if import_config_name:
  405. cmd += ['-importcfg', import_config_name]
  406. if args.link_flags:
  407. cmd += args.link_flags
  408. if args.mode in ('exe', 'test'):
  409. cmd.append('-buildmode=exe')
  410. elif args.mode == 'dll':
  411. cmd.append('-buildmode=c-shared')
  412. else:
  413. assert False, 'Unexpected mode: {}'.format(args.mode)
  414. cmd.append('-extld={}'.format(args.extld))
  415. extldflags = []
  416. if args.extldflags is not None:
  417. filter_musl = bool
  418. if args.musl:
  419. cmd.append('-linkmode=external')
  420. extldflags.append('-static')
  421. filter_musl = lambda x: x not in ('-lc', '-ldl', '-lm', '-lpthread', '-lrt')
  422. extldflags += [x for x in args.extldflags if filter_musl(x)]
  423. cgo_peers = []
  424. if args.cgo_peers is not None and len(args.cgo_peers) > 0:
  425. is_group = args.targ_os == 'linux'
  426. if is_group:
  427. cgo_peers.append('-Wl,--start-group')
  428. cgo_peers.extend(args.cgo_peers)
  429. if is_group:
  430. cgo_peers.append('-Wl,--end-group')
  431. try:
  432. index = extldflags.index('--cgo-peers')
  433. extldflags = extldflags[:index] + cgo_peers + extldflags[index+1:]
  434. except ValueError:
  435. extldflags.extend(cgo_peers)
  436. if len(extldflags) > 0:
  437. cmd.append('-extldflags={}'.format(' '.join(extldflags)))
  438. cmd.append(compile_args.output)
  439. call(cmd, args.build_root)
  440. def gen_cover_info(args):
  441. lines = []
  442. lines.extend([
  443. """
  444. var (
  445. coverCounters = make(map[string][]uint32)
  446. coverBlocks = make(map[string][]testing.CoverBlock)
  447. )
  448. """,
  449. 'func init() {',
  450. ])
  451. for var, file in (x.split(':') for x in args.cover_info):
  452. lines.append(' coverRegisterFile("{file}", _cover0.{var}.Count[:], _cover0.{var}.Pos[:], _cover0.{var}.NumStmt[:])'.format(file=file, var=var))
  453. lines.extend([
  454. '}',
  455. """
  456. func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) {
  457. if 3*len(counter) != len(pos) || len(counter) != len(numStmts) {
  458. panic("coverage: mismatched sizes")
  459. }
  460. if coverCounters[fileName] != nil {
  461. // Already registered.
  462. return
  463. }
  464. coverCounters[fileName] = counter
  465. block := make([]testing.CoverBlock, len(counter))
  466. for i := range counter {
  467. block[i] = testing.CoverBlock{
  468. Line0: pos[3*i+0],
  469. Col0: uint16(pos[3*i+2]),
  470. Line1: pos[3*i+1],
  471. Col1: uint16(pos[3*i+2]>>16),
  472. Stmts: numStmts[i],
  473. }
  474. }
  475. coverBlocks[fileName] = block
  476. }
  477. """,
  478. ])
  479. return lines
  480. def filter_out_skip_tests(tests, skip_tests):
  481. skip_set = set()
  482. star_skip_set = set()
  483. for t in skip_tests:
  484. work_set = star_skip_set if '*' in t else skip_set
  485. work_set.add(t)
  486. re_star_tests = None
  487. if len(star_skip_set) > 0:
  488. re_star_tests = re.compile(re.sub(r'(\*)+', r'.\1', '^({})$'.format('|'.join(star_skip_set))))
  489. return [x for x in tests if not (x in skip_tests or re_star_tests and re_star_tests.match(x))]
  490. def gen_test_main(args, test_lib_args, xtest_lib_args):
  491. assert args and (test_lib_args or xtest_lib_args)
  492. test_miner = args.test_miner
  493. test_module_path = test_lib_args.import_path if test_lib_args else xtest_lib_args.import_path
  494. is_cover = args.cover_info and len(args.cover_info) > 0
  495. # Prepare GOPATH
  496. # $BINDIR
  497. # |- __go__
  498. # |- src
  499. # |- pkg
  500. # |- ${TARGET_OS}_${TARGET_ARCH}
  501. go_path_root = os.path.join(args.output_root, '__go__')
  502. test_src_dir = os.path.join(go_path_root, 'src')
  503. target_os_arch = '_'.join([args.targ_os, args.targ_arch])
  504. test_pkg_dir = os.path.join(go_path_root, 'pkg', target_os_arch, os.path.dirname(test_module_path))
  505. os.makedirs(test_pkg_dir)
  506. my_env = os.environ.copy()
  507. my_env['GOROOT'] = ''
  508. my_env['GOPATH'] = go_path_root
  509. my_env['GOARCH'] = args.targ_arch
  510. my_env['GOOS'] = args.targ_os
  511. tests = []
  512. xtests = []
  513. os_symlink = get_symlink_or_copyfile()
  514. # Get the list of "internal" tests
  515. if test_lib_args:
  516. os.makedirs(os.path.join(test_src_dir, test_module_path))
  517. os_symlink(test_lib_args.output, os.path.join(test_pkg_dir, os.path.basename(test_module_path) + '.a'))
  518. cmd = [test_miner, '-benchmarks', '-tests', test_module_path]
  519. tests = [x for x in (call(cmd, test_lib_args.output_root, my_env).decode('UTF-8') or '').strip().split('\n') if len(x) > 0]
  520. if args.skip_tests:
  521. tests = filter_out_skip_tests(tests, args.skip_tests)
  522. test_main_found = '#TestMain' in tests
  523. # Get the list of "external" tests
  524. if xtest_lib_args:
  525. xtest_module_path = xtest_lib_args.import_path
  526. os.makedirs(os.path.join(test_src_dir, xtest_module_path))
  527. os_symlink(xtest_lib_args.output, os.path.join(test_pkg_dir, os.path.basename(xtest_module_path) + '.a'))
  528. cmd = [test_miner, '-benchmarks', '-tests', xtest_module_path]
  529. xtests = [x for x in (call(cmd, xtest_lib_args.output_root, my_env).decode('UTF-8') or '').strip().split('\n') if len(x) > 0]
  530. if args.skip_tests:
  531. xtests = filter_out_skip_tests(xtests, args.skip_tests)
  532. xtest_main_found = '#TestMain' in xtests
  533. test_main_package = None
  534. if test_main_found and xtest_main_found:
  535. assert False, 'multiple definition of TestMain'
  536. elif test_main_found:
  537. test_main_package = '_test'
  538. elif xtest_main_found:
  539. test_main_package = '_xtest'
  540. shutil.rmtree(go_path_root)
  541. lines = ['package main', '', 'import (']
  542. if test_main_package is None:
  543. lines.append(' "os"')
  544. lines.extend([' "testing"', ' "testing/internal/testdeps"'])
  545. if len(tests) > 0:
  546. lines.append(' _test "{}"'.format(test_module_path))
  547. elif test_lib_args:
  548. lines.append(' _ "{}"'.format(test_module_path))
  549. if len(xtests) > 0:
  550. lines.append(' _xtest "{}"'.format(xtest_module_path))
  551. elif xtest_lib_args:
  552. lines.append(' _ "{}"'.format(xtest_module_path))
  553. if is_cover:
  554. lines.append(' _cover0 "{}"'.format(test_module_path))
  555. lines.extend([')', ''])
  556. if compare_versions('1.18', args.goversion) < 0:
  557. kinds = ['Test', 'Benchmark', 'Example']
  558. else:
  559. kinds = ['Test', 'Benchmark', 'FuzzTarget', 'Example']
  560. var_names = []
  561. for kind in kinds:
  562. var_name = '{}s'.format(kind.lower())
  563. var_names.append(var_name)
  564. lines.append('var {} = []testing.Internal{}{{'.format(var_name, kind))
  565. for test in [x for x in tests if x.startswith(kind)]:
  566. lines.append(' {{"{test}", _test.{test}}},'.format(test=test))
  567. for test in [x for x in xtests if x.startswith(kind)]:
  568. lines.append(' {{"{test}", _xtest.{test}}},'.format(test=test))
  569. lines.extend(['}', ''])
  570. if is_cover:
  571. lines.extend(gen_cover_info(args))
  572. lines.append('func main() {')
  573. if is_cover:
  574. lines.extend([
  575. ' testing.RegisterCover(testing.Cover{',
  576. ' Mode: "set",',
  577. ' Counters: coverCounters,',
  578. ' Blocks: coverBlocks,',
  579. ' CoveredPackages: "",',
  580. ' })',
  581. ])
  582. lines.extend([
  583. ' m := testing.MainStart(testdeps.TestDeps{{}}, {})'.format(', '.join(var_names)),
  584. '',
  585. ])
  586. if test_main_package:
  587. lines.append(' {}.TestMain(m)'.format(test_main_package))
  588. else:
  589. lines.append(' os.Exit(m.Run())')
  590. lines.extend(['}', ''])
  591. content = '\n'.join(lines)
  592. # sys.stderr.write('{}\n'.format(content))
  593. return content
  594. def do_link_test(args):
  595. assert args.srcs or args.xtest_srcs
  596. assert args.test_miner is not None
  597. test_module_path = get_source_path(args)
  598. test_import_path, _ = get_import_path(test_module_path)
  599. test_lib_args = copy_args(args) if args.srcs else None
  600. xtest_lib_args = copy_args(args) if args.xtest_srcs else None
  601. if xtest_lib_args is not None:
  602. xtest_lib_args.embed = args.embed_xtest if args.embed_xtest else None
  603. ydx_file_name = None
  604. xtest_ydx_file_name = None
  605. need_append_ydx = test_lib_args and xtest_lib_args and args.ydx_file and args.vet_flags
  606. if need_append_ydx:
  607. def find_ydx_file_name(name, flags):
  608. for i, elem in enumerate(flags):
  609. if elem.endswith(name):
  610. return (i, elem)
  611. assert False, 'Unreachable code'
  612. idx, ydx_file_name = find_ydx_file_name(xtest_lib_args.ydx_file, xtest_lib_args.vet_flags)
  613. xtest_ydx_file_name = '{}_xtest'.format(ydx_file_name)
  614. xtest_lib_args.vet_flags = copy.copy(xtest_lib_args.vet_flags)
  615. xtest_lib_args.vet_flags[idx] = xtest_ydx_file_name
  616. if test_lib_args:
  617. test_lib_args.output = os.path.join(args.output_root, 'test.a')
  618. test_lib_args.vet_report_output = vet_report_output_name(test_lib_args.output)
  619. test_lib_args.module_path = test_module_path
  620. test_lib_args.import_path = test_import_path
  621. do_link_lib(test_lib_args)
  622. if xtest_lib_args:
  623. xtest_lib_args.srcs = xtest_lib_args.xtest_srcs
  624. classify_srcs(xtest_lib_args.srcs, xtest_lib_args)
  625. xtest_lib_args.output = os.path.join(args.output_root, 'xtest.a')
  626. xtest_lib_args.vet_report_output = vet_report_output_name(xtest_lib_args.output)
  627. xtest_lib_args.module_path = test_module_path + '_test'
  628. xtest_lib_args.import_path = test_import_path + '_test'
  629. if test_lib_args:
  630. xtest_lib_args.module_map[test_import_path] = test_lib_args.output
  631. need_append_ydx = args.ydx_file and args.srcs and args.vet_flags
  632. do_link_lib(xtest_lib_args)
  633. if need_append_ydx:
  634. with open(os.path.join(args.build_root, ydx_file_name), 'ab') as dst_file:
  635. with open(os.path.join(args.build_root, xtest_ydx_file_name), 'rb') as src_file:
  636. dst_file.write(src_file.read())
  637. test_main_content = gen_test_main(args, test_lib_args, xtest_lib_args)
  638. test_main_name = os.path.join(args.output_root, '_test_main.go')
  639. with open(test_main_name, "w") as f:
  640. f.write(test_main_content)
  641. test_args = copy_args(args)
  642. test_args.embed = None
  643. test_args.srcs = [test_main_name]
  644. if test_args.test_import_path is None:
  645. # it seems that we can do it unconditionally, but this kind
  646. # of mangling doesn't really looks good to me and we leave it
  647. # for pure GO_TEST module
  648. test_args.module_path = test_args.module_path + '___test_main__'
  649. test_args.import_path = test_args.import_path + '___test_main__'
  650. classify_srcs(test_args.srcs, test_args)
  651. if test_lib_args:
  652. test_args.module_map[test_lib_args.import_path] = test_lib_args.output
  653. if xtest_lib_args:
  654. test_args.module_map[xtest_lib_args.import_path] = xtest_lib_args.output
  655. if args.vet:
  656. dump_vet_report_for_tests(test_args, test_lib_args, xtest_lib_args)
  657. test_args.vet = False
  658. do_link_exe(test_args)
  659. if __name__ == '__main__':
  660. reload(sys)
  661. sys.setdefaultencoding('utf-8')
  662. sys.stdout = codecs.getwriter('utf8')(sys.stdout)
  663. sys.stderr = codecs.getwriter('utf8')(sys.stderr)
  664. args = pcf.get_args(sys.argv[1:])
  665. parser = argparse.ArgumentParser(prefix_chars='+')
  666. parser.add_argument('++mode', choices=['dll', 'exe', 'lib', 'test'], required=True)
  667. parser.add_argument('++srcs', nargs='*', required=True)
  668. parser.add_argument('++cgo-srcs', nargs='*')
  669. parser.add_argument('++test_srcs', nargs='*')
  670. parser.add_argument('++xtest_srcs', nargs='*')
  671. parser.add_argument('++cover_info', nargs='*')
  672. parser.add_argument('++output', nargs='?', default=None)
  673. parser.add_argument('++source-root', default=None)
  674. parser.add_argument('++build-root', required=True)
  675. parser.add_argument('++tools-root', default=None)
  676. parser.add_argument('++output-root', required=True)
  677. parser.add_argument('++toolchain-root', required=True)
  678. parser.add_argument('++host-os', choices=['linux', 'darwin', 'windows'], required=True)
  679. parser.add_argument('++host-arch', choices=['amd64', 'arm64'], required=True)
  680. parser.add_argument('++targ-os', choices=['linux', 'darwin', 'windows'], required=True)
  681. parser.add_argument('++targ-arch', choices=['amd64', 'x86', 'arm64'], required=True)
  682. parser.add_argument('++peers', nargs='*')
  683. parser.add_argument('++non-local-peers', nargs='*')
  684. parser.add_argument('++cgo-peers', nargs='*')
  685. parser.add_argument('++asmhdr', nargs='?', default=None)
  686. parser.add_argument('++test-import-path', nargs='?')
  687. parser.add_argument('++test-miner', nargs='?')
  688. parser.add_argument('++arc-project-prefix', nargs='?', default=arc_project_prefix)
  689. parser.add_argument('++std-lib-prefix', nargs='?', default=std_lib_prefix)
  690. parser.add_argument('++vendor-prefix', nargs='?', default=vendor_prefix)
  691. parser.add_argument('++extld', nargs='?', default=None)
  692. parser.add_argument('++extldflags', nargs='+', default=None)
  693. parser.add_argument('++goversion', required=True)
  694. parser.add_argument('++asm-flags', nargs='*')
  695. parser.add_argument('++compile-flags', nargs='*')
  696. parser.add_argument('++link-flags', nargs='*')
  697. parser.add_argument('++vcs', nargs='?', default=None)
  698. parser.add_argument('++vet', nargs='?', const=True, default=False)
  699. parser.add_argument('++vet-flags', nargs='*', default=None)
  700. parser.add_argument('++vet-info-ext', default=vet_info_ext)
  701. parser.add_argument('++vet-report-ext', default=vet_report_ext)
  702. parser.add_argument('++musl', action='store_true')
  703. parser.add_argument('++skip-tests', nargs='*', default=None)
  704. parser.add_argument('++ydx-file', default='')
  705. parser.add_argument('++debug-root-map', default=None)
  706. parser.add_argument('++embed', action='append', nargs='*')
  707. parser.add_argument('++embed_xtest', action='append', nargs='*')
  708. args = parser.parse_args(args)
  709. arc_project_prefix = args.arc_project_prefix
  710. std_lib_prefix = args.std_lib_prefix
  711. vendor_prefix = args.vendor_prefix
  712. vet_info_ext = args.vet_info_ext
  713. vet_report_ext = args.vet_report_ext
  714. preprocess_args(args)
  715. try:
  716. os.unlink(args.output)
  717. except OSError:
  718. pass
  719. # We are going to support only 'lib', 'exe' and 'cgo' build modes currently
  720. # and as a result we are going to generate only one build node per module
  721. # (or program)
  722. dispatch = {
  723. 'exe': do_link_exe,
  724. 'dll': do_link_exe,
  725. 'lib': do_link_lib,
  726. 'test': do_link_test
  727. }
  728. exit_code = 1
  729. try:
  730. dispatch[args.mode](args)
  731. exit_code = 0
  732. except KeyError:
  733. sys.stderr.write('Unknown build mode [{}]...\n'.format(args.mode))
  734. except subprocess.CalledProcessError as e:
  735. sys.stderr.write('{} returned non-zero exit code {}.\n{}\n'.format(' '.join(e.cmd), e.returncode, e.output))
  736. exit_code = e.returncode
  737. except Exception as e:
  738. sys.stderr.write('Unhandled exception [{}]...\n'.format(str(e)))
  739. sys.exit(exit_code)