@@ -216,29 +216,35 @@ def go_stdlib(name:str, tags:list=CONFIG.GO.BUILD_TAGS, labels:list=[], visibili
216216 visibility = visibility,
217217 )
218218
219-
220- def _pkg_sources(name: str, deps: list, test_only:bool):
219+ def _pkg_local_deps(deps: list):
221220 """
222- This rule generates a list of go srcs for any rule in the deps list that is local to this package.
223-
224- This is how we avoid transitively depending on all sources for a target in order to compile non-external tests.
225- Without this, we'd have to transitively pull in all third party sources for the rule which is very expensive,
226- especially for remote execution.
221+ Used by go_test to find libraries that are under test. Returns any build targets from
222+ the deps list that are in the current package.
227223 """
228224 package_prefix = canonicalise(":all").removesuffix("all")
225+ return [dep for dep in deps if canonicalise(dep).startswith(package_prefix)]
226+
227+
228+ def _go_srcs_for_deps(name: str, deps: list, test_only:bool):
229+ """
230+ Used by go_test to extract the sources of libraries under test so we can compile them
231+ into the test package. This just uses the require/provide mechanism to export the go_src
232+ rule for each go_library target.
233+ """
234+
229235 return filegroup(
230- name = name,
231- tag = "pkg_srcs",
232- exported_deps = [dep for dep in deps if canonicalise(dep).startswith(package_prefix)] ,
233- requires = ["go_src"],
234- test_only = test_only,
235- )
236+ name = tag( name, "test_srcs") ,
237+ tag = "pkg_srcs",
238+ exported_deps = deps,
239+ requires = ["go_src"],
240+ test_only = test_only,
241+ )
236242
237243def go_library(name:str, srcs:list, resources:list=[], asm_srcs:list=None, hdrs:list=None, deps:list=[],
238244 visibility:list=None, test_only:bool&testonly=False, complete:bool=True,
239245 _needs_transitive_deps=False, _all_srcs=False, cover:bool=True,
240246 filter_srcs:bool=True, _link_private:bool=False, _link_extra:bool=True, _abi:str=None,
241- _generate_import_config:bool=True, _generate_pkg_info:bool=CONFIG.GO.PKG_INFO,
247+ _generate_import_config:bool=True, _generate_pkg_info:bool=CONFIG.GO.PKG_INFO, _excluded_imports:list=None,
242248 import_path:str='', labels:list=[], package:str=None, pgo_file:str=None, _module:str='', _subrepo:str=''):
243249 """Generates a Go library which can be reused by other rules.
244250
@@ -304,10 +310,6 @@ def go_library(name:str, srcs:list, resources:list=[], asm_srcs:list=None, hdrs:
304310 )
305311 deps += [modinfo]
306312
307- # We could just depend on go_src for all our deps but that would mean bringing in srcs for targets outside this
308- # package like third party sources which is especially slow on systems with slow syscall performance (macOS)
309- if _all_srcs:
310- deps += [_pkg_sources(name, deps, test_only)]
311313 if CONFIG.GO.STDLIB:
312314 deps += _stdlib()
313315
@@ -483,7 +485,18 @@ def go_library(name:str, srcs:list, resources:list=[], asm_srcs:list=None, hdrs:
483485 if pgo_file:
484486 srcs['pgo'] = [pgo_file]
485487
486- cmds, tools = _go_library_cmds(name, import_path=package_path, complete=complete, all_srcs=_all_srcs, cover=cover, filter_srcs=filter_srcs, abi=_abi, embedcfg=embedcfg, pgo_file=pgo_file)
488+ cmds, tools = _go_library_cmds(
489+ name=name,
490+ import_path=package_path,
491+ complete=complete,
492+ all_srcs=_all_srcs,
493+ cover=cover,
494+ filter_srcs=filter_srcs,
495+ abi=_abi,
496+ embedcfg=embedcfg,
497+ pgo_file=pgo_file,
498+ excluded_imports=_excluded_imports,
499+ )
487500
488501 return build_rule(
489502 name = name,
@@ -533,8 +546,15 @@ def _generate_pkg_import_cfg_cmd(name, out, pkg_location, handle_basename_eq_dir
533546
534547 return f'{target_comment} && {find_archives} | {remove_pkg_location} | {remove_ext} | {format_packagefile_directive} | sort -u >> {out}'
535548
536- def _aggregate_import_cfg_cmd():
537- return 'find . -name "*.importconfig" | xargs -I{} cat {} | sort -u >> importconfig'
549+ def _aggregate_import_cfg_cmd(excluded_libs:list):
550+ exclude = ""
551+ # Exclude any libs from the import config. This is used to exclude the package under
552+ # test in go_test, as this has been replaced with a test version of this package that
553+ # contains the test sources as well.
554+ if excluded_libs:
555+ exclude_pattern = "|".join([f"$(location {lib})" for lib in excluded_libs])
556+ exclude = f" | grep -v \"{exclude_pattern}\""
557+ return 'find . -name "*.importconfig" | xargs -I{} cat {}' + exclude + ' | sort -u >> importconfig'
538558
539559def _trim_import_path(pkg):
540560 parts = pkg.split("/")
@@ -729,14 +749,19 @@ def go_test(name:str, srcs:list, resources:list=None, data:list|dict=None, deps:
729749 used to contruct the list of definitions passed to the linker.
730750 env (dict): Additional environment variables to set for the test
731751 """
732-
733- test_package = name + '_lib'
752+ # For non-external tests, we exclude any local go_libraries from deps. These packages will be recompiled
753+ # as _{name}#lib with the test sources. This new package replaces the non-test version.
754+ excluded_packages = None
755+ if not external:
756+ excluded_packages = _pkg_local_deps(deps)
757+ deps += [_go_srcs_for_deps(tag(name, "local_srcs"), excluded_packages, True)]
758+
759+ test_package = name + '_lib' if external or not package_name() else None
734760 # Unfortunately we have to recompile this to build the test together with its library.
735761 lib_rule = go_library(
736762 name = f'_{name}#lib',
737763 srcs = srcs,
738764 resources = resources,
739- package = test_package,
740765 deps = deps,
741766 _all_srcs=not external,
742767 labels = labels,
@@ -747,10 +772,11 @@ def go_test(name:str, srcs:list, resources:list=None, data:list|dict=None, deps:
747772 _generate_import_config=False,
748773 _generate_pkg_info = False,
749774 import_path = test_package,
775+ _excluded_imports=excluded_packages,
750776 )
751777
752778 if cgo:
753- archive_name = test_package + "_cgo "
779+ archive_name = f"_{name}#lib_cgo "
754780 lib_rule = merge_cgo_obj(
755781 name = name,
756782 tag='cgo',
@@ -762,12 +788,12 @@ def go_test(name:str, srcs:list, resources:list=None, data:list|dict=None, deps:
762788 package = archive_name,
763789 )
764790 else:
765- archive_name = test_package
791+ archive_name = f"_{name}#lib"
766792
767793 import_config = build_rule(
768794 name=f'_{name}#lib_import_config',
769795 cmd = _write_import_config_cmd(archive_name, test_package),
770- outs = [f'{test_package} .importconfig'],
796+ outs = [f'{name}_test_lib .importconfig'],
771797 visibility = visibility,
772798 test_only = True,
773799 labels = labels,
@@ -779,8 +805,8 @@ def go_test(name:str, srcs:list, resources:list=None, data:list|dict=None, deps:
779805 deps = deps,
780806 test_only = True,
781807 labels = labels,
782- test_package = test_package,
783808 external = external,
809+ test_package = test_package,
784810 )
785811 modinfo = _go_modinfo(
786812 name = name,
@@ -799,9 +825,16 @@ def go_test(name:str, srcs:list, resources:list=None, data:list|dict=None, deps:
799825 labels = labels,
800826 import_path = "main",
801827 _generate_pkg_info = False,
828+ _excluded_imports=excluded_packages,
829+ )
830+ cmds, tools = _go_binary_cmds(
831+ name=name,
832+ static=static,
833+ definitions=definitions,
834+ gcov=cgo,
835+ test=True,
836+ excluded_imports=excluded_packages,
802837 )
803- cmds, tools = _go_binary_cmds(name, static=static, definitions=definitions, gcov=cgo, test=True)
804-
805838
806839 test_cmd = f'$TEST {flags} 2>&1 | tee $TMP_DIR/test.results'
807840 worker_cmd = f'$(worker {worker})' if worker else ""
@@ -855,7 +888,7 @@ def go_test(name:str, srcs:list, resources:list=None, data:list|dict=None, deps:
855888 building_description = "Compiling...",
856889 needs_transitive_deps = True,
857890 output_is_complete = True,
858- pre_build = _collect_linker_flags(static, definitions),
891+ pre_build = _collect_linker_flags(static, definitions, excluded_packages ),
859892 env = env,
860893 )
861894
@@ -893,13 +926,19 @@ def go_benchmark(name:str, srcs:list, resources:list=None, data:list|dict=None,
893926 used to contruct the list of definitions passed to the linker.
894927 test_only (bool): If True, is only visible to test rules.
895928 """
896- test_package = name + '_lib'
929+ # For non-external tests, we exclude any local go_libraries from deps. These packages will be recompiled
930+ # as _{name}#lib with the test sources. This new package replaces the non-test version.
931+ excluded_packages = None
932+ if not external:
933+ excluded_packages = _pkg_local_deps(deps)
934+ deps += [_go_srcs_for_deps(tag(name, "local_srcs"), excluded_packages, True)]
935+
936+ test_package = name + '_lib' if external or not package_name() else None
897937 # Unfortunately we have to recompile this to build the test together with its library.
898938 lib_rule = go_library(
899939 name = '_%s#lib' % name,
900940 srcs = srcs,
901941 resources = resources,
902- package = test_package,
903942 deps = deps,
904943 _all_srcs=not external,
905944 labels = labels,
@@ -909,14 +948,7 @@ def go_benchmark(name:str, srcs:list, resources:list=None, data:list|dict=None,
909948 complete = False,
910949 _generate_import_config=False,
911950 import_path=test_package,
912- )
913- import_config = build_rule(
914- name=f'_{name}#lib_import_config',
915- cmd = _write_import_config_cmd(test_package, test_package),
916- outs = [f'{test_package}.importconfig'],
917- visibility=visibility,
918- test_only=True,
919- labels=labels,
951+ _excluded_imports=excluded_packages,
920952 )
921953 if cgo:
922954 lib_rule = merge_cgo_obj(
@@ -928,6 +960,14 @@ def go_benchmark(name:str, srcs:list, resources:list=None, data:list|dict=None,
928960 deps = deps,
929961 test_only = test_only,
930962 )
963+ import_config = build_rule(
964+ name=f'_{name}#lib_import_config',
965+ cmd = _write_import_config_cmd(test_package if test_package else lib_rule.removeprefix(":"), test_package),
966+ outs = [f'{test_package}.importconfig'],
967+ visibility=visibility,
968+ test_only=True,
969+ labels=labels,
970+ )
931971 main_rule = go_test_main(
932972 name = name,
933973 _tag = 'main',
@@ -952,8 +992,9 @@ def go_benchmark(name:str, srcs:list, resources:list=None, data:list|dict=None,
952992 labels = labels,
953993 test_only = test_only,
954994 import_path="main",
995+ _excluded_imports=excluded_packages,
955996 )
956- cmds, tools = _go_binary_cmds(name, static=static, definitions=definitions, gcov=cgo, test=True)
997+ cmds, tools = _go_binary_cmds(name, static=static, definitions=definitions, gcov=cgo, test=True, excluded_imports=excluded_packages )
957998
958999 return build_rule(
9591000 name=name,
@@ -973,7 +1014,7 @@ def go_benchmark(name:str, srcs:list, resources:list=None, data:list|dict=None,
9731014 needs_transitive_deps=True,
9741015 output_is_complete=True,
9751016 test_only=test_only,
976- pre_build = _collect_linker_flags(static, definitions),
1017+ pre_build = _collect_linker_flags(static, definitions, excluded_packages ),
9771018 )
9781019
9791020def go_test_main(name:str, srcs:list, test_package:str="", test_only:bool=False, external=False,
@@ -1112,7 +1153,7 @@ def _go_install_module(name:str, module:str, install:list, src:str, outs:list, d
11121153 else:
11131154 cmd += [_generate_pkg_import_cfg_cmd(name, "goroot.importconfig", '"$GOROOT"')]
11141155 cmd += [
1115- _aggregate_import_cfg_cmd(),
1156+ _aggregate_import_cfg_cmd(None ),
11161157 f"$TOOLS_PLEASE_GO install {build_tags} --trim_path $TMP_DIR --src_root=$(location {src}) --module_name={module} --importcfg=importconfig --go_tool=$TOOLS_GO {cc_tool_flag} --out=pkg/{CONFIG.OS}_{CONFIG.ARCH} " + " ".join(install),
11171158 "cat LD_FLAGS",
11181159 ]
@@ -1610,7 +1651,7 @@ def _set_go_env():
16101651 return f"export CGO_ENABLED=1 && {cmd}" if CONFIG.GO.CGO_ENABLED else cmd
16111652
16121653
1613- def _go_library_cmds(name, import_path:str="", complete=True, all_srcs=False, cover=True, filter_srcs=True, abi=False, embedcfg=None, pgo_file=None):
1654+ def _go_library_cmds(name, import_path:str="", complete=True, all_srcs=False, cover=True, filter_srcs=True, abi=False, embedcfg=None, pgo_file=None, excluded_imports=None ):
16141655 """Returns the commands to run for building a Go library."""
16151656 complete_flag = '-complete ' if complete else ''
16161657 embed_flag = ' -embedcfg $SRCS_EMBED' if embedcfg else ''
@@ -1640,7 +1681,7 @@ def _go_library_cmds(name, import_path:str="", complete=True, all_srcs=False, co
16401681 gen_import_cfg = _set_go_env()
16411682 if not CONFIG.GO.STDLIB:
16421683 gen_import_cfg += ' && ' + _generate_pkg_import_cfg_cmd(name, "goroot.importconfig", '"$GOROOT"')
1643- gen_import_cfg += ' && ' + _aggregate_import_cfg_cmd()
1684+ gen_import_cfg += ' && ' + _aggregate_import_cfg_cmd(excluded_imports )
16441685 prefix = ('export SRCS_GO="$PKG_DIR/*.go"; ' + gen_import_cfg) if all_srcs else gen_import_cfg
16451686
16461687 cmds = {
@@ -1657,7 +1698,7 @@ def _go_library_cmds(name, import_path:str="", complete=True, all_srcs=False, co
16571698 return cmds, tools
16581699
16591700
1660- def _go_binary_cmds(name, static=False, ldflags='', pkg_config='', definitions=None, gcov=False, split_debug=False, strip=None, test=False):
1701+ def _go_binary_cmds(name, excluded_imports=None, static=False, ldflags='', pkg_config='', definitions=None, gcov=False, split_debug=False, strip=None, test=False):
16611702 """Returns the commands to run for linking a Go binary."""
16621703
16631704 tool = '"$TOOLS_GO"' if CONFIG.GO.GO_LINK_TOOL else '"$TOOLS_GO" tool link'
@@ -1666,7 +1707,7 @@ def _go_binary_cmds(name, static=False, ldflags='', pkg_config='', definitions=N
16661707 gen_import_cfg = _set_go_env()
16671708 if not CONFIG.GO.STDLIB:
16681709 gen_import_cfg += ' && ' + _generate_pkg_import_cfg_cmd(name, "goroot.importconfig", '"$GOROOT"')
1669- gen_import_cfg += ' && ' + _aggregate_import_cfg_cmd()
1710+ gen_import_cfg += ' && ' + _aggregate_import_cfg_cmd(excluded_imports )
16701711
16711712 linkerdefs = []
16721713 if definitions is None:
@@ -1736,12 +1777,20 @@ def _go_import_path_cmd(import_path):
17361777 return ' && ln -s "$TMP_DIR" ' + import_path
17371778
17381779
1739- def _collect_linker_flags(static, definitions, strip=None):
1780+ def _collect_linker_flags(static, definitions, excluded_imports=None, strip=None):
17401781 """Returns a pre-build function to apply transitive linker flags to a go_binary rule."""
17411782 def collect_linker_flags(name):
17421783 ldflags, pkg_config = _get_ldflags_and_pkgconfig(name)
17431784 if ldflags or pkg_config:
1744- cmds, _ = _go_binary_cmds(name=name, static=static, ldflags=ldflags, pkg_config=pkg_config, definitions=definitions, strip=strip)
1785+ cmds, _ = _go_binary_cmds(
1786+ name=name,
1787+ static=static,
1788+ ldflags=ldflags,
1789+ pkg_config=pkg_config,
1790+ definitions=definitions,
1791+ strip=strip,
1792+ excluded_imports=excluded_imports,
1793+ )
17451794 for k, v in cmds.items():
17461795 set_command(name, k, v)
17471796 return collect_linker_flags
0 commit comments