Skip to content

Commit a56b114

Browse files
rscromaindoumenc
authored andcommitted
cmd/go: add -C flag
The -C flag is like tar -C or make -C: it changes to the named directory early in command startup, before anything else happens. Fixes golang#50332. Change-Id: I8e4546f69044cb3a028d4d26dfba482b08cb845d Reviewed-on: https://go-review.googlesource.com/c/go/+/421436 Reviewed-by: Bryan Mills <[email protected]> Auto-Submit: Russ Cox <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Russ Cox <[email protected]>
1 parent df205e8 commit a56b114

File tree

24 files changed

+136
-9
lines changed

24 files changed

+136
-9
lines changed

src/cmd/doc/main.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,13 @@ import (
5757
)
5858

5959
var (
60-
unexported bool // -u flag
61-
matchCase bool // -c flag
62-
showAll bool // -all flag
63-
showCmd bool // -cmd flag
64-
showSrc bool // -src flag
65-
short bool // -short flag
60+
unexported bool // -u flag
61+
matchCase bool // -c flag
62+
chdir string // -C flag
63+
showAll bool // -all flag
64+
showCmd bool // -cmd flag
65+
showSrc bool // -src flag
66+
short bool // -short flag
6667
)
6768

6869
// usage is a replacement usage function for the flags package.
@@ -96,13 +97,19 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
9697
flagSet.Usage = usage
9798
unexported = false
9899
matchCase = false
100+
flagSet.StringVar(&chdir, "C", "", "change to `dir` before running command")
99101
flagSet.BoolVar(&unexported, "u", false, "show unexported symbols as well as exported")
100102
flagSet.BoolVar(&matchCase, "c", false, "symbol matching honors case (paths not affected)")
101103
flagSet.BoolVar(&showAll, "all", false, "show all documentation for package")
102104
flagSet.BoolVar(&showCmd, "cmd", false, "show symbols with package docs even if package is a command")
103105
flagSet.BoolVar(&showSrc, "src", false, "show source code for symbol")
104106
flagSet.BoolVar(&short, "short", false, "one-line representation for each symbol")
105107
flagSet.Parse(args)
108+
if chdir != "" {
109+
if err := os.Chdir(chdir); err != nil {
110+
return err
111+
}
112+
}
106113
var paths []string
107114
var symbol, method string
108115
// Loop until something is printed.

src/cmd/go/alldocs.go

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/go/chdir_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"cmd/go/internal/base"
9+
"os"
10+
"strings"
11+
"testing"
12+
)
13+
14+
func TestChdir(t *testing.T) {
15+
// We want -C to apply to every go subcommand.
16+
// Test that every command either has a -C flag registered
17+
// or has CustomFlags set. In the latter case, the command
18+
// must be explicitly tested in TestScript/chdir.
19+
script, err := os.ReadFile("testdata/script/chdir.txt")
20+
if err != nil {
21+
t.Fatal(err)
22+
}
23+
24+
var walk func(string, *base.Command)
25+
walk = func(name string, cmd *base.Command) {
26+
if len(cmd.Commands) > 0 {
27+
for _, sub := range cmd.Commands {
28+
walk(name+" "+sub.Name(), sub)
29+
}
30+
return
31+
}
32+
if !cmd.Runnable() {
33+
return
34+
}
35+
if cmd.CustomFlags {
36+
if !strings.Contains(string(script), "# "+name+"\n") {
37+
t.Errorf("%s has custom flags, not tested in testdata/script/chdir.txt", name)
38+
}
39+
return
40+
}
41+
f := cmd.Flag.Lookup("C")
42+
if f == nil {
43+
t.Errorf("%s has no -C flag", name)
44+
} else if f.Usage != "AddChdirFlag" {
45+
t.Errorf("%s has -C flag but not from AddChdirFlag", name)
46+
}
47+
}
48+
walk("go", base.Go)
49+
}

src/cmd/go/internal/base/flag.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package base
66

