bazel-contrib / bazel-contrib/rules_go

Cannot use generated `pgoprofile` with coverdata

Open
#4,385 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
1.5k
Forks
760
Avg merge
1d 11h
Merged PRs (30d)
12

Description

I am trying to specify `pgoprofile` in a `go_binary` that (recursively) depends on `@io_bazel_rules_go//go/tools/coverdata`. The `pgoprofile` is a label that points to a generated file. This runs into the following error:

```
ERROR: .../BUILD:8:11: in go_library rule [...]:
Traceback (most recent call last):
File "[...]/io_bazel_rules_go/go/private/rules/library.bzl", line 40, column 25, in _go_library_impl
archive = go.archive(go, source)
File "[...]/io_bazel_rules_go/go/private/actions/archive.bzl", line 77, column 17, in emit_archive
fail("Archive mode does not match {} is {} expected {}".format(a.data.label, mode_string(a.source.mode), mode_string(go.mode)))
Error in fail: Archive mode does not match @io_bazel_rules_go//go/tools/coverdata:coverdata is linux_amd64_pure_stripped expected linux_amd64_pure_stripped
```

I added the following debug messages just above the `fail` in `go/private/actions/archive.bzl`'s `emit_archive` to see what was going on:

```
files = []
for a in direct:
files.append(a.runfiles)
if a.source.mode != go.mode:
print("amd64: {} vs {}: eq={}", a.source.mode.amd64, go.mode.amd64, a.source.mode.amd64 == go.mode.amd64)
print("arm: {} vs {}: eq={}", a.source.mode.arm, go.mode.arm, a.source.mode.arm == go.mode.arm)
print("cover_format: {} vs {}: eq={}", a.source.mode.cover_format, go.mode.cover_format, a.source.mode.cover_format == go.mode.cover
_format)
print("debug: {} vs {}: eq={}", a.source.mode.debug, go.mode.debug, a.source.mode.debug == go.mode.debug)
print("gc_goopts: {} vs {}: eq={}", a.source.mode.gc_goopts, go.mode.gc_goopts, a.source.mode.gc_goopts == go.mode.gc_goopts)
print("gc_linkopts: {} vs {}: eq={}", a.source.mode.gc_linkopts, go.mode.gc_linkopts, a.source.mode.gc_linkopts == go.mode.gc_linkop
ts)
print("goarch: {} vs {}: eq={}", a.source.mode.goarch, go.mode.goarch, a.source.mode.goarch == go.mode.goarch)
print("goos: {} vs {}: eq={}", a.source.mode.goos, go.mode.goos, a.source.mode.goos == go.mode.goos)
print("link: {} vs {}: eq={}", a.source.mode.link, go.mode.link, a.source.mode.link == go.mode.link)
print("msan: {} vs {}: eq={}", a.source.mode.msan, go.mode.msan, a.source.mode.msan == go.mode.msan)
print("pgoprofile: {} vs {}: eq={}", a.source.mode.pgoprofile, go.mode.pgoprofile, a.source.mode.pgoprofile == go.mode.pgoprofile)
print("pure: {} vs {}: eq={}", a.source.mode.pure, go.mode.pure, a.source.mode.pure == go.mode.pure)
print("race: {} vs {}: eq={}", a.source.mode.race, go.mode.race, a.source.mode.race == go.mode.race)
print("stamp: {} vs {}: eq={}", a.source.mode.stamp, go.mode.stamp, a.source.mode.stamp == go.mode.stamp)
print("static: {} vs {}: eq={}", a.source.mode.static, go.mode.static, a.source.mode.static == go.mode.static)
print("strip: {} vs {}: eq={}", a.source.mode.strip, go.mode.strip, a.source.mode.strip == go.mode.strip)
print("tags: {} vs {}: eq={}", a.source.mode.tags, go.mode.tags, a.source.mode.tags == go.mode.tags)
fail("Archive mode does not match {} is {} expected {}".format(a.data.label, a.source.mode, go.mode))
runfiles = runfiles.merge_all(files)
```

Which revealed that the mismatching field of `mode` was the `pgoprofile` field:

```
DEBUG: [...]/go/private/actions/archive.bzl:77:18: amd64: {} vs {}: eq={} True
DEBUG: [...]/go/private/actions/archive.bzl:78:18: arm: {} vs {}: eq={} True
DEBUG: [...]/go/private/actions/archive.bzl:79:18: cover_format: {} vs {}: eq={} lcov lcov True
DEBUG: [...]/go/private/actions/archive.bzl:80:18: debug: {} vs {}: eq={} False False True
DEBUG: [...]/go/private/actions/archive.bzl:81:18: gc_goopts: {} vs {}: eq={} [] [] True
DEBUG: [...]/go/private/actions/archive.bzl:82:18: gc_linkopts: {} vs {}: eq={} [] [] True
DEBUG: [...]/go/private/actions/archive.bzl:83:18: goarch: {} vs {}: eq={} amd64 amd64 True
DEBUG: [...]/go/private/actions/archive.bzl:84:18: goos: {} vs {}: eq={} linux linux True
DEBUG: [...]/go/private/actions/archive.bzl:85:18: link: {} vs {}: eq={} normal normal True
DEBUG: [...]/go/private/actions/archive.bzl:86:18: msan: {} vs {}: eq={} False False True
DEBUG: [...]/go/private/actions/archive.bzl:87:18: pgoprofile: {} vs {}: eq={} False
DEBUG: [...]/go/private/actions/archive.bzl:88:18: pure: {} vs {}: eq={} True True True
DEBUG: [...]/go/private/actions/archive.bzl:89:18: race: {} vs {}: eq={} False False True
DEBUG: [...]/go/private/actions/archive.bzl:90:18: stamp: {} vs {}: eq={} True True True
DEBUG: [...]/go/private/actions/archive.bzl:91:18: static: {} vs {}: eq={} False False True
DEBUG: [...]/go/private/actions/archive.bzl:92:18: strip: {} vs {}: eq={} True True True
DEBUG: [...]/go/private/actions/archive.bzl:93:18: tags: {} vs {}: eq={} ["pgo"] ["pgo"] True
```

... So I believe the check at https://github.com/bazel-contrib/rules_go/blob/5e3cb433c184b5054480fe0e17fa70efc6805bf3/go/private/actions/archive.bzl#L82-L83 is failing because `a.source.mode.pgoprofile` and `go.mode.pgoprofile` are seen as different, even though they are the same auto-generated label.

Workarounds:

- Passing the PGO profile label as `--@io_bazel_rules_go//go/config:pgoprofile` works.
- Removing the dependency on `@io_bazel_rules_go//go/tools/coverdata` also works.

Contributor guide

Open the contributing guide

Research direction

Reproduce the failure with a go_binary using a generated pgoprofile and a recursive dependency on @io_bazel_rules_go//go/tools/coverdata. Start in go/private/actions/archive.bzl at emit_archive and inspect how the pgoprofile mode field is compared. Done means the generated profile is accepted with coverdata while the command-line pgoprofile workaround and existing mode checks remain valid.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
build-system, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.