Skip to content

cmd/go: cache results of exec.LookPath #61464

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/cmd/go/internal/cfg/bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cfg

import (
"internal/testenv"
"testing"
)

func BenchmarkLookPath(b *testing.B) {
testenv.MustHaveExecPath(b, "go")
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := LookPath("go")
if err != nil {
b.Fatal(err)
}
}
}
3 changes: 1 addition & 2 deletions src/cmd/go/internal/cfg/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"internal/cfg"
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
Expand Down Expand Up @@ -161,7 +160,7 @@ func defaultContext() build.Context {
if ctxt.CgoEnabled {
if os.Getenv("CC") == "" {
cc := DefaultCC(ctxt.GOOS, ctxt.GOARCH)
if _, err := exec.LookPath(cc); err != nil {
if _, err := LookPath(cc); err != nil {
ctxt.CgoEnabled = false
}
}
Expand Down
21 changes: 21 additions & 0 deletions src/cmd/go/internal/cfg/lookpath.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cfg

import (
"cmd/go/internal/par"
"os/exec"
)

var lookPathCache par.ErrCache[string, string]

// LookPath wraps exec.LookPath and caches the result
// which can be called by multiple Goroutines at the same time.
func LookPath(file string) (path string, err error) {
return lookPathCache.Do(file,
func() (string, error) {
return exec.LookPath(file)
})
}
2 changes: 1 addition & 1 deletion src/cmd/go/internal/generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ func (g *Generator) exec(words []string) {
// intends to use the same 'go' as 'go generate' itself.
// Prefer to resolve the binary from GOROOT/bin, and for consistency
// prefer to resolve any other commands there too.
gorootBinPath, err := exec.LookPath(filepath.Join(cfg.GOROOTbin, path))
gorootBinPath, err := cfg.LookPath(filepath.Join(cfg.GOROOTbin, path))
if err == nil {
path = gorootBinPath
}
Expand Down
3 changes: 1 addition & 2 deletions src/cmd/go/internal/load/pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"internal/platform"
"io/fs"
"os"
"os/exec"
pathpkg "path"
"path/filepath"
"runtime"
Expand Down Expand Up @@ -2483,7 +2482,7 @@ func (p *Package) setBuildInfo(ctx context.Context, autoVCS bool) {
goto omitVCS
}
if cfg.BuildBuildvcs == "auto" && vcsCmd != nil && vcsCmd.Cmd != "" {
if _, err := exec.LookPath(vcsCmd.Cmd); err != nil {
if _, err := cfg.LookPath(vcsCmd.Cmd); err != nil {
// We fould a repository, but the required VCS tool is not present.
// "-buildvcs=auto" means that we should silently drop the VCS metadata.
goto omitVCS
Expand Down
3 changes: 2 additions & 1 deletion src/cmd/go/internal/script/cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package script

import (
"cmd/go/internal/cfg"
"cmd/go/internal/robustio"
"errors"
"fmt"
Expand Down Expand Up @@ -824,7 +825,7 @@ func Program(name string, cancel func(*exec.Cmd) error, waitDelay time.Duration)
},
func(s *State, args ...string) (WaitFunc, error) {
lookPathOnce.Do(func() {
path, pathErr = exec.LookPath(name)
path, pathErr = cfg.LookPath(name)
})
if pathErr != nil {
return nil, pathErr
Expand Down
4 changes: 2 additions & 2 deletions src/cmd/go/internal/script/scripttest/scripttest.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ package scripttest

import (
"bufio"
"cmd/go/internal/cfg"
"cmd/go/internal/script"
"errors"
"io"
"os/exec"
"strings"
"testing"
)
Expand Down Expand Up @@ -137,7 +137,7 @@ func CachedExec() script.Cond {
return script.CachedCondition(
"<suffix> names an executable in the test binary's PATH",
func(name string) (bool, error) {
_, err := exec.LookPath(name)
_, err := cfg.LookPath(name)
return err == nil, nil
})
}
3 changes: 1 addition & 2 deletions src/cmd/go/internal/toolchain/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"io/fs"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
Expand Down Expand Up @@ -283,7 +282,7 @@ func Exec(gotoolchain string) {
// Look in PATH for the toolchain before we download one.
// This allows custom toolchains as well as reuse of toolchains
// already installed using go install golang.org/dl/go1.2.3@latest.
if exe, err := exec.LookPath(gotoolchain); err == nil {
if exe, err := cfg.LookPath(gotoolchain); err == nil {
execGoToolchain(gotoolchain, "", exe)
}

Expand Down
2 changes: 1 addition & 1 deletion src/cmd/go/internal/vcs/vcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ func (v *Cmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([
args = args[2:]
}

_, err := exec.LookPath(v.Cmd)
_, err := cfg.LookPath(v.Cmd)
if err != nil {
fmt.Fprintf(os.Stderr,
"go: missing %s command. See https://golang.org/s/gogetcmd\n",
Expand Down
3 changes: 1 addition & 2 deletions src/cmd/go/internal/work/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"fmt"
"go/build"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
Expand Down Expand Up @@ -899,7 +898,7 @@ func FindExecCmd() []string {
if cfg.Goos == runtime.GOOS && cfg.Goarch == runtime.GOARCH {
return ExecCmd
}
path, err := exec.LookPath(fmt.Sprintf("go_%s_%s_exec", cfg.Goos, cfg.Goarch))
path, err := cfg.LookPath(fmt.Sprintf("go_%s_%s_exec", cfg.Goos, cfg.Goarch))
if err == nil {
ExecCmd = []string{path}
}
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/go/internal/work/buildid.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ func (b *Builder) gccToolID(name, language string) (id, exe string, err error) {
}
exe = fields[0]
if !strings.ContainsAny(exe, `/\`) {
if lp, err := exec.LookPath(exe); err == nil {
if lp, err := cfg.LookPath(exe); err == nil {
exe = lp
}
}
Expand Down
10 changes: 7 additions & 3 deletions src/cmd/go/internal/work/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -2373,7 +2373,11 @@ func (b *Builder) runOut(a *Action, dir string, env []string, cmdargs ...any) ([
}

var buf bytes.Buffer
cmd := exec.Command(cmdline[0], cmdline[1:]...)
path, err := cfg.LookPath(cmdline[0])
if err != nil {
return nil, err
}
cmd := exec.Command(path, cmdline[1:]...)
if cmd.Path != "" {
cmd.Args[0] = cmd.Path
}
Expand All @@ -2397,7 +2401,7 @@ func (b *Builder) runOut(a *Action, dir string, env []string, cmdargs ...any) ([

cmd.Env = append(cmd.Env, env...)
start := time.Now()
err := cmd.Run()
err = cmd.Run()
if a != nil && a.json != nil {
aj := a.json
aj.Cmd = append(aj.Cmd, joinUnambiguously(cmdline))
Expand Down Expand Up @@ -3017,7 +3021,7 @@ func (b *Builder) gccCompilerID(compiler string) (id cache.ActionID, ok bool) {
//
// Otherwise, we compute a new validation description
// and compiler id (below).
exe, err := exec.LookPath(compiler)
exe, err := cfg.LookPath(compiler)
if err != nil {
return cache.ActionID{}, false
}
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/go/internal/work/gccgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func init() {
if GccgoName == "" {
GccgoName = "gccgo"
}
GccgoBin, gccgoErr = exec.LookPath(GccgoName)
GccgoBin, gccgoErr = cfg.LookPath(GccgoName)
}

func (gccgoToolchain) compiler() string {
Expand Down