Skip to content

Commit 9792f1c

Browse files
authored
link: add package path to build info (#4690)
**What type of PR is this?** Feature **What does this PR do? Why is it needed?** Binaries built with rules_go leave `BuildInfo.Path` empty even though analysis knows the main archive import path. `go version -m` and `runtime/debug` clients use this field to identify the main package, as they do for binaries built with `cmd/go`. Pass the main package import path to the linker and serialize it with Go 1.20's `debug.BuildInfo.String` API. Extend the builder and end-to-end tests to cover binaries with and without dependency metadata. This builds on #4595 and is deliberately limited to `BuildInfo.Path`; main-module identity and build settings will be proposed separately. **Which issues(s) does this PR fix?** Part of #3090. **Other notes for review** None.
1 parent 61f1929 commit 9792f1c

5 files changed

Lines changed: 41 additions & 8 deletions

File tree

go/private/actions/link.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ def emit_link(
178178

179179
builder_args.add("-o", executable)
180180
builder_args.add("-main", archive.data.file)
181+
builder_args.add("-main_package_path", archive.data.importpath)
181182
builder_args.add("-p", archive.data.importmap)
182183
tool_args.add_all(gc_linkopts)
183184
tool_args.add_all(go.toolchain.flags.link)

go/tools/builders/buildinfo.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@ func buildInfoDeps(modules []moduleInfo) []*debug.Module {
189189
return deps
190190
}
191191

192-
func modInfoData(modules []moduleInfo) string {
193-
info := &debug.BuildInfo{Deps: buildInfoDeps(modules)}
192+
func modInfoData(path string, modules []moduleInfo) string {
193+
info := &debug.BuildInfo{Path: path, Deps: buildInfoDeps(modules)}
194194
return buildInfoStart + info.String() + buildInfoEnd
195195
}
196196

go/tools/builders/buildinfo_test.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,14 @@ func TestBuildInfoDepsSortAndDedup(t *testing.T) {
180180
}
181181

182182
func TestModInfoDataRoundTrip(t *testing.T) {
183-
info := parseModInfoData(t, modInfoData([]moduleInfo{
183+
info := parseModInfoData(t, modInfoData("example.com/cmd/tool", []moduleInfo{
184184
{path: "golang.org/x/sync", version: "v0.8.0"},
185185
{path: "github.com/google/go-cmp", version: "v0.6.0"},
186186
{path: "github.com/google/go-cmp", version: "v0.6.0"},
187187
}))
188188

189-
if info.Path != "" {
190-
t.Fatalf("got Path %q; want empty", info.Path)
189+
if info.Path != "example.com/cmd/tool" {
190+
t.Fatalf("got Path %q; want %q", info.Path, "example.com/cmd/tool")
191191
}
192192
if info.Main.Path != "" || info.Main.Version != "" {
193193
t.Fatalf("got Main %+v; want empty", info.Main)
@@ -207,18 +207,22 @@ func TestModInfoDataRoundTrip(t *testing.T) {
207207
}
208208

209209
func TestModInfoDataWithoutDeps(t *testing.T) {
210-
info := parseModInfoData(t, modInfoData(nil))
210+
info := parseModInfoData(t, modInfoData("example.com/cmd/tool", nil))
211+
if info.Path != "example.com/cmd/tool" {
212+
t.Fatalf("got Path %q; want %q", info.Path, "example.com/cmd/tool")
213+
}
211214
if len(info.Deps) != 0 {
212215
t.Fatalf("got %d deps; want 0", len(info.Deps))
213216
}
214217
}
215218

216219
func TestModInfoDataFormat(t *testing.T) {
217-
got := modInfoData([]moduleInfo{
220+
got := modInfoData("example.com/cmd/tool", []moduleInfo{
218221
{path: "github.com/google/go-cmp", version: "v0.6.0"},
219222
{path: "golang.org/x/sync", version: "v0.8.0"},
220223
})
221224
want := buildInfoStart +
225+
"path\texample.com/cmd/tool\n" +
222226
"dep\tgithub.com/google/go-cmp\tv0.6.0\t\n" +
223227
"dep\tgolang.org/x/sync\tv0.8.0\t\n" +
224228
buildInfoEnd
@@ -227,6 +231,16 @@ func TestModInfoDataFormat(t *testing.T) {
227231
}
228232
}
229233

234+
func TestModInfoDataWithoutPathOrDeps(t *testing.T) {
235+
info := parseModInfoData(t, modInfoData("", nil))
236+
if info.Path != "" {
237+
t.Fatalf("got Path %q; want empty", info.Path)
238+
}
239+
if len(info.Deps) != 0 {
240+
t.Fatalf("got %d deps; want 0", len(info.Deps))
241+
}
242+
}
243+
230244
func TestShouldEmitBuildInfo(t *testing.T) {
231245
testCases := []struct {
232246
buildmode string

go/tools/builders/link.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func link(args []string) error {
4343
flags := flag.NewFlagSet("link", flag.ExitOnError)
4444
goenv := envFlags(flags)
4545
main := flags.String("main", "", "Path to the main archive.")
46+
mainPackagePath := flags.String("main_package_path", "", "Import path of the main package.")
4647
packagePath := flags.String("p", "", "Package path of the main archive.")
4748
outFile := flags.String("o", "", "Path to output file.")
4849
flags.Var(&archives, "arc", "Label, package path, and file name of a dependency, separated by '='")
@@ -98,7 +99,7 @@ func link(args []string) error {
9899
if err != nil {
99100
return err
100101
}
101-
modinfo = modInfoData(modules)
102+
modinfo = modInfoData(*mainPackagePath, modules)
102103
}
103104
importcfgName, err := buildImportcfgFileForLink(archives, *packageList, goenv.installSuffix, filepath.Dir(*outFile), modinfo)
104105
if err != nil {

tests/core/go_binary/buildinfo_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ type dep struct {
8484
8585
type output struct {
8686
OK bool ` + "`json:\"ok\"`" + `
87+
Path string ` + "`json:\"path\"`" + `
8788
MainPath string ` + "`json:\"main_path\"`" + `
8889
MainVersion string ` + "`json:\"main_version\"`" + `
8990
Deps []dep ` + "`json:\"deps\"`" + `
@@ -95,6 +96,7 @@ func main() {
9596
info, ok := debug.ReadBuildInfo()
9697
out := output{OK: ok}
9798
if info != nil {
99+
out.Path = info.Path
98100
out.MainPath = info.Main.Path
99101
out.MainVersion = info.Main.Version
100102
for _, module := range info.Deps {
@@ -147,6 +149,7 @@ import (
147149
148150
type output struct {
149151
OK bool ` + "`json:\"ok\"`" + `
152+
Path string ` + "`json:\"path\"`" + `
150153
MainPath string ` + "`json:\"main_path\"`" + `
151154
MainVersion string ` + "`json:\"main_version\"`" + `
152155
DepCount int ` + "`json:\"dep_count\"`" + `
@@ -156,6 +159,7 @@ func main() {
156159
info, ok := debug.ReadBuildInfo()
157160
out := output{OK: ok}
158161
if info != nil {
162+
out.Path = info.Path
159163
out.MainPath = info.Main.Path
160164
out.MainVersion = info.Main.Version
161165
out.DepCount = len(info.Deps)
@@ -181,6 +185,7 @@ type dep struct {
181185
182186
type output struct {
183187
OK bool ` + "`json:\"ok\"`" + `
188+
Path string ` + "`json:\"path\"`" + `
184189
MainPath string ` + "`json:\"main_path\"`" + `
185190
MainVersion string ` + "`json:\"main_version\"`" + `
186191
Deps []dep ` + "`json:\"deps\"`" + `
@@ -192,6 +197,7 @@ func main() {
192197
info, ok := debug.ReadBuildInfo()
193198
out := output{OK: ok}
194199
if info != nil {
200+
out.Path = info.Path
195201
out.MainPath = info.Main.Path
196202
out.MainVersion = info.Main.Version
197203
for _, module := range info.Deps {
@@ -360,13 +366,15 @@ type dep struct {
360366

361367
type withDepOutput struct {
362368
OK bool `json:"ok"`
369+
Path string `json:"path"`
363370
MainPath string `json:"main_path"`
364371
MainVersion string `json:"main_version"`
365372
Deps []dep `json:"deps"`
366373
}
367374

368375
type stdlibOnlyOutput struct {
369376
OK bool `json:"ok"`
377+
Path string `json:"path"`
370378
MainPath string `json:"main_path"`
371379
MainVersion string `json:"main_version"`
372380
DepCount int `json:"dep_count"`
@@ -385,6 +393,9 @@ func TestReadBuildInfoDeps(t *testing.T) {
385393
if !got.OK {
386394
t.Fatalf("ReadBuildInfo returned ok=false: %+v", got)
387395
}
396+
if got.Path != "with_dep" {
397+
t.Fatalf("got Path %q; want %q", got.Path, "with_dep")
398+
}
388399
if got.MainPath != "" || got.MainVersion != "" {
389400
t.Fatalf("got Main %q %q; want empty", got.MainPath, got.MainVersion)
390401
}
@@ -428,6 +439,9 @@ func TestReadBuildInfoWithoutMetadata(t *testing.T) {
428439
if !got.OK {
429440
t.Fatalf("ReadBuildInfo returned ok=false: %+v", got)
430441
}
442+
if got.Path != "stdlib_only" {
443+
t.Fatalf("got Path %q; want %q", got.Path, "stdlib_only")
444+
}
431445
if got.MainPath != "" || got.MainVersion != "" {
432446
t.Fatalf("got Main %q %q; want empty", got.MainPath, got.MainVersion)
433447
}
@@ -461,6 +475,9 @@ func TestReadBuildInfoVersionlessDep(t *testing.T) {
461475
if !got.OK {
462476
t.Fatalf("ReadBuildInfo returned ok=false: %+v", got)
463477
}
478+
if got.Path != "with_versionless_dep" {
479+
t.Fatalf("got Path %q; want %q", got.Path, "with_versionless_dep")
480+
}
464481

465482
foundVersionless := false
466483
for _, dep := range got.Deps {

0 commit comments

Comments
 (0)