Skip to content

Commit da9cad4

Browse files
committed
go/packages: avoid unnecessary "realpath" on pwd
GOPACKAGESDRIVER commands absolutize filenames relative to their current working directory. However, os.Getwd may inadvertently expand out any symbolic links in the path, causing files to have the "wrong" path, and breaking various name-based equivalence tests that are common in the go/packages domain. This CL exploits the same trick used in gocommand to prevent os.Getwd from expanding symbolic links: if Stat(Getenv(PWD) returns the process's working directory, then the iterated ".." search (which inadvertently expands symlinks) is avoided. It is unfortunate that driver writers must think about this. Mostly it only shows up in tests, as that's where the subprocess directory varies from the parent directory. Also, add -driver flag to gopackages debug helper, which causes it to use drivertest instead of go list directly. Change-Id: Ibe12531fe565e74ca1d2565805b0f2458803f6b4 Reviewed-on: https://go-review.googlesource.com/c/tools/+/588767 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent 71b7fa9 commit da9cad4

File tree

5 files changed

+40
-9
lines changed

5 files changed

+40
-9
lines changed

go/internal/packagesdriver/sizes.go

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"golang.org/x/tools/internal/gocommand"
1414
)
1515

16+
// TODO(adonovan): move back into go/packages.
1617
func GetSizesForArgsGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (string, string, error) {
1718
inv.Verb = "list"
1819
inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"}

go/packages/external.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,19 @@ func findExternalDriver(cfg *Config) driver {
119119
stderr := new(bytes.Buffer)
120120
cmd := exec.CommandContext(cfg.Context, tool, words...)
121121
cmd.Dir = cfg.Dir
122-
cmd.Env = cfg.Env
122+
// The cwd gets resolved to the real path. On Darwin, where
123+
// /tmp is a symlink, this breaks anything that expects the
124+
// working directory to keep the original path, including the
125+
// go command when dealing with modules.
126+
//
127+
// os.Getwd stdlib has a special feature where if the
128+
// cwd and the PWD are the same node then it trusts
129+
// the PWD, so by setting it in the env for the child
130+
// process we fix up all the paths returned by the go
131+
// command.
132+
//
133+
// (See similar trick in Invocation.run in ../../internal/gocommand/invoke.go)
134+
cmd.Env = append(slicesClip(cfg.Env), "PWD="+cfg.Dir)
123135
cmd.Stdin = bytes.NewReader(req)
124136
cmd.Stdout = buf
125137
cmd.Stderr = stderr
@@ -138,3 +150,7 @@ func findExternalDriver(cfg *Config) driver {
138150
return &response, nil
139151
}
140152
}
153+
154+
// slicesClip removes unused capacity from the slice, returning s[:len(s):len(s)].
155+
// TODO(adonovan): use go1.21 slices.Clip.
156+
func slicesClip[S ~[]E, E any](s S) S { return s[:len(s):len(s)] }

go/packages/gopackages/main.go

+10
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,19 @@ import (
1414
"flag"
1515
"fmt"
1616
"go/types"
17+
"log"
1718
"os"
1819
"sort"
1920
"strings"
2021

2122
"golang.org/x/tools/go/packages"
2223
"golang.org/x/tools/go/types/typeutil"
24+
"golang.org/x/tools/internal/drivertest"
2325
"golang.org/x/tools/internal/tool"
2426
)
2527

2628
func main() {
29+
drivertest.RunIfChild()
2730
tool.Main(context.Background(), &application{Mode: "imports"}, os.Args[1:])
2831
}
2932

@@ -37,6 +40,7 @@ type application struct {
3740
Private bool `flag:"private" help:"show non-exported declarations too (if -mode=syntax)"`
3841
PrintJSON bool `flag:"json" help:"print package in JSON form"`
3942
BuildFlags stringListValue `flag:"buildflag" help:"pass argument to underlying build system (may be repeated)"`
43+
Driver bool `flag:"driver" help:"use golist passthrough driver (for debugging driver issues)"`
4044
}
4145

4246
// Name implements tool.Application returning the binary name.
@@ -82,11 +86,17 @@ func (app *application) Run(ctx context.Context, args ...string) error {
8286
return tool.CommandLineErrorf("not enough arguments")
8387
}
8488

89+
env := os.Environ()
90+
if app.Driver {
91+
env = append(env, drivertest.Env(log.Default())...)
92+
}
93+
8594
// Load, parse, and type-check the packages named on the command line.
8695
cfg := &packages.Config{
8796
Mode: packages.LoadSyntax,
8897
Tests: app.Test,
8998
BuildFlags: app.BuildFlags,
99+
Env: env,
90100
}
91101

92102
// -mode flag

internal/drivertest/driver.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import (
1717
"flag"
1818
"log"
1919
"os"
20-
"testing"
2120

2221
"golang.org/x/tools/go/packages"
2322
)
@@ -37,7 +36,9 @@ func RunIfChild() {
3736

3837
// Env returns additional environment variables for use in [packages.Config]
3938
// to enable the use of drivertest as the driver.
40-
func Env(t *testing.T) []string {
39+
//
40+
// t abstracts a *testing.T or log.Default().
41+
func Env(t interface{ Fatal(...any) }) []string {
4142
exe, err := os.Executable()
4243
if err != nil {
4344
t.Fatal(err)

internal/gocommand/invoke.go

+9-6
Original file line numberDiff line numberDiff line change
@@ -259,12 +259,15 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
259259
waitDelay.Set(reflect.ValueOf(30 * time.Second))
260260
}
261261

262-
// On darwin the cwd gets resolved to the real path, which breaks anything that
263-
// expects the working directory to keep the original path, including the
262+
// The cwd gets resolved to the real path. On Darwin, where
263+
// /tmp is a symlink, this breaks anything that expects the
264+
// working directory to keep the original path, including the
264265
// go command when dealing with modules.
265-
// The Go stdlib has a special feature where if the cwd and the PWD are the
266-
// same node then it trusts the PWD, so by setting it in the env for the child
267-
// process we fix up all the paths returned by the go command.
266+
//
267+
// os.Getwd has a special feature where if the cwd and the PWD
268+
// are the same node then it trusts the PWD, so by setting it
269+
// in the env for the child process we fix up all the paths
270+
// returned by the go command.
268271
if !i.CleanEnv {
269272
cmd.Env = os.Environ()
270273
}
@@ -474,7 +477,7 @@ func cmdDebugStr(cmd *exec.Cmd) string {
474477
}
475478

476479
// WriteOverlays writes each value in the overlay (see the Overlay
477-
// field of go/packages.Cfg) to a temporary file and returns the name
480+
// field of go/packages.Config) to a temporary file and returns the name
478481
// of a JSON file describing the mapping that is suitable for the "go
479482
// list -overlay" flag.
480483
//

0 commit comments

Comments
 (0)