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