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