xref: /aosp_15_r20/external/bazelbuild-rules_go/go/private/rules/test.bzl (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
1# Copyright 2014 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#    http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15load(
16    "//go/private:context.bzl",
17    "go_context",
18)
19load(
20    "//go/private:common.bzl",
21    "as_list",
22    "asm_exts",
23    "cgo_exts",
24    "go_exts",
25    "split_srcs",
26)
27load(
28    "//go/private:go_toolchain.bzl",
29    "GO_TOOLCHAIN",
30)
31load(
32    "//go/private/rules:binary.bzl",
33    "gc_linkopts",
34)
35load(
36    "//go/private:providers.bzl",
37    "GoArchive",
38    "GoLibrary",
39    "GoSource",
40    "INFERRED_PATH",
41    "get_archive",
42)
43load(
44    "//go/private/rules:transition.bzl",
45    "go_transition",
46)
47load(
48    "//go/private:mode.bzl",
49    "LINKMODE_NORMAL",
50)
51load(
52    "@bazel_skylib//lib:structs.bzl",
53    "structs",
54)
55
56def _go_test_impl(ctx):
57    """go_test_impl implements go testing.
58
59    It emits an action to run the test generator, and then compiles the
60    test into a binary."""
61
62    go = go_context(ctx)
63
64    # Compile the library to test with internal white box tests
65    internal_library = go.new_library(go, testfilter = "exclude")
66    internal_source = go.library_to_source(go, ctx.attr, internal_library, ctx.coverage_instrumented())
67    internal_archive = go.archive(go, internal_source)
68    go_srcs = split_srcs(internal_source.srcs).go
69
70    # Compile the library with the external black box tests
71    external_library = go.new_library(
72        go,
73        name = internal_library.name + "_test",
74        importpath = internal_library.importpath + "_test",
75        testfilter = "only",
76    )
77    external_source = go.library_to_source(go, struct(
78        srcs = [struct(files = go_srcs)],
79        embedsrcs = [struct(files = internal_source.embedsrcs)],
80        deps = internal_archive.direct + [internal_archive],
81        x_defs = ctx.attr.x_defs,
82    ), external_library, ctx.coverage_instrumented())
83    external_source, internal_archive = _recompile_external_deps(go, external_source, internal_archive, [t.label for t in ctx.attr.embed])
84    external_archive = go.archive(go, external_source)
85
86    # now generate the main function
87    repo_relative_rundir = ctx.attr.rundir or ctx.label.package or "."
88    if ctx.label.workspace_name:
89        # The test is contained in an external repository (Label.workspace_name is always the empty
90        # string for the main repository, which is the canonical repository name of this repo).
91        # The test runner cd's into the directory corresponding to the main repository, so walk up
92        # and then down.
93        run_dir = "../" + ctx.label.workspace_name + "/" + repo_relative_rundir
94    else:
95        run_dir = repo_relative_rundir
96
97    main_go = go.declare_file(go, path = "testmain.go")
98    arguments = go.builder_args(go, "gentestmain")
99    arguments.add("-output", main_go)
100    if go.coverage_enabled:
101        if go.mode.race:
102            arguments.add("-cover_mode", "atomic")
103        else:
104            arguments.add("-cover_mode", "set")
105        arguments.add("-cover_format", go.cover_format)
106    arguments.add(
107        # the l is the alias for the package under test, the l_test must be the
108        # same with the test suffix
109        "-import",
110        "l=" + internal_source.library.importpath,
111    )
112    arguments.add(
113        "-import",
114        "l_test=" + external_source.library.importpath,
115    )
116    arguments.add("-pkgname", internal_source.library.importpath)
117    arguments.add_all(go_srcs, before_each = "-src", format_each = "l=%s")
118    ctx.actions.run(
119        inputs = go_srcs,
120        outputs = [main_go],
121        mnemonic = "GoTestGenTest",
122        executable = go.toolchain._builder,
123        arguments = [arguments],
124    )
125
126    test_gc_linkopts = gc_linkopts(ctx)
127    if not go.mode.debug:
128        # Disable symbol table and DWARF generation for test binaries.
129        test_gc_linkopts.extend(["-s", "-w"])
130
131    # Link in the run_dir global for bzltestutil
132    test_gc_linkopts.extend(["-X", "github.com/bazelbuild/rules_go/go/tools/bzltestutil.RunDir=" + run_dir])
133
134    # Now compile the test binary itself
135    test_library = GoLibrary(
136        name = go.label.name + "~testmain",
137        label = go.label,
138        importpath = "testmain",
139        importmap = "testmain",
140        importpath_aliases = (),
141        pathtype = INFERRED_PATH,
142        is_main = True,
143        resolve = None,
144    )
145    test_deps = external_archive.direct + [external_archive] + ctx.attr._testmain_additional_deps
146    if ctx.configuration.coverage_enabled:
147        test_deps.append(go.coverdata)
148    test_source = go.library_to_source(go, struct(
149        srcs = [struct(files = [main_go])],
150        deps = test_deps,
151    ), test_library, False)
152    test_archive, executable, runfiles = go.binary(
153        go,
154        name = ctx.label.name,
155        source = test_source,
156        test_archives = [internal_archive.data],
157        gc_linkopts = test_gc_linkopts,
158        version_file = ctx.version_file,
159        info_file = ctx.info_file,
160    )
161
162    env = {}
163    for k, v in ctx.attr.env.items():
164        env[k] = ctx.expand_location(v, ctx.attr.data)
165
166    run_environment_info = RunEnvironmentInfo(env, ctx.attr.env_inherit)
167
168    # Bazel only looks for coverage data if the test target has an
169    # InstrumentedFilesProvider. If the provider is found and at least one
170    # source file is present, Bazel will set the COVERAGE_OUTPUT_FILE
171    # environment variable during tests and will save that file to the build
172    # events + test outputs.
173    return [
174        test_archive,
175        DefaultInfo(
176            files = depset([executable]),
177            runfiles = runfiles,
178            executable = executable,
179        ),
180        OutputGroupInfo(
181            compilation_outputs = [internal_archive.data.file],
182        ),
183        coverage_common.instrumented_files_info(
184            ctx,
185            source_attributes = ["srcs"],
186            dependency_attributes = ["data", "deps", "embed", "embedsrcs"],
187            extensions = ["go"],
188        ),
189        run_environment_info,
190    ]
191
192_go_test_kwargs = {
193    "implementation": _go_test_impl,
194    "attrs": {
195        "data": attr.label_list(
196            allow_files = True,
197            doc = """List of files needed by this rule at run-time. This may include data files
198            needed or other programs that may be executed. The [bazel] package may be
199            used to locate run files; they may appear in different places depending on the
200            operating system and environment. See [data dependencies] for more
201            information on data files.
202            """,
203        ),
204        "srcs": attr.label_list(
205            allow_files = go_exts + asm_exts + cgo_exts,
206            doc = """The list of Go source files that are compiled to create the package.
207            Only `.go` and `.s` files are permitted, unless the `cgo`
208            attribute is set, in which case,
209            `.c .cc .cpp .cxx .h .hh .hpp .hxx .inc .m .mm`
210            files are also permitted. Files may be filtered at build time
211            using Go [build constraints].
212            """,
213        ),
214        "deps": attr.label_list(
215            providers = [GoLibrary],
216            doc = """List of Go libraries this test imports directly.
217            These may be go_library rules or compatible rules with the [GoLibrary] provider.
218            """,
219            cfg = go_transition,
220        ),
221        "embed": attr.label_list(
222            providers = [GoLibrary],
223            doc = """List of Go libraries whose sources should be compiled together with this
224            package's sources. Labels listed here must name `go_library`,
225            `go_proto_library`, or other compatible targets with the [GoLibrary] and
226            [GoSource] providers. Embedded libraries must have the same `importpath` as
227            the embedding library. At most one embedded library may have `cgo = True`,
228            and the embedding library may not also have `cgo = True`. See [Embedding]
229            for more information.
230            """,
231            cfg = go_transition,
232        ),
233        "embedsrcs": attr.label_list(
234            allow_files = True,
235            doc = """The list of files that may be embedded into the compiled package using
236            `//go:embed` directives. All files must be in the same logical directory
237            or a subdirectory as source files. All source files containing `//go:embed`
238            directives must be in the same logical directory. It's okay to mix static and
239            generated source files and static and generated embeddable files.
240            """,
241        ),
242        "env": attr.string_dict(
243            doc = """Environment variables to set for the test execution.
244            The values (but not keys) are subject to
245            [location expansion](https://docs.bazel.build/versions/main/skylark/macros.html) but not full
246            [make variable expansion](https://docs.bazel.build/versions/main/be/make-variables.html).
247            """,
248        ),
249        "env_inherit": attr.string_list(
250            doc = """Environment variables to inherit from the external environment.
251            """,
252        ),
253        "importpath": attr.string(
254            doc = """The import path of this test. Tests can't actually be imported, but this
255            may be used by [go_path] and other tools to report the location of source
256            files. This may be inferred from embedded libraries.
257            """,
258        ),
259        "gc_goopts": attr.string_list(
260            doc = """List of flags to add to the Go compilation command when using the gc compiler.
261            Subject to ["Make variable"] substitution and [Bourne shell tokenization].
262            """,
263        ),
264        "gc_linkopts": attr.string_list(
265            doc = """List of flags to add to the Go link command when using the gc compiler.
266            Subject to ["Make variable"] substitution and [Bourne shell tokenization].
267            """,
268        ),
269        "rundir": attr.string(
270            doc = """ A directory to cd to before the test is run.
271            This should be a path relative to the root directory of the
272            repository in which the test is defined, which can be the main or an
273            external repository.
274
275            The default behaviour is to change to the relative path
276            corresponding to the test's package, which replicates the normal
277            behaviour of `go test` so it is easy to write compatible tests.
278
279            Setting it to `.` makes the test behave the normal way for a bazel
280            test, except that the working directory is always that of the test's
281            repository, which is not necessarily the main repository.
282
283            Note: If runfile symlinks are disabled (such as on Windows by
284            default), the test will run in the working directory set by Bazel,
285            which is the subdirectory of the runfiles directory corresponding to
286            the main repository.
287            """,
288        ),
289        "x_defs": attr.string_dict(
290            doc = """Map of defines to add to the go link command.
291            See [Defines and stamping] for examples of how to use these.
292            """,
293        ),
294        "linkmode": attr.string(
295            default = LINKMODE_NORMAL,
296            doc = """Determines how the binary should be built and linked. This accepts some of
297            the same values as `go build -buildmode` and works the same way.
298            <br><br>
299            <ul>
300            <li>`normal`: Builds a normal executable with position-dependent code.</li>
301            <li>`pie`: Builds a position-independent executable.</li>
302            <li>`plugin`: Builds a shared library that can be loaded as a Go plugin. Only supported on platforms that support plugins.</li>
303            <li>`c-shared`: Builds a shared library that can be linked into a C program.</li>
304            <li>`c-archive`: Builds an archive that can be linked into a C program.</li>
305            </ul>
306            """,
307        ),
308        "cgo": attr.bool(
309            doc = """
310            If `True`, the package may contain [cgo] code, and `srcs` may contain
311            C, C++, Objective-C, and Objective-C++ files and non-Go assembly files.
312            When cgo is enabled, these files will be compiled with the C/C++ toolchain
313            and included in the package. Note that this attribute does not force cgo
314            to be enabled. Cgo is enabled for non-cross-compiling builds when a C/C++
315            toolchain is configured.
316            """,
317        ),
318        "cdeps": attr.label_list(
319            doc = """The list of other libraries that the c code depends on.
320            This can be anything that would be allowed in [cc_library deps]
321            Only valid if `cgo` = `True`.
322            """,
323        ),
324        "cppopts": attr.string_list(
325            doc = """List of flags to add to the C/C++ preprocessor command.
326            Subject to ["Make variable"] substitution and [Bourne shell tokenization].
327            Only valid if `cgo` = `True`.
328            """,
329        ),
330        "copts": attr.string_list(
331            doc = """List of flags to add to the C compilation command.
332            Subject to ["Make variable"] substitution and [Bourne shell tokenization].
333            Only valid if `cgo` = `True`.
334            """,
335        ),
336        "cxxopts": attr.string_list(
337            doc = """List of flags to add to the C++ compilation command.
338            Subject to ["Make variable"] substitution and [Bourne shell tokenization].
339            Only valid if `cgo` = `True`.
340            """,
341        ),
342        "clinkopts": attr.string_list(
343            doc = """List of flags to add to the C link command.
344            Subject to ["Make variable"] substitution and [Bourne shell tokenization].
345            Only valid if `cgo` = `True`.
346            """,
347        ),
348        "pure": attr.string(
349            default = "auto",
350            doc = """Controls whether cgo source code and dependencies are compiled and linked,
351            similar to setting `CGO_ENABLED`. May be one of `on`, `off`,
352            or `auto`. If `auto`, pure mode is enabled when no C/C++
353            toolchain is configured or when cross-compiling. It's usually better to
354            control this on the command line with
355            `--@io_bazel_rules_go//go/config:pure`. See [mode attributes], specifically
356            [pure].
357            """,
358        ),
359        "static": attr.string(
360            default = "auto",
361            doc = """Controls whether a binary is statically linked. May be one of `on`,
362            `off`, or `auto`. Not available on all platforms or in all
363            modes. It's usually better to control this on the command line with
364            `--@io_bazel_rules_go//go/config:static`. See [mode attributes],
365            specifically [static].
366            """,
367        ),
368        "race": attr.string(
369            default = "auto",
370            doc = """Controls whether code is instrumented for race detection. May be one of
371            `on`, `off`, or `auto`. Not available when cgo is
372            disabled. In most cases, it's better to control this on the command line with
373            `--@io_bazel_rules_go//go/config:race`. See [mode attributes], specifically
374            [race].
375            """,
376        ),
377        "msan": attr.string(
378            default = "auto",
379            doc = """Controls whether code is instrumented for memory sanitization. May be one of
380            `on`, `off`, or `auto`. Not available when cgo is
381            disabled. In most cases, it's better to control this on the command line with
382            `--@io_bazel_rules_go//go/config:msan`. See [mode attributes], specifically
383            [msan].
384            """,
385        ),
386        "gotags": attr.string_list(
387            doc = """Enables a list of build tags when evaluating [build constraints]. Useful for
388            conditional compilation.
389            """,
390        ),
391        "goos": attr.string(
392            default = "auto",
393            doc = """Forces a binary to be cross-compiled for a specific operating system. It's
394            usually better to control this on the command line with `--platforms`.
395
396            This disables cgo by default, since a cross-compiling C/C++ toolchain is
397            rarely available. To force cgo, set `pure` = `off`.
398
399            See [Cross compilation] for more information.
400            """,
401        ),
402        "goarch": attr.string(
403            default = "auto",
404            doc = """Forces a binary to be cross-compiled for a specific architecture. It's usually
405            better to control this on the command line with `--platforms`.
406
407            This disables cgo by default, since a cross-compiling C/C++ toolchain is
408            rarely available. To force cgo, set `pure` = `off`.
409
410            See [Cross compilation] for more information.
411            """,
412        ),
413        "_go_context_data": attr.label(default = "//:go_context_data", cfg = go_transition),
414        "_testmain_additional_deps": attr.label_list(
415            providers = [GoLibrary],
416            default = ["//go/tools/bzltestutil"],
417            cfg = go_transition,
418        ),
419        # Required for Bazel to collect coverage of instrumented C/C++ binaries
420        # executed by go_test.
421        # This is just a shell script and thus cheap enough to depend on
422        # unconditionally.
423        "_collect_cc_coverage": attr.label(
424            default = "@bazel_tools//tools/test:collect_cc_coverage",
425            cfg = "exec",
426        ),
427        # Required for Bazel to merge coverage reports for Go and other
428        # languages into a single report per test.
429        # Using configuration_field ensures that the tool is only built when
430        # run with bazel coverage, not with bazel test.
431        "_lcov_merger": attr.label(
432            default = configuration_field(fragment = "coverage", name = "output_generator"),
433            cfg = "exec",
434        ),
435        "_allowlist_function_transition": attr.label(
436            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
437        ),
438    },
439    "executable": True,
440    "test": True,
441    "toolchains": [GO_TOOLCHAIN],
442    "doc": """This builds a set of tests that can be run with `bazel test`.<br><br>
443    To run all tests in the workspace, and print output on failure (the
444    equivalent of `go test ./...`), run<br>
445    ```
446    bazel test --test_output=errors //...
447    ```<br><br>
448    To run a Go benchmark test, run<br>
449    ```
450    bazel run //path/to:test -- -test.bench=.
451    ```<br><br>
452    You can run specific tests by passing the `--test_filter=pattern
453    <test_filter_>` argument to Bazel. You can pass arguments to tests by passing
454    `--test_arg=arg <test_arg_>` arguments to Bazel, and you can set environment
455    variables in the test environment by passing
456    `--test_env=VAR=value <test_env_>`. You can terminate test execution after the first
457    failure by passing the `--test_runner_fast_fast <test_runner_fail_fast_>` argument
458    to Bazel. This is equivalent to passing `--test_arg=-failfast <test_arg_>`.<br><br>
459    To write structured testlog information to Bazel's `XML_OUTPUT_FILE`, tests
460    ran with `bazel test` execute using a wrapper. This functionality can be
461    disabled by setting `GO_TEST_WRAP=0` in the test environment. Additionally,
462    the testbinary can be invoked with `-test.v` by setting
463    `GO_TEST_WRAP_TESTV=1` in the test environment; this will result in the
464    `XML_OUTPUT_FILE` containing more granular data.<br><br>
465    ***Note:*** To interoperate cleanly with old targets generated by [Gazelle], `name`
466    should be `go_default_test` for internal tests and
467    `go_default_xtest` for external tests. Gazelle now generates
468    the name  based on the last component of the path. For example, a test
469    in `//foo/bar` is named `bar_test`, and uses internal and external
470    sources.
471    """,
472}
473
474go_test = rule(**_go_test_kwargs)
475
476def _recompile_external_deps(go, external_source, internal_archive, library_labels):
477    """Recompiles some archives in order to split internal and external tests.
478
479    go_test, like 'go test', splits tests into two separate archives: an
480    internal archive ('package foo') and an external archive
481    ('package foo_test'). The library under test is embedded into the internal
482    archive. The external archive may import it and may depend on symbols
483    defined in the internal test files.
484
485    To avoid conflicts, the library under test must not be linked into the test
486    binary, since the internal test archive embeds the same sources.
487    Libraries imported by the external test that transitively import the
488    library under test must be recompiled too, or the linker will complain that
489    export data they were compiled with doesn't match the export data they
490    are linked with.
491
492    This function identifies which archives may need to be recompiled, then
493    declares new output files and actions to recompile them. This is an
494    unfortunately an expensive process requiring O(V+E) time and space in the
495    size of the test's dependency graph for each test.
496
497    Args:
498        go: go object returned by go_context.
499        external_source: GoSource for the external archive.
500        internal_archive: GoArchive for the internal archive.
501        library_labels: labels for embedded libraries under test.
502
503    Returns:
504        external_soruce: recompiled GoSource for the external archive. If no
505            recompilation is needed, the original GoSource is returned.
506        internal_archive: recompiled GoArchive for the internal archive. If no
507            recompilation is needed, the original GoSource is returned.
508    """
509
510    # If no libraries are embedded in the internal archive, then nothing needs
511    # to be recompiled.
512    if not library_labels:
513        return external_source, internal_archive
514
515    # Build a map from labels to GoArchiveData.
516    # If none of the librares embedded in the internal archive are in the
517    # dependency graph, then nothing needs to be recompiled.
518    arc_data_list = depset(transitive = [get_archive(dep).transitive for dep in external_source.deps]).to_list()
519    label_to_arc_data = {a.label: a for a in arc_data_list}
520    if all([l not in label_to_arc_data for l in library_labels]):
521        return external_source, internal_archive
522
523    # Build a depth-first post-order list of dependencies starting with the
524    # external archive. Each archive appears after its dependencies and before
525    # its dependents.
526    #
527    # This is tricky because Starlark doesn't support recursion or while loops.
528    # We simulate a while loop by iterating over a list of 2N elements where
529    # N is the number of archives. Each archive is pushed onto the stack
530    # twice: once before its dependencies are pushed, and once after.
531
532    # dep_list is the post-order list of dependencies we're building.
533    dep_list = []
534
535    # stack is a stack of targets to process. We're done when it's empty.
536    stack = [get_archive(dep).data.label for dep in external_source.deps]
537
538    # deps_pushed tracks the status of each target.
539    # DEPS_UNPROCESSED means the target is on the stack, but its dependencies
540    # are not.
541    # Non-negative integers are the number of dependencies on the stack that
542    # still need to be processed.
543    # A target is on the stack if its status is DEPS_UNPROCESSED or 0.
544    DEPS_UNPROCESSED = -1
545    deps_pushed = {l: DEPS_UNPROCESSED for l in stack}
546
547    # dependents maps labels to lists of known dependents. When a target is
548    # processed, its dependents' deps_pushed count is deprecated.
549    dependents = {l: [] for l in stack}
550
551    # step is a list to iterate over to simulate a while loop. i tracks
552    # iterations.
553    step = [None] * (2 * len(arc_data_list))
554    i = 0
555    for _ in step:
556        if len(stack) == 0:
557            break
558        i += 1
559
560        label = stack.pop()
561        if deps_pushed[label] == 0:
562            # All deps have been added to dep_list. Append this target to the
563            # list. If a dependent is not waiting for anything else, push
564            # it back onto the stack.
565            dep_list.append(label)
566            for p in dependents.get(label, []):
567                deps_pushed[p] -= 1
568                if deps_pushed[p] == 0:
569                    stack.append(p)
570            continue
571
572        # deps_pushed[label] == None, indicating we don't know whether this
573        # targets dependencies have been processed. Other targets processed
574        # earlier may depend on them.
575        deps_pushed[label] = 0
576        arc_data = label_to_arc_data[label]
577        for c in arc_data._dep_labels:
578            if c not in deps_pushed:
579                # Dependency not seen yet; push it.
580                stack.append(c)
581                deps_pushed[c] = None
582                deps_pushed[label] += 1
583                dependents[c] = [label]
584            elif deps_pushed[c] != 0:
585                # Dependency pushed, not processed; wait for it.
586                deps_pushed[label] += 1
587                dependents[c].append(label)
588        if deps_pushed[label] == 0:
589            # No dependencies to wait for; push self.
590            stack.append(label)
591    if i != len(step):
592        fail("assertion failed: iterated %d times instead of %d" % (i, len(step)))
593
594    # Determine which dependencies need to be recompiled because they depend
595    # on embedded libraries.
596    need_recompile = {}
597    for label in dep_list:
598        arc_data = label_to_arc_data[label]
599        need_recompile[label] = any([
600            dep in library_labels or need_recompile[dep]
601            for dep in arc_data._dep_labels
602        ])
603
604    # Recompile the internal archive without dependencies that need
605    # recompilation. This breaks a cycle which occurs because the deps list
606    # is shared between the internal and external archive. The internal archive
607    # can't import anything that imports itself.
608    internal_source = internal_archive.source
609
610    internal_deps = []
611
612    # Pass internal dependencies that need to be recompiled down to the builder to check if the internal archive
613    # tries to import any of the dependencies. If there is, that means that there is a dependency cycle.
614    need_recompile_deps = []
615    for dep in internal_source.deps:
616        dep_data = get_archive(dep).data
617        if not need_recompile[dep_data.label]:
618            internal_deps.append(dep)
619        else:
620            need_recompile_deps.append(dep_data.importpath)
621
622    x_defs = dict(internal_source.x_defs)
623    x_defs.update(internal_archive.x_defs)
624    attrs = structs.to_dict(internal_source)
625    attrs["deps"] = internal_deps
626    attrs["x_defs"] = x_defs
627    internal_source = GoSource(**attrs)
628    internal_archive = go.archive(go, internal_source, _recompile_suffix = ".recompileinternal", recompile_internal_deps = need_recompile_deps)
629
630    # Build a map from labels to possibly recompiled GoArchives.
631    label_to_archive = {}
632    i = 0
633    for label in dep_list:
634        i += 1
635        recompile_suffix = ".recompile%d" % i
636
637        # If this library is the internal archive, use the recompiled version.
638        if label == internal_archive.data.label:
639            label_to_archive[label] = internal_archive
640            continue
641
642        # If this is a library embedded into the internal test archive,
643        # use the internal test archive instead.
644        if label in library_labels:
645            label_to_archive[label] = internal_archive
646            continue
647
648        # Create a stub GoLibrary and GoSource from the archive data.
649        arc_data = label_to_arc_data[label]
650        library = GoLibrary(
651            name = arc_data.name,
652            label = arc_data.label,
653            importpath = arc_data.importpath,
654            importmap = arc_data.importmap,
655            importpath_aliases = arc_data.importpath_aliases,
656            pathtype = arc_data.pathtype,
657            resolve = None,
658            testfilter = None,
659            is_main = False,
660        )
661        deps = [label_to_archive[d] for d in arc_data._dep_labels]
662        source = GoSource(
663            library = library,
664            mode = go.mode,
665            srcs = as_list(arc_data.srcs),
666            orig_srcs = as_list(arc_data.orig_srcs),
667            orig_src_map = dict(zip(arc_data.srcs, arc_data._orig_src_map)),
668            cover = arc_data._cover,
669            embedsrcs = as_list(arc_data._embedsrcs),
670            x_defs = dict(arc_data._x_defs),
671            deps = deps,
672            gc_goopts = as_list(arc_data._gc_goopts),
673            runfiles = go._ctx.runfiles(files = arc_data.data_files),
674            cgo = arc_data._cgo,
675            cdeps = as_list(arc_data._cdeps),
676            cppopts = as_list(arc_data._cppopts),
677            copts = as_list(arc_data._copts),
678            cxxopts = as_list(arc_data._cxxopts),
679            clinkopts = as_list(arc_data._clinkopts),
680            cgo_exports = as_list(arc_data._cgo_exports),
681        )
682
683        # If this archive needs to be recompiled, use go.archive.
684        # Otherwise, create a stub GoArchive, using the original file.
685        if need_recompile[label]:
686            recompile_suffix = ".recompile%d" % i
687            archive = go.archive(go, source, _recompile_suffix = recompile_suffix)
688        else:
689            archive = GoArchive(
690                source = source,
691                data = arc_data,
692                direct = deps,
693                libs = depset(direct = [arc_data.file], transitive = [a.libs for a in deps]),
694                transitive = depset(direct = [arc_data], transitive = [a.transitive for a in deps]),
695                x_defs = source.x_defs,
696                cgo_deps = depset(direct = arc_data._cgo_deps, transitive = [a.cgo_deps for a in deps]),
697                cgo_exports = depset(direct = list(source.cgo_exports), transitive = [a.cgo_exports for a in deps]),
698                runfiles = source.runfiles,
699                mode = go.mode,
700            )
701        label_to_archive[label] = archive
702
703    # Finally, we need to replace external_source.deps with the recompiled
704    # archives.
705    attrs = structs.to_dict(external_source)
706    attrs["deps"] = [label_to_archive[get_archive(dep).data.label] for dep in external_source.deps]
707    return GoSource(**attrs), internal_archive
708