aboutsummaryrefslogtreecommitdiffstats
path: root/build
diff options
context:
space:
mode:
authoralexv-smirnov <alex@ydb.tech>2022-08-18 16:52:30 +0300
committeralexv-smirnov <alex@ydb.tech>2022-08-18 16:52:30 +0300
commitc140abc954b61ab7d86af80bdeced01482d9971a (patch)
treec47d70fa3213240d5e0eb59787a5325782a360de /build
parent0ce07b9705ed20e3fce2759eae41496014ca4c33 (diff)
downloadydb-c140abc954b61ab7d86af80bdeced01482d9971a.tar.gz
temp fix ydb oss sync config to unlock sync on /vendor dependency
Diffstat (limited to 'build')
-rw-r--r--build/scripts/cgo1_wrapper.py45
-rw-r--r--build/scripts/go_fake_include/go_asm.h0
-rw-r--r--build/scripts/go_tool.py867
3 files changed, 912 insertions, 0 deletions
diff --git a/build/scripts/cgo1_wrapper.py b/build/scripts/cgo1_wrapper.py
new file mode 100644
index 0000000000..986082f7e9
--- /dev/null
+++ b/build/scripts/cgo1_wrapper.py
@@ -0,0 +1,45 @@
+import argparse
+import shutil
+import subprocess
+import sys
+
+
+CGO1_SUFFIX='.cgo1.go'
+
+
+def call(cmd, cwd, env=None):
+ # sys.stderr.write('{}\n'.format(' '.join(cmd)))
+ return subprocess.call(cmd, stdin=None, stderr=sys.stderr, stdout=sys.stdout, cwd=cwd, env=env)
+
+
+def process_file(source_root, source_prefix, build_root, build_prefix, src_path, comment_prefix):
+ dst_path = '{}.tmp'.format(src_path)
+ with open(src_path, 'r') as src_file, open(dst_path, 'w') as dst_file:
+ for line in src_file:
+ if line.startswith(comment_prefix):
+ dst_file.write(line.replace(source_root, source_prefix).replace(build_root, build_prefix))
+ else:
+ dst_file.write(line)
+ shutil.move(dst_path, src_path)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--build-prefix', default='__ARCADIA_BUILD_ROOT_PREFIX__')
+ parser.add_argument('--build-root', required=True)
+ parser.add_argument('--cgo1-files', nargs='+', required=True)
+ parser.add_argument('--cgo2-files', nargs='+', required=True)
+ parser.add_argument('--source-prefix', default='__ARCADIA_SOURCE_ROOT_PREFIX__')
+ parser.add_argument('--source-root', required=True)
+ parser.add_argument('cgo1_cmd', nargs='*')
+ args = parser.parse_args()
+
+ exit_code = call(args.cgo1_cmd, args.source_root)
+ if exit_code != 0:
+ sys.exit(exit_code)
+
+ for src_path in args.cgo1_files:
+ process_file(args.source_root, args.source_prefix, args.build_root, args.build_prefix, src_path, '//')
+
+ for src_path in args.cgo2_files:
+ process_file(args.source_root, args.source_prefix, args.build_root, args.build_prefix, src_path, '#line')
diff --git a/build/scripts/go_fake_include/go_asm.h b/build/scripts/go_fake_include/go_asm.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/build/scripts/go_fake_include/go_asm.h
diff --git a/build/scripts/go_tool.py b/build/scripts/go_tool.py
new file mode 100644
index 0000000000..7c51e65a2e
--- /dev/null
+++ b/build/scripts/go_tool.py
@@ -0,0 +1,867 @@
+from __future__ import absolute_import, unicode_literals
+import argparse
+import copy
+import json
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tarfile
+import tempfile
+import threading
+import six
+from functools import reduce
+
+import process_command_files as pcf
+import process_whole_archive_option as pwa
+
+arc_project_prefix = 'a.yandex-team.ru/'
+std_lib_prefix = 'contrib/go/_std_1.18/src/'
+vendor_prefix = 'vendor/'
+vet_info_ext = '.vet.out'
+vet_report_ext = '.vet.txt'
+
+FIXED_CGO1_SUFFIX='.fixed.cgo1.go'
+
+COMPILE_OPTIMIZATION_FLAGS=('-N',)
+
+
+def get_trimpath_args(args):
+ return ['-trimpath', args.trimpath] if args.trimpath else []
+
+
+def preprocess_cgo1(src_path, dst_path, source_root):
+ with open(src_path, 'r') as f:
+ content = f.read()
+ content = content.replace('__ARCADIA_SOURCE_ROOT_PREFIX__', source_root)
+ with open(dst_path, 'w') as f:
+ f.write(content)
+
+
+def preprocess_args(args):
+ # Temporary work around for noauto
+ if args.cgo_srcs and len(args.cgo_srcs) > 0:
+ cgo_srcs_set = set(args.cgo_srcs)
+ args.srcs = [x for x in args.srcs if x not in cgo_srcs_set]
+
+ args.pkg_root = os.path.join(args.toolchain_root, 'pkg')
+ toolchain_tool_root = os.path.join(args.pkg_root, 'tool', '{}_{}'.format(args.host_os, args.host_arch))
+ args.go_compile = os.path.join(toolchain_tool_root, 'compile')
+ args.go_cgo = os.path.join(toolchain_tool_root, 'cgo')
+ args.go_link = os.path.join(toolchain_tool_root, 'link')
+ args.go_asm = os.path.join(toolchain_tool_root, 'asm')
+ args.go_pack = os.path.join(toolchain_tool_root, 'pack')
+ args.go_vet = os.path.join(toolchain_tool_root, 'vet') if args.vet is True else args.vet
+ args.output = os.path.normpath(args.output)
+ args.vet_report_output = vet_report_output_name(args.output, args.vet_report_ext)
+ args.trimpath = None
+ if args.debug_root_map:
+ roots = {'build': args.build_root, 'source': args.source_root, 'tools': args.tools_root}
+ replaces = []
+ for root in args.debug_root_map.split(';'):
+ src, dst = root.split('=', 1)
+ assert src in roots
+ replaces.append('{}=>{}'.format(roots[src], dst))
+ del roots[src]
+ assert len(replaces) > 0
+ args.trimpath = ';'.join(replaces)
+ args.build_root = os.path.normpath(args.build_root)
+ args.build_root_dir = args.build_root + os.path.sep
+ args.source_root = os.path.normpath(args.source_root)
+ args.source_root_dir = args.source_root + os.path.sep
+ args.output_root = os.path.normpath(args.output_root)
+ args.import_map = {}
+ args.module_map = {}
+ if args.cgo_peers:
+ args.cgo_peers = [x for x in args.cgo_peers if not x.endswith('.fake.pkg')]
+
+ srcs = []
+ for f in args.srcs:
+ if f.endswith('.gosrc'):
+ with tarfile.open(f, 'r') as tar:
+ srcs.extend(os.path.join(args.output_root, src) for src in tar.getnames())
+ tar.extractall(path=args.output_root)
+ else:
+ srcs.append(f)
+ args.srcs = srcs
+
+ assert args.mode == 'test' or args.test_srcs is None and args.xtest_srcs is None
+ # add lexical oreder by basename for go sources
+ args.srcs.sort(key=lambda x: os.path.basename(x))
+ if args.test_srcs:
+ args.srcs += sorted(args.test_srcs, key=lambda x: os.path.basename(x))
+ del args.test_srcs
+ if args.xtest_srcs:
+ args.xtest_srcs.sort(key=lambda x: os.path.basename(x))
+
+ # compute root relative module dir path
+ assert args.output is None or args.output_root == os.path.dirname(args.output)
+ assert args.output_root.startswith(args.build_root_dir)
+ args.module_path = args.output_root[len(args.build_root_dir):]
+ args.source_module_dir = os.path.join(args.source_root, args.test_import_path or args.module_path) + os.path.sep
+ assert len(args.module_path) > 0
+ args.import_path, args.is_std = get_import_path(args.module_path)
+
+ assert args.asmhdr is None or args.word == 'go'
+
+ srcs = []
+ for f in args.srcs:
+ if f.endswith(FIXED_CGO1_SUFFIX) and f.startswith(args.build_root_dir):
+ path = os.path.join(args.output_root, '{}.cgo1.go'.format(os.path.basename(f[:-len(FIXED_CGO1_SUFFIX)])))
+ srcs.append(path)
+ preprocess_cgo1(f, path, args.source_root)
+ else:
+ srcs.append(f)
+ args.srcs = srcs
+
+ if args.extldflags:
+ args.extldflags = pwa.ProcessWholeArchiveOption(args.targ_os).construct_cmd(args.extldflags)
+
+ classify_srcs(args.srcs, args)
+
+
+def compare_versions(version1, version2):
+ def last_index(version):
+ index = version.find('beta')
+ return len(version) if index < 0 else index
+
+ v1 = tuple(x.zfill(8) for x in version1[:last_index(version1)].split('.'))
+ v2 = tuple(x.zfill(8) for x in version2[:last_index(version2)].split('.'))
+ if v1 == v2:
+ return 0
+ return 1 if v1 < v2 else -1
+
+
+def get_symlink_or_copyfile():
+ os_symlink = getattr(os, 'symlink', None)
+ if os_symlink is None or os.name == 'nt':
+ os_symlink = shutil.copyfile
+ return os_symlink
+
+
+def copy_args(args):
+ return copy.copy(args)
+
+
+def get_vendor_index(import_path):
+ index = import_path.rfind('/' + vendor_prefix)
+ if index < 0:
+ index = 0 if import_path.startswith(vendor_prefix) else index
+ else:
+ index = index + 1
+ return index
+
+
+def get_import_path(module_path):
+ assert len(module_path) > 0
+ import_path = module_path.replace('\\', '/')
+ is_std_module = import_path.startswith(std_lib_prefix)
+ if is_std_module:
+ import_path = import_path[len(std_lib_prefix):]
+ elif import_path.startswith(vendor_prefix):
+ import_path = import_path[len(vendor_prefix):]
+ else:
+ import_path = arc_project_prefix + import_path
+ assert len(import_path) > 0
+ return import_path, is_std_module
+
+
+def call(cmd, cwd, env=None):
+ # sys.stderr.write('{}\n'.format(' '.join(cmd)))
+ return subprocess.check_output(cmd, stdin=None, stderr=subprocess.STDOUT, cwd=cwd, env=env, text=True)
+
+
+def classify_srcs(srcs, args):
+ args.go_srcs = [x for x in srcs if x.endswith('.go')]
+ args.asm_srcs = [x for x in srcs if x.endswith('.s')]
+ args.objects = [x for x in srcs if x.endswith('.o') or x.endswith('.obj')]
+ args.symabis = [x for x in srcs if x.endswith('.symabis')]
+ args.sysos = [x for x in srcs if x.endswith('.syso')]
+
+
+def get_import_config_info(peers, gen_importmap, import_map={}, module_map={}):
+ info = {'importmap': [], 'packagefile': [], 'standard': {}}
+ if gen_importmap:
+ for key, value in six.iteritems(import_map):
+ info['importmap'].append((key, value))
+ for peer in peers:
+ peer_import_path, is_std = get_import_path(os.path.dirname(peer))
+ if gen_importmap:
+ index = get_vendor_index(peer_import_path)
+ if index >= 0:
+ index += len(vendor_prefix)
+ info['importmap'].append((peer_import_path[index:], peer_import_path))
+ info['packagefile'].append((peer_import_path, os.path.join(args.build_root, peer)))
+ if is_std:
+ info['standard'][peer_import_path] = True
+ for key, value in six.iteritems(module_map):
+ info['packagefile'].append((key, value))
+ return info
+
+
+def create_import_config(peers, gen_importmap, import_map={}, module_map={}):
+ lines = []
+ info = get_import_config_info(peers, gen_importmap, import_map, module_map)
+ for key in ('importmap', 'packagefile'):
+ for item in info[key]:
+ lines.append('{} {}={}'.format(key, *item))
+ if len(lines) > 0:
+ lines.append('')
+ content = '\n'.join(lines)
+ # sys.stderr.writelines('{}\n'.format(l) for l in lines)
+ with tempfile.NamedTemporaryFile(delete=False) as f:
+ f.write(content.encode('UTF-8'))
+ return f.name
+ return None
+
+
+def create_embed_config(args):
+ data = {
+ 'Patterns': {},
+ 'Files': {},
+ }
+ for info in args.embed:
+ pattern = info[0]
+ if pattern.endswith('/**/*'):
+ pattern = pattern[:-3]
+ files = {os.path.relpath(f, args.source_module_dir).replace('\\', '/'): f for f in info[1:]}
+ data['Patterns'][pattern] = list(files.keys())
+ data['Files'].update(files)
+ # sys.stderr.write('{}\n'.format(json.dumps(data, indent=4)))
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.embedcfg') as f:
+ f.write(json.dumps(data).encode('UTF-8'))
+ return f.name
+
+
+def vet_info_output_name(path, ext=None):
+ return '{}{}'.format(path, ext or vet_info_ext)
+
+
+def vet_report_output_name(path, ext=None):
+ return '{}{}'.format(path, ext or vet_report_ext)
+
+
+def get_source_path(args):
+ return args.test_import_path or args.module_path
+
+
+def gen_vet_info(args):
+ import_path = args.real_import_path if hasattr(args, 'real_import_path') else args.import_path
+ info = get_import_config_info(args.peers, True, args.import_map, args.module_map)
+
+ import_map = dict(info['importmap'])
+ # FIXME(snermolaev): it seems that adding import map for 'fake' package
+ # does't make any harm (it needs to be revised later)
+ import_map['unsafe'] = 'unsafe'
+
+ for (key, _) in info['packagefile']:
+ if key not in import_map:
+ import_map[key] = key
+
+ data = {
+ 'ID': import_path,
+ 'Compiler': 'gc',
+ 'Dir': os.path.join(args.source_root, get_source_path(args)),
+ 'ImportPath': import_path,
+ 'GoFiles': [x for x in args.go_srcs if x.endswith('.go')],
+ 'NonGoFiles': [x for x in args.go_srcs if not x.endswith('.go')],
+ 'ImportMap': import_map,
+ 'PackageFile': dict(info['packagefile']),
+ 'Standard': dict(info['standard']),
+ 'PackageVetx': dict((key, vet_info_output_name(value)) for key, value in info['packagefile']),
+ 'VetxOnly': False,
+ 'VetxOutput': vet_info_output_name(args.output),
+ 'SucceedOnTypecheckFailure': False
+ }
+ # sys.stderr.write('{}\n'.format(json.dumps(data, indent=4)))
+ return data
+
+
+def create_vet_config(args, info):
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.cfg') as f:
+ f.write(json.dumps(info).encode('UTF-8'))
+ return f.name
+
+
+def decode_vet_report(json_report):
+ report = ''
+ if json_report:
+ try:
+ full_diags = json.JSONDecoder().decode(json_report.decode('UTF-8'))
+ except ValueError:
+ report = json_report
+ else:
+ messages = []
+ for _, module_diags in six.iteritems(full_diags):
+ for _, type_diags in six.iteritems(module_diags):
+ for diag in type_diags:
+ messages.append('{}: {}'.format(diag['posn'], json.dumps(diag['message'])))
+ report = '\n'.join(messages)
+
+ return report
+
+
+def dump_vet_report(args, report):
+ if report:
+ report = report.replace(args.build_root, '$B')
+ report = report.replace(args.source_root, '$S')
+ with open(args.vet_report_output, 'w') as f:
+ f.write(report)
+
+
+def read_vet_report(args):
+ assert args
+ report = ''
+ if os.path.exists(args.vet_report_output):
+ with open(args.vet_report_output, 'r') as f:
+ report += f.read()
+ return report
+
+
+def dump_vet_report_for_tests(args, *test_args_list):
+ dump_vet_report(args, reduce(lambda x, y: x + read_vet_report(y), [_f for _f in test_args_list if _f], ''))
+
+
+def do_vet(args):
+ assert args.vet
+ info = gen_vet_info(args)
+ vet_config = create_vet_config(args, info)
+ cmd = [args.go_vet, '-json']
+ if args.vet_flags:
+ cmd.extend(args.vet_flags)
+ cmd.append(vet_config)
+ # sys.stderr.write('>>>> [{}]\n'.format(' '.join(cmd)))
+ p_vet = subprocess.Popen(cmd, stdin=None, stderr=subprocess.PIPE, stdout=subprocess.PIPE, cwd=args.source_root)
+ vet_out, vet_err = p_vet.communicate()
+ report = decode_vet_report(vet_out) if vet_out else ''
+ dump_vet_report(args, report)
+ if p_vet.returncode:
+ raise subprocess.CalledProcessError(returncode=p_vet.returncode, cmd=cmd, output=vet_err)
+
+
+def _do_compile_go(args):
+ import_path, is_std_module = args.import_path, args.is_std
+ cmd = [
+ args.go_compile,
+ '-o',
+ args.output,
+ '-p',
+ import_path,
+ '-D',
+ '""',
+ '-goversion',
+ 'go{}'.format(args.goversion)
+ ]
+ if args.lang:
+ cmd.append('-lang=go{}'.format(args.lang))
+ cmd.extend(get_trimpath_args(args))
+ compiling_runtime = False
+ if is_std_module:
+ cmd.append('-std')
+ if import_path in ('runtime', 'internal/abi', 'internal/bytealg', 'internal/cpu') or import_path.startswith('runtime/internal/'):
+ cmd.append('-+')
+ compiling_runtime = True
+ import_config_name = create_import_config(args.peers, True, args.import_map, args.module_map)
+ if import_config_name:
+ cmd += ['-importcfg', import_config_name]
+ else:
+ if import_path == 'unsafe' or len(args.objects) > 0 or args.asmhdr:
+ pass
+ else:
+ cmd.append('-complete')
+ # if compare_versions('1.16', args.goversion) >= 0:
+ if args.embed:
+ embed_config_name = create_embed_config(args)
+ cmd.extend(['-embedcfg', embed_config_name])
+ if args.asmhdr:
+ cmd += ['-asmhdr', args.asmhdr]
+ # Use .symabis (starting from 1.12 version)
+ if args.symabis:
+ cmd += ['-symabis'] + args.symabis
+ # If 1.12 <= version < 1.13 we have to pass -allabis for 'runtime' and 'runtime/internal/atomic'
+ # if compare_versions('1.13', args.goversion) >= 0:
+ # pass
+ # elif import_path in ('runtime', 'runtime/internal/atomic'):
+ # cmd.append('-allabis')
+ compile_workers = '4'
+ if args.compile_flags:
+ if compiling_runtime:
+ cmd.extend(x for x in args.compile_flags if x not in COMPILE_OPTIMIZATION_FLAGS)
+ else:
+ cmd.extend(args.compile_flags)
+ if any([x in ('-race', '-shared') for x in args.compile_flags]):
+ compile_workers = '1'
+ cmd += ['-pack', '-c={}'.format(compile_workers)]
+ cmd += args.go_srcs
+ call(cmd, args.build_root)
+
+
+class VetThread(threading.Thread):
+
+ def __init__(self, target, args):
+ super(VetThread, self).__init__(target=target, args=args)
+ self.exc_info = None
+
+ def run(self):
+ try:
+ super(VetThread, self).run()
+ except:
+ self.exc_info = sys.exc_info()
+
+ def join_with_exception(self, reraise_exception):
+ self.join()
+ if reraise_exception and self.exc_info:
+ six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
+
+
+def do_compile_go(args):
+ raise_exception_from_vet = False
+ if args.vet:
+ run_vet = VetThread(target=do_vet, args=(args,))
+ run_vet.start()
+ try:
+ _do_compile_go(args)
+ raise_exception_from_vet = True
+ finally:
+ if args.vet:
+ run_vet.join_with_exception(raise_exception_from_vet)
+
+
+def do_compile_asm(args):
+ def need_compiling_runtime(import_path):
+ return import_path in ('runtime', 'reflect', 'syscall') or \
+ import_path.startswith('runtime/internal/') or \
+ compare_versions('1.17', args.goversion) >= 0 and import_path == 'internal/bytealg'
+
+ assert(len(args.srcs) == 1 and len(args.asm_srcs) == 1)
+ cmd = [args.go_asm]
+ cmd += get_trimpath_args(args)
+ cmd += ['-I', args.output_root, '-I', os.path.join(args.pkg_root, 'include')]
+ cmd += ['-D', 'GOOS_' + args.targ_os, '-D', 'GOARCH_' + args.targ_arch, '-o', args.output]
+
+ # if compare_versions('1.16', args.goversion) >= 0:
+ cmd += ['-p', args.import_path]
+ if need_compiling_runtime(args.import_path):
+ cmd += ['-compiling-runtime']
+
+ if args.asm_flags:
+ cmd += args.asm_flags
+ cmd += args.asm_srcs
+ call(cmd, args.build_root)
+
+
+def do_link_lib(args):
+ if len(args.asm_srcs) > 0:
+ asmargs = copy_args(args)
+ asmargs.asmhdr = os.path.join(asmargs.output_root, 'go_asm.h')
+ do_compile_go(asmargs)
+ for src in asmargs.asm_srcs:
+ asmargs.srcs = [src]
+ asmargs.asm_srcs = [src]
+ asmargs.output = os.path.join(asmargs.output_root, os.path.basename(src) + '.o')
+ do_compile_asm(asmargs)
+ args.objects.append(asmargs.output)
+ else:
+ do_compile_go(args)
+ if args.objects or args.sysos:
+ cmd = [args.go_pack, 'r', args.output] + args.objects + args.sysos
+ call(cmd, args.build_root)
+
+
+def do_link_exe(args):
+ assert args.extld is not None
+ assert args.non_local_peers is not None
+ compile_args = copy_args(args)
+ compile_args.output = os.path.join(args.output_root, 'main.a')
+ compile_args.real_import_path = compile_args.import_path
+ compile_args.import_path = 'main'
+
+ if args.vcs and os.path.isfile(compile_args.vcs):
+ build_info = os.path.join('library', 'go', 'core', 'buildinfo')
+ if any([x.startswith(build_info) for x in compile_args.peers]):
+ compile_args.go_srcs.append(compile_args.vcs)
+
+ do_link_lib(compile_args)
+ cmd = [args.go_link, '-o', args.output]
+ import_config_name = create_import_config(args.peers + args.non_local_peers, False, args.import_map, args.module_map)
+ if import_config_name:
+ cmd += ['-importcfg', import_config_name]
+ if args.link_flags:
+ cmd += args.link_flags
+
+ if args.mode in ('exe', 'test'):
+ cmd.append('-buildmode=exe')
+ elif args.mode == 'dll':
+ cmd.append('-buildmode=c-shared')
+ else:
+ assert False, 'Unexpected mode: {}'.format(args.mode)
+ cmd.append('-extld={}'.format(args.extld))
+
+ extldflags = []
+ if args.extldflags is not None:
+ filter_musl = bool
+ if args.musl:
+ cmd.append('-linkmode=external')
+ extldflags.append('-static')
+ filter_musl = lambda x: x not in ('-lc', '-ldl', '-lm', '-lpthread', '-lrt')
+ extldflags += [x for x in args.extldflags if filter_musl(x)]
+ cgo_peers = []
+ if args.cgo_peers is not None and len(args.cgo_peers) > 0:
+ is_group = args.targ_os == 'linux'
+ if is_group:
+ cgo_peers.append('-Wl,--start-group')
+ cgo_peers.extend(args.cgo_peers)
+ if is_group:
+ cgo_peers.append('-Wl,--end-group')
+ try:
+ index = extldflags.index('--cgo-peers')
+ extldflags = extldflags[:index] + cgo_peers + extldflags[index+1:]
+ except ValueError:
+ extldflags.extend(cgo_peers)
+ if len(extldflags) > 0:
+ cmd.append('-extldflags={}'.format(' '.join(extldflags)))
+ cmd.append(compile_args.output)
+ call(cmd, args.build_root)
+
+
+def gen_cover_info(args):
+ lines = []
+ lines.extend([
+ """
+var (
+ coverCounters = make(map[string][]uint32)
+ coverBlocks = make(map[string][]testing.CoverBlock)
+)
+ """,
+ 'func init() {',
+ ])
+ for var, file in (x.split(':') for x in args.cover_info):
+ lines.append(' coverRegisterFile("{file}", _cover0.{var}.Count[:], _cover0.{var}.Pos[:], _cover0.{var}.NumStmt[:])'.format(file=file, var=var))
+ lines.extend([
+ '}',
+ """
+func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) {
+ if 3*len(counter) != len(pos) || len(counter) != len(numStmts) {
+ panic("coverage: mismatched sizes")
+ }
+ if coverCounters[fileName] != nil {
+ // Already registered.
+ return
+ }
+ coverCounters[fileName] = counter
+ block := make([]testing.CoverBlock, len(counter))
+ for i := range counter {
+ block[i] = testing.CoverBlock{
+ Line0: pos[3*i+0],
+ Col0: uint16(pos[3*i+2]),
+ Line1: pos[3*i+1],
+ Col1: uint16(pos[3*i+2]>>16),
+ Stmts: numStmts[i],
+ }
+ }
+ coverBlocks[fileName] = block
+}
+ """,
+ ])
+ return lines
+
+
+def filter_out_skip_tests(tests, skip_tests):
+ skip_set = set()
+ star_skip_set = set()
+ for t in skip_tests:
+ work_set = star_skip_set if '*' in t else skip_set
+ work_set.add(t)
+
+ re_star_tests = None
+ if len(star_skip_set) > 0:
+ re_star_tests = re.compile(re.sub(r'(\*)+', r'.\1', '^({})$'.format('|'.join(star_skip_set))))
+
+ return [x for x in tests if not (x in skip_tests or re_star_tests and re_star_tests.match(x))]
+
+
+def gen_test_main(args, test_lib_args, xtest_lib_args):
+ assert args and (test_lib_args or xtest_lib_args)
+ test_miner = args.test_miner
+ test_module_path = test_lib_args.import_path if test_lib_args else xtest_lib_args.import_path
+ is_cover = args.cover_info and len(args.cover_info) > 0
+
+ # Prepare GOPATH
+ # $BINDIR
+ # |- __go__
+ # |- src
+ # |- pkg
+ # |- ${TARGET_OS}_${TARGET_ARCH}
+ go_path_root = os.path.join(args.output_root, '__go__')
+ test_src_dir = os.path.join(go_path_root, 'src')
+ target_os_arch = '_'.join([args.targ_os, args.targ_arch])
+ test_pkg_dir = os.path.join(go_path_root, 'pkg', target_os_arch, os.path.dirname(test_module_path))
+ os.makedirs(test_pkg_dir)
+
+ my_env = os.environ.copy()
+ my_env['GOROOT'] = ''
+ my_env['GOPATH'] = go_path_root
+ my_env['GOARCH'] = args.targ_arch
+ my_env['GOOS'] = args.targ_os
+
+ tests = []
+ xtests = []
+ os_symlink = get_symlink_or_copyfile()
+
+ # Get the list of "internal" tests
+ if test_lib_args:
+ os.makedirs(os.path.join(test_src_dir, test_module_path))
+ os_symlink(test_lib_args.output, os.path.join(test_pkg_dir, os.path.basename(test_module_path) + '.a'))
+ cmd = [test_miner, '-benchmarks', '-tests', test_module_path]
+ tests = [x for x in (call(cmd, test_lib_args.output_root, my_env) or '').strip().split('\n') if len(x) > 0]
+ if args.skip_tests:
+ tests = filter_out_skip_tests(tests, args.skip_tests)
+ test_main_found = '#TestMain' in tests
+
+ # Get the list of "external" tests
+ if xtest_lib_args:
+ xtest_module_path = xtest_lib_args.import_path
+ os.makedirs(os.path.join(test_src_dir, xtest_module_path))
+ os_symlink(xtest_lib_args.output, os.path.join(test_pkg_dir, os.path.basename(xtest_module_path) + '.a'))
+ cmd = [test_miner, '-benchmarks', '-tests', xtest_module_path]
+ xtests = [x for x in (call(cmd, xtest_lib_args.output_root, my_env) or '').strip().split('\n') if len(x) > 0]
+ if args.skip_tests:
+ xtests = filter_out_skip_tests(xtests, args.skip_tests)
+ xtest_main_found = '#TestMain' in xtests
+
+ test_main_package = None
+ if test_main_found and xtest_main_found:
+ assert False, 'multiple definition of TestMain'
+ elif test_main_found:
+ test_main_package = '_test'
+ elif xtest_main_found:
+ test_main_package = '_xtest'
+
+ shutil.rmtree(go_path_root)
+
+ lines = ['package main', '', 'import (']
+ if test_main_package is None:
+ lines.append(' "os"')
+ lines.extend([' "testing"', ' "testing/internal/testdeps"'])
+
+ if len(tests) > 0:
+ lines.append(' _test "{}"'.format(test_module_path))
+ elif test_lib_args:
+ lines.append(' _ "{}"'.format(test_module_path))
+
+ if len(xtests) > 0:
+ lines.append(' _xtest "{}"'.format(xtest_module_path))
+ elif xtest_lib_args:
+ lines.append(' _ "{}"'.format(xtest_module_path))
+
+ if is_cover:
+ lines.append(' _cover0 "{}"'.format(test_module_path))
+ lines.extend([')', ''])
+
+ if compare_versions('1.18', args.goversion) < 0:
+ kinds = ['Test', 'Benchmark', 'Example']
+ else:
+ kinds = ['Test', 'Benchmark', 'FuzzTarget', 'Example']
+
+ var_names = []
+ for kind in kinds:
+ var_name = '{}s'.format(kind.lower())
+ var_names.append(var_name)
+ lines.append('var {} = []testing.Internal{}{{'.format(var_name, kind))
+ for test in [x for x in tests if x.startswith(kind)]:
+ lines.append(' {{"{test}", _test.{test}}},'.format(test=test))
+ for test in [x for x in xtests if x.startswith(kind)]:
+ lines.append(' {{"{test}", _xtest.{test}}},'.format(test=test))
+ lines.extend(['}', ''])
+
+ if is_cover:
+ lines.extend(gen_cover_info(args))
+
+ lines.append('func main() {')
+ if is_cover:
+ lines.extend([
+ ' testing.RegisterCover(testing.Cover{',
+ ' Mode: "set",',
+ ' Counters: coverCounters,',
+ ' Blocks: coverBlocks,',
+ ' CoveredPackages: "",',
+ ' })',
+ ])
+ lines.extend([
+ ' m := testing.MainStart(testdeps.TestDeps{{}}, {})'.format(', '.join(var_names)),
+ '',
+ ])
+
+ if test_main_package:
+ lines.append(' {}.TestMain(m)'.format(test_main_package))
+ else:
+ lines.append(' os.Exit(m.Run())')
+ lines.extend(['}', ''])
+
+ content = '\n'.join(lines)
+ # sys.stderr.write('{}\n'.format(content))
+ return content
+
+
+def do_link_test(args):
+ assert args.srcs or args.xtest_srcs
+ assert args.test_miner is not None
+
+ test_module_path = get_source_path(args)
+ test_import_path, _ = get_import_path(test_module_path)
+
+ test_lib_args = copy_args(args) if args.srcs else None
+ xtest_lib_args = copy_args(args) if args.xtest_srcs else None
+ if xtest_lib_args is not None:
+ xtest_lib_args.embed = args.embed_xtest if args.embed_xtest else None
+
+ ydx_file_name = None
+ xtest_ydx_file_name = None
+ need_append_ydx = test_lib_args and xtest_lib_args and args.ydx_file and args.vet_flags
+ if need_append_ydx:
+ def find_ydx_file_name(name, flags):
+ for i, elem in enumerate(flags):
+ if elem.endswith(name):
+ return (i, elem)
+ assert False, 'Unreachable code'
+
+ idx, ydx_file_name = find_ydx_file_name(xtest_lib_args.ydx_file, xtest_lib_args.vet_flags)
+ xtest_ydx_file_name = '{}_xtest'.format(ydx_file_name)
+ xtest_lib_args.vet_flags = copy.copy(xtest_lib_args.vet_flags)
+ xtest_lib_args.vet_flags[idx] = xtest_ydx_file_name
+
+ if test_lib_args:
+ test_lib_args.output = os.path.join(args.output_root, 'test.a')
+ test_lib_args.vet_report_output = vet_report_output_name(test_lib_args.output)
+ test_lib_args.module_path = test_module_path
+ test_lib_args.import_path = test_import_path
+ do_link_lib(test_lib_args)
+
+ if xtest_lib_args:
+ xtest_lib_args.srcs = xtest_lib_args.xtest_srcs
+ classify_srcs(xtest_lib_args.srcs, xtest_lib_args)
+ xtest_lib_args.output = os.path.join(args.output_root, 'xtest.a')
+ xtest_lib_args.vet_report_output = vet_report_output_name(xtest_lib_args.output)
+ xtest_lib_args.module_path = test_module_path + '_test'
+ xtest_lib_args.import_path = test_import_path + '_test'
+ if test_lib_args:
+ xtest_lib_args.module_map[test_import_path] = test_lib_args.output
+ need_append_ydx = args.ydx_file and args.srcs and args.vet_flags
+ do_link_lib(xtest_lib_args)
+
+ if need_append_ydx:
+ with open(os.path.join(args.build_root, ydx_file_name), 'ab') as dst_file:
+ with open(os.path.join(args.build_root, xtest_ydx_file_name), 'rb') as src_file:
+ dst_file.write(src_file.read())
+
+ test_main_content = gen_test_main(args, test_lib_args, xtest_lib_args)
+ test_main_name = os.path.join(args.output_root, '_test_main.go')
+ with open(test_main_name, "w") as f:
+ f.write(test_main_content)
+ test_args = copy_args(args)
+ test_args.embed = None
+ test_args.srcs = [test_main_name]
+ if test_args.test_import_path is None:
+ # it seems that we can do it unconditionally, but this kind
+ # of mangling doesn't really looks good to me and we leave it
+ # for pure GO_TEST module
+ test_args.module_path = test_args.module_path + '___test_main__'
+ test_args.import_path = test_args.import_path + '___test_main__'
+ classify_srcs(test_args.srcs, test_args)
+ if test_lib_args:
+ test_args.module_map[test_lib_args.import_path] = test_lib_args.output
+ if xtest_lib_args:
+ test_args.module_map[xtest_lib_args.import_path] = xtest_lib_args.output
+
+ if args.vet:
+ dump_vet_report_for_tests(test_args, test_lib_args, xtest_lib_args)
+ test_args.vet = False
+
+ do_link_exe(test_args)
+
+
+if __name__ == '__main__':
+ args = pcf.get_args(sys.argv[1:])
+
+ parser = argparse.ArgumentParser(prefix_chars='+')
+ parser.add_argument('++mode', choices=['dll', 'exe', 'lib', 'test'], required=True)
+ parser.add_argument('++srcs', nargs='*', required=True)
+ parser.add_argument('++cgo-srcs', nargs='*')
+ parser.add_argument('++test_srcs', nargs='*')
+ parser.add_argument('++xtest_srcs', nargs='*')
+ parser.add_argument('++cover_info', nargs='*')
+ parser.add_argument('++output', nargs='?', default=None)
+ parser.add_argument('++source-root', default=None)
+ parser.add_argument('++build-root', required=True)
+ parser.add_argument('++tools-root', default=None)
+ parser.add_argument('++output-root', required=True)
+ parser.add_argument('++toolchain-root', required=True)
+ parser.add_argument('++host-os', choices=['linux', 'darwin', 'windows'], required=True)
+ parser.add_argument('++host-arch', choices=['amd64', 'arm64'], required=True)
+ parser.add_argument('++targ-os', choices=['linux', 'darwin', 'windows'], required=True)
+ parser.add_argument('++targ-arch', choices=['amd64', 'x86', 'arm64'], required=True)
+ parser.add_argument('++peers', nargs='*')
+ parser.add_argument('++non-local-peers', nargs='*')
+ parser.add_argument('++cgo-peers', nargs='*')
+ parser.add_argument('++asmhdr', nargs='?', default=None)
+ parser.add_argument('++test-import-path', nargs='?')
+ parser.add_argument('++test-miner', nargs='?')
+ parser.add_argument('++arc-project-prefix', nargs='?', default=arc_project_prefix)
+ parser.add_argument('++std-lib-prefix', nargs='?', default=std_lib_prefix)
+ parser.add_argument('++vendor-prefix', nargs='?', default=vendor_prefix)
+ parser.add_argument('++extld', nargs='?', default=None)
+ parser.add_argument('++extldflags', nargs='+', default=None)
+ parser.add_argument('++goversion', required=True)
+ parser.add_argument('++lang', nargs='?', default=None)
+ parser.add_argument('++asm-flags', nargs='*')
+ parser.add_argument('++compile-flags', nargs='*')
+ parser.add_argument('++link-flags', nargs='*')
+ parser.add_argument('++vcs', nargs='?', default=None)
+ parser.add_argument('++vet', nargs='?', const=True, default=False)
+ parser.add_argument('++vet-flags', nargs='*', default=None)
+ parser.add_argument('++vet-info-ext', default=vet_info_ext)
+ parser.add_argument('++vet-report-ext', default=vet_report_ext)
+ parser.add_argument('++musl', action='store_true')
+ parser.add_argument('++skip-tests', nargs='*', default=None)
+ parser.add_argument('++ydx-file', default='')
+ parser.add_argument('++debug-root-map', default=None)
+ parser.add_argument('++embed', action='append', nargs='*')
+ parser.add_argument('++embed_xtest', action='append', nargs='*')
+ args = parser.parse_args(args)
+
+ arc_project_prefix = args.arc_project_prefix
+ std_lib_prefix = args.std_lib_prefix
+ vendor_prefix = args.vendor_prefix
+ vet_info_ext = args.vet_info_ext
+ vet_report_ext = args.vet_report_ext
+
+ preprocess_args(args)
+
+ try:
+ os.unlink(args.output)
+ except OSError:
+ pass
+
+ # We are going to support only 'lib', 'exe' and 'cgo' build modes currently
+ # and as a result we are going to generate only one build node per module
+ # (or program)
+ dispatch = {
+ 'exe': do_link_exe,
+ 'dll': do_link_exe,
+ 'lib': do_link_lib,
+ 'test': do_link_test
+ }
+
+ exit_code = 1
+ try:
+ dispatch[args.mode](args)
+ exit_code = 0
+ except KeyError:
+ sys.stderr.write('Unknown build mode [{}]...\n'.format(args.mode))
+ except subprocess.CalledProcessError as e:
+ sys.stderr.write('{} returned non-zero exit code {}.\n{}\n'.format(' '.join(e.cmd), e.returncode, e.output))
+ exit_code = e.returncode
+ except Exception as e:
+ sys.stderr.write('Unhandled exception [{}]...\n'.format(str(e)))
+ sys.exit(exit_code)