From 21e441c461a79be41de20a99e6001098142946e6 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Wed, 14 Oct 2020 16:21:39 -0400 Subject: [PATCH] cmd/go: rewrite paths for overlaid files using -trimpath Pass the trimpath flag to cmd/compile to use the correct file paths for files that are overlaid: that is, the "destination" path in the overlay's Replace mapping rather than the "source" path. Also fix paths to go source files provided to the gccgo compiler. For #39958 Change-Id: I3741aeb2272bd0d5aa32cb28133b61e58264fd39 Reviewed-on: https://go-review.googlesource.com/c/go/+/257198 Trust: Michael Matloob Trust: Bryan C. Mills Run-TryBot: Michael Matloob TryBot-Result: Go Bot Reviewed-by: Jay Conrod Reviewed-by: Bryan C. Mills --- src/cmd/go/internal/work/exec.go | 2 + src/cmd/go/internal/work/gc.go | 25 +++++--- src/cmd/go/internal/work/gccgo.go | 33 +++++++++-- src/cmd/go/testdata/script/build_overlay.txt | 58 ++++++++++++++++--- src/cmd/go/testdata/script/build_trimpath.txt | 28 ++++++++- 5 files changed, 125 insertions(+), 21 deletions(-) diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index 074bcc16c0..824a4b5a0a 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -2214,6 +2214,8 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s // when -trimpath is enabled. if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") { if cfg.BuildTrimpath { + // TODO(#39958): handle overlays + // Keep in sync with Action.trimpath. // The trimmed paths are a little different, but we need to trim in the // same situations. diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go index 1f15654c79..56ad1872be 100644 --- a/src/cmd/go/internal/work/gc.go +++ b/src/cmd/go/internal/work/gc.go @@ -152,8 +152,6 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, s // so these paths can be handed directly to tools. // Deleted files won't show up in when scanning directories earlier, // so OverlayPath will never return "" (meaning a deleted file) here. - // TODO(#39958): Handle -trimprefix and other cases where - // tools depend on the names of the files that are passed in. // TODO(#39958): Handle cases where the package directory // doesn't exist on disk (this can happen when all the package's // files are in an overlay): the code expects the package directory @@ -167,7 +165,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, s args = append(args, f) } - output, err = b.runOut(a, p.Dir, nil, args...) + output, err = b.runOut(a, base.Cwd, nil, args...) return ofile, output, err } @@ -256,17 +254,28 @@ func (a *Action) trimpath() string { } rewrite := objdir + "=>" - // For "go build -trimpath", rewrite package source directory - // to a file system-independent path (just the import path). + rewriteDir := a.Package.Dir if cfg.BuildTrimpath { if m := a.Package.Module; m != nil && m.Version != "" { - rewrite += ";" + a.Package.Dir + "=>" + m.Path + "@" + m.Version + strings.TrimPrefix(a.Package.ImportPath, m.Path) + rewriteDir = m.Path + "@" + m.Version + strings.TrimPrefix(a.Package.ImportPath, m.Path) } else { - rewrite += ";" + a.Package.Dir + "=>" + a.Package.ImportPath + rewriteDir = a.Package.ImportPath } + rewrite += ";" + a.Package.Dir + "=>" + rewriteDir } - // TODO(#39958): Add rewrite rules for overlaid files. + // Add rewrites for overlays. The 'from' and 'to' paths in overlays don't need to have + // same basename, so go from the overlay contents file path (passed to the compiler) + // to the path the disk path would be rewritten to. + if fsys.OverlayFile != "" { + for _, filename := range a.Package.AllFiles() { + overlayPath, ok := fsys.OverlayPath(filepath.Join(a.Package.Dir, filename)) + if !ok { + continue + } + rewrite += ";" + overlayPath + "=>" + filepath.Join(rewriteDir, filename) + } + } return rewrite } diff --git a/src/cmd/go/internal/work/gccgo.go b/src/cmd/go/internal/work/gccgo.go index dd5adf2d7b..ade8964b7c 100644 --- a/src/cmd/go/internal/work/gccgo.go +++ b/src/cmd/go/internal/work/gccgo.go @@ -15,6 +15,7 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" + "cmd/go/internal/fsys" "cmd/go/internal/load" "cmd/go/internal/str" "cmd/internal/pkgpath" @@ -93,13 +94,37 @@ func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg args = append(args, "-I", root) } } - if cfg.BuildTrimpath && b.gccSupportsFlag(args[:1], "-ffile-prefix-map=a=b") { - args = append(args, "-ffile-prefix-map="+base.Cwd+"=.") - args = append(args, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build") + + if b.gccSupportsFlag(args[:1], "-ffile-prefix-map=a=b") { + if cfg.BuildTrimpath { + args = append(args, "-ffile-prefix-map="+base.Cwd+"=.") + args = append(args, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build") + } + if fsys.OverlayFile != "" { + for _, name := range gofiles { + absPath := mkAbs(p.Dir, name) + overlayPath, ok := fsys.OverlayPath(absPath) + if !ok { + continue + } + toPath := absPath + // gccgo only applies the last matching rule, so also handle the case where + // BuildTrimpath is true and the path is relative to base.Cwd. + if cfg.BuildTrimpath && str.HasFilePathPrefix(toPath, base.Cwd) { + toPath = "." + toPath[len(base.Cwd):] + } + args = append(args, "-ffile-prefix-map="+overlayPath+"="+toPath) + } + } } + args = append(args, a.Package.Internal.Gccgoflags...) for _, f := range gofiles { - args = append(args, mkAbs(p.Dir, f)) + f := mkAbs(p.Dir, f) + // Overlay files if necessary. + // See comment on gctoolchain.gc about overlay TODOs + f, _ = fsys.OverlayPath(f) + args = append(args, f) } output, err = b.runOut(a, p.Dir, nil, args) diff --git a/src/cmd/go/testdata/script/build_overlay.txt b/src/cmd/go/testdata/script/build_overlay.txt index 3b039901fa..5f598f37e7 100644 --- a/src/cmd/go/testdata/script/build_overlay.txt +++ b/src/cmd/go/testdata/script/build_overlay.txt @@ -9,36 +9,66 @@ # file in an overlay and one file outside the overlay, which in turn imports m/dir, # which only has source files in the overlay. +cd m + ! go build . go build -overlay overlay.json -o main$GOEXE . exec ./main$goexe stdout '^hello$' --- go.mod -- +go build -overlay overlay.json -o print_abspath$GOEXE ./printpath +exec ./print_abspath$GOEXE +stdout $WORK[/\\]gopath[/\\]src[/\\]m[/\\]printpath[/\\]main.go + +go build -overlay overlay.json -o print_trimpath$GOEXE -trimpath ./printpath +exec ./print_trimpath$GOEXE +stdout ^m[/\\]printpath[/\\]main.go + +# Run same tests but with gccgo. +env GO111MODULE=off +[!exec:gccgo] stop + +! go build -compiler=gccgo . +go build -compiler=gccgo -overlay overlay.json -o main_gccgo$GOEXE . +exec ./main_gccgo$goexe +stdout '^hello$' + +go build -compiler=gccgo -overlay overlay.json -o print_abspath_gccgo$GOEXE ./printpath +exec ./print_abspath_gccgo$GOEXE +stdout $WORK[/\\]gopath[/\\]src[/\\]m[/\\]printpath[/\\]main.go + +go build -compiler=gccgo -overlay overlay.json -o print_trimpath_gccgo$GOEXE -trimpath ./printpath +exec ./print_trimpath_gccgo$GOEXE +stdout ^\.[/\\]printpath[/\\]main.go + +-- m/go.mod -- // TODO(matloob): how do overlays work with go.mod (especially if mod=readonly) module m go 1.16 --- dir2/h.go -- +-- m/dir2/h.go -- package dir2 func PrintMessage() { printMessage() } --- dir/foo.txt -- +-- m/dir/foo.txt -- The build action code currently expects the package directory to exist, so it can run the compiler in that directory. TODO(matloob): Remove this requirement. --- overlay.json -- +-- m/printpath/about.txt -- +the actual code is in the overlay +-- m/overlay.json -- { "Replace": { "f.go": "overlay/f.go", "dir/g.go": "overlay/dir_g.go", - "dir2/i.go": "overlay/dir2_i.go" + "dir2/i.go": "overlay/dir2_i.go", + "printpath/main.go": "overlay/printpath.go" } } --- overlay/f.go -- +-- m/overlay/f.go -- package main import "m/dir2" @@ -46,7 +76,7 @@ import "m/dir2" func main() { dir2.PrintMessage() } --- overlay/dir_g.go -- +-- m/overlay/dir_g.go -- package dir import "fmt" @@ -54,7 +84,19 @@ import "fmt" func PrintMessage() { fmt.Println("hello") } --- overlay/dir2_i.go -- +-- m/overlay/printpath.go -- +package main + +import ( + "fmt" + "runtime" +) + +func main() { + _, file, _, _ := runtime.Caller(0) + fmt.Println(file) +} +-- m/overlay/dir2_i.go -- package dir2 import "m/dir" diff --git a/src/cmd/go/testdata/script/build_trimpath.txt b/src/cmd/go/testdata/script/build_trimpath.txt index ad78bcf2b2..2c3bee8fdc 100644 --- a/src/cmd/go/testdata/script/build_trimpath.txt +++ b/src/cmd/go/testdata/script/build_trimpath.txt @@ -9,6 +9,8 @@ env GO111MODULE=on mkdir $WORK/a/src/paths $WORK/b/src/paths cp paths.go $WORK/a/src/paths cp paths.go $WORK/b/src/paths +cp overlay.json $WORK/a/src/paths +cp overlay.json $WORK/b/src/paths cp go.mod $WORK/a/src/paths/ cp go.mod $WORK/b/src/paths/ @@ -43,6 +45,29 @@ go build -trimpath -o $WORK/paths-b.exe cmp -q $WORK/paths-a.exe $WORK/paths-b.exe +# Same sequence of tests but with overlays. +# A binary built without -trimpath should contain the module root dir +# and GOROOT for debugging and stack traces. +cd $WORK/a/src/paths +go build -overlay overlay.json -o $WORK/paths-dbg.exe ./overlaydir +exec $WORK/paths-dbg.exe $WORK/paths-dbg.exe +stdout 'binary contains module root: true' +stdout 'binary contains GOROOT: true' + +# A binary built with -trimpath should not contain the current workspace +# or GOROOT. +go build -overlay overlay.json -trimpath -o $WORK/paths-a.exe ./overlaydir +exec $WORK/paths-a.exe $WORK/paths-a.exe +stdout 'binary contains module root: false' +stdout 'binary contains GOROOT: false' + +# Two binaries built from identical packages in different directories +# should be identical. +cd $WORK/b/src/paths +go build -overlay overlay.json -trimpath -o $WORK/paths-b.exe ./overlaydir +cmp -q $WORK/paths-a.exe $WORK/paths-b.exe + + # Same sequence of tests but in GOPATH mode. # A binary built without -trimpath should contain GOPATH and GOROOT. env GO111MODULE=off @@ -129,7 +154,8 @@ func check(data []byte, desc, dir string) { containsSlashDir := bytes.Contains(data, []byte(filepath.ToSlash(dir))) fmt.Printf("binary contains %s: %v\n", desc, containsDir || containsSlashDir) } - +-- overlay.json -- +{ "Replace": { "overlaydir/paths.go": "paths.go" } } -- go.mod -- module paths -- 2.50.0