77
import (
88
"flag"
9+
"os"
910

1011
"cmd/go/internal/cfg"
1112
"cmd/go/internal/fsys"
@@ -57,6 +58,13 @@ func AddBuildFlagsNX(flags *flag.FlagSet) {
5758
flags.BoolVar(&cfg.BuildX, "x", false, "")
5859
}
5960

61+
// AddChdirFlag adds the -C flag to the flag set.
62+
func AddChdirFlag(flags *flag.FlagSet) {
63+
// The usage message is never printed, but it's used in chdir_test.go
64+
// to identify that the -C flag is from AddChdirFlag.
65+
flags.Func("C", "AddChdirFlag", os.Chdir)
66+
}
67+
6068
// AddModFlag adds the -mod build flag to the flag set.
6169
func AddModFlag(flags *flag.FlagSet) {
6270
flags.Var(explicitStringFlag{value: &cfg.BuildMod, explicit: &cfg.BuildModExplicit}, "mod", "")

src/cmd/go/internal/bug/bug.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ The report includes useful system information.
3737

3838
func init() {
3939
CmdBug.Flag.BoolVar(&cfg.BuildV, "v", false, "")
40+
base.AddChdirFlag(&CmdBug.Flag)
4041
}
4142

4243
func runBug(ctx context.Context, cmd *base.Command, args []string) {

src/cmd/go/internal/envcmd/env.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ For more about environment variables, see 'go help environment'.
5757

5858
func init() {
5959
CmdEnv.Run = runEnv // break init cycle
60+
base.AddChdirFlag(&CmdEnv.Flag)
6061
}
6162

6263
var (

src/cmd/go/internal/fmtcmd/fmt.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121

2222
func init() {
2323
base.AddBuildFlagsNX(&CmdFmt.Flag)
24+
base.AddChdirFlag(&CmdFmt.Flag)
2425
base.AddModFlag(&CmdFmt.Flag)
2526
base.AddModCommonFlags(&CmdFmt.Flag)
2627
}

src/cmd/go/internal/modcmd/download.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ func init() {
8484

8585
// TODO(jayconrod): https://golang.org/issue/35849 Apply -x to other 'go mod' commands.
8686
cmdDownload.Flag.BoolVar(&cfg.BuildX, "x", false, "")
87+
base.AddChdirFlag(&cmdDownload.Flag)
8788
base.AddModCommonFlags(&cmdDownload.Flag)
8889
}
8990

src/cmd/go/internal/modcmd/edit.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ Note that this only describes the go.mod file itself, not other modules
127127
referred to indirectly. For the full set of modules available to a build,
128128
use 'go list -m -json all'.
129129
130+
Edit also provides the -C, -n, and -x build flags.
131+
130132
See https://golang.org/ref/mod#go-mod-edit for more about 'go mod edit'.
131133
`,
132134
}
@@ -157,8 +159,9 @@ func init() {
157159
cmdEdit.Flag.Var(flagFunc(flagRetract), "retract", "")
158160
cmdEdit.Flag.Var(flagFunc(flagDropRetract), "dropretract", "")
159161

160-
base.AddModCommonFlags(&cmdEdit.Flag)
161162
base.AddBuildFlagsNX(&cmdEdit.Flag)
163+
base.AddChdirFlag(&cmdEdit.Flag)
164+
base.AddModCommonFlags(&cmdEdit.Flag)
162165
}
163166

164167
func runEdit(ctx context.Context, cmd *base.Command, args []string) {

src/cmd/go/internal/modcmd/graph.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ var (
4141

4242
func init() {
4343
cmdGraph.Flag.Var(&graphGo, "go", "")
44+
base.AddChdirFlag(&cmdGraph.Flag)
4445
base.AddModCommonFlags(&cmdGraph.Flag)
4546
}
4647

src/cmd/go/internal/modcmd/init.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ See https://golang.org/ref/mod#go-mod-init for more about 'go mod init'.
3434
}
3535

3636
func init() {
37+
base.AddChdirFlag(&cmdInit.Flag)
3738
base.AddModCommonFlags(&cmdInit.Flag)
3839
}
3940

src/cmd/go/internal/modcmd/tidy.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ func init() {
6464
cmdTidy.Flag.BoolVar(&tidyE, "e", false, "")
6565
cmdTidy.Flag.Var(&tidyGo, "go", "")
6666
cmdTidy.Flag.Var(&tidyCompat, "compat", "")
67+
base.AddChdirFlag(&cmdTidy.Flag)
6768
base.AddModCommonFlags(&cmdTidy.Flag)
6869
}
6970

src/cmd/go/internal/modcmd/vendor.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ func init() {
6161
cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "")
6262
cmdVendor.Flag.BoolVar(&vendorE, "e", false, "")
6363
cmdVendor.Flag.StringVar(&vendorO, "o", "", "")
64+
base.AddChdirFlag(&cmdVendor.Flag)
6465
base.AddModCommonFlags(&cmdVendor.Flag)
6566
}
6667

src/cmd/go/internal/modcmd/verify.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ See https://golang.org/ref/mod#go-mod-verify for more about 'go mod verify'.
3838
}
3939

4040
func init() {
41+
base.AddChdirFlag(&cmdVerify.Flag)
4142
base.AddModCommonFlags(&cmdVerify.Flag)
4243
}
4344

src/cmd/go/internal/modcmd/why.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ var (
5858

5959
func init() {
6060
cmdWhy.Run = runWhy // break init cycle
61+
base.AddChdirFlag(&cmdWhy.Flag)
6162
base.AddModCommonFlags(&cmdWhy.Flag)
6263
}
6364

src/cmd/go/internal/tool/tool.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ func isGccgoTool(tool string) bool {
4848
}
4949

5050
func init() {
51+
base.AddChdirFlag(&CmdTool.Flag)
5152
CmdTool.Flag.BoolVar(&toolN, "n", false, "")
5253
}
5354

src/cmd/go/internal/version/version.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ See also: go doc runtime/debug.BuildInfo.
4444
}
4545

4646
func init() {
47+
base.AddChdirFlag(&CmdVersion.Flag)
4748
CmdVersion.Run = runVersion // break init cycle
4849
}
4950

src/cmd/go/internal/vet/vet.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func init() {
2525

2626
var CmdVet = &base.Command{
2727
CustomFlags: true,
28-
UsageLine: "go vet [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]",
28+
UsageLine: "go vet [-C dir] [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]",
2929
Short: "report likely mistakes in packages",
3030
Long: `
3131
Vet runs the Go vet command on the packages named by the import paths.
@@ -35,6 +35,7 @@ For more about specifying packages, see 'go help packages'.
3535
For a list of checkers and their flags, see 'go tool vet help'.
3636
For details of a specific checker such as 'printf', see 'go tool vet help printf'.
3737
38+
The -C flag changes to dir before running the 'go vet' command.
3839
The -n flag prints commands that would be executed.
3940
The -x flag prints commands as they are executed.
4041

src/cmd/go/internal/work/build.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ will be written to that directory.
5858
The build flags are shared by the build, clean, get, install, list, run,
5959
and test commands:
6060
61+
-C dir
62+
Change to dir before running the command.
63+
Any files named on the command line are interpreted after
64+
changing directories.
6165
-a
6266
force rebuilding of packages that are already up-to-date.
6367
-n
@@ -282,6 +286,7 @@ const (
282286
// install, list, run, and test commands.
283287
func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
284288
base.AddBuildFlagsNX(&cmd.Flag)
289+
base.AddChdirFlag(&cmd.Flag)
285290
cmd.Flag.BoolVar(&cfg.BuildA, "a", false, "")
286291
cmd.Flag.IntVar(&cfg.BuildP, "p", cfg.BuildP, "")
287292
if mask&OmitVFlag == 0 {

src/cmd/go/internal/workcmd/edit.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ func init() {
109109
cmdEdit.Flag.Var(flagFunc(flagEditworkDropUse), "dropuse", "")
110110
cmdEdit.Flag.Var(flagFunc(flagEditworkReplace), "replace", "")
111111
cmdEdit.Flag.Var(flagFunc(flagEditworkDropReplace), "dropreplace", "")
112+
base.AddChdirFlag(&cmdEdit.Flag)
112113
}
113114

114115
func runEditwork(ctx context.Context, cmd *base.Command, args []string) {

src/cmd/go/internal/workcmd/init.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ for more information.
3434
}
3535

3636
func init() {
37+
base.AddChdirFlag(&cmdInit.Flag)
3738
base.AddModCommonFlags(&cmdInit.Flag)
3839
}
3940

src/cmd/go/internal/workcmd/sync.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ for more information.
4141
}
4242

4343
func init() {
44+
base.AddChdirFlag(&cmdSync.Flag)
4445
base.AddModCommonFlags(&cmdSync.Flag)
4546
}
4647

src/cmd/go/internal/workcmd/use.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ var useR = cmdUse.Flag.Bool("r", false, "")
4343
func init() {
4444
cmdUse.Run = runUse // break init cycle
4545

46+
base.AddChdirFlag(&cmdUse.Flag)
4647
base.AddModCommonFlags(&cmdUse.Flag)
4748
}
4849

src/cmd/go/testdata/script/chdir.txt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
env OLD=$PWD
2+
3+
# basic -C functionality
4+
cd $GOROOT/src/math
5+
go list -C ../strings
6+
stdout strings
7+
! go list -C ../nonexist
8+
stderr 'chdir.*nonexist'
9+
10+
# check for -C in subcommands with custom flag parsing
11+
# cmd/go/chdir_test.go handles the normal ones more directly.
12+
13+
# go doc
14+
go doc -C ../strings HasPrefix
15+
16+
# go env
17+
go env -C $OLD/custom GOMOD
18+
stdout 'custom[\\/]go.mod'
19+
! go env -C ../nonexist
20+
stderr '^invalid value "../nonexist" for flag -C: chdir ../nonexist:.*$'
21+
22+
# go test
23+
go test -n -C ../strings
24+
stderr 'strings\.test'
25+
26+
# go vet
27+
go vet -n -C ../strings
28+
stderr strings_test
29+
30+
-- custom/go.mod --
31+
module m

0 commit comments

Comments
 (0)