]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: add -C flag
authorRuss Cox <rsc@golang.org>
Fri, 5 Aug 2022 17:09:20 +0000 (13:09 -0400)
committerGopher Robot <gobot@golang.org>
Thu, 3 Nov 2022 12:16:35 +0000 (12:16 +0000)
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 #50332.

Change-Id: I8e4546f69044cb3a028d4d26dfba482b08cb845d
Reviewed-on: https://go-review.googlesource.com/c/go/+/421436
Reviewed-by: Bryan Mills <bcmills@google.com>
Auto-Submit: Russ Cox <rsc@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>

24 files changed:
src/cmd/doc/main.go
src/cmd/go/alldocs.go
src/cmd/go/chdir_test.go [new file with mode: 0644]
src/cmd/go/internal/base/flag.go
src/cmd/go/internal/bug/bug.go
src/cmd/go/internal/envcmd/env.go
src/cmd/go/internal/fmtcmd/fmt.go
src/cmd/go/internal/modcmd/download.go
src/cmd/go/internal/modcmd/edit.go
src/cmd/go/internal/modcmd/graph.go
src/cmd/go/internal/modcmd/init.go
src/cmd/go/internal/modcmd/tidy.go
src/cmd/go/internal/modcmd/vendor.go
src/cmd/go/internal/modcmd/verify.go
src/cmd/go/internal/modcmd/why.go
src/cmd/go/internal/tool/tool.go
src/cmd/go/internal/version/version.go
src/cmd/go/internal/vet/vet.go
src/cmd/go/internal/work/build.go
src/cmd/go/internal/workcmd/edit.go
src/cmd/go/internal/workcmd/init.go
src/cmd/go/internal/workcmd/sync.go
src/cmd/go/internal/workcmd/use.go
src/cmd/go/testdata/script/chdir.txt [new file with mode: 0644]

index 3c45dd76dfa38d2cd554708f1bf8124181529921..ae1b7575e8225220ac33021a5e7cd27b1772f417 100644 (file)
@@ -57,12 +57,13 @@ import (
 )
 
 var (
-       unexported bool // -u flag
-       matchCase  bool // -c flag
-       showAll    bool // -all flag
-       showCmd    bool // -cmd flag
-       showSrc    bool // -src flag
-       short      bool // -short flag
+       unexported bool   // -u flag
+       matchCase  bool   // -c flag
+       chdir      string // -C flag
+       showAll    bool   // -all flag
+       showCmd    bool   // -cmd flag
+       showSrc    bool   // -src flag
+       short      bool   // -short flag
 )
 
 // usage is a replacement usage function for the flags package.
@@ -96,6 +97,7 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
        flagSet.Usage = usage
        unexported = false
        matchCase = false
+       flagSet.StringVar(&chdir, "C", "", "change to `dir` before running command")
        flagSet.BoolVar(&unexported, "u", false, "show unexported symbols as well as exported")
        flagSet.BoolVar(&matchCase, "c", false, "symbol matching honors case (paths not affected)")
        flagSet.BoolVar(&showAll, "all", false, "show all documentation for package")
@@ -103,6 +105,11 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
        flagSet.BoolVar(&showSrc, "src", false, "show source code for symbol")
        flagSet.BoolVar(&short, "short", false, "one-line representation for each symbol")
        flagSet.Parse(args)
+       if chdir != "" {
+               if err := os.Chdir(chdir); err != nil {
+                       return err
+               }
+       }
        var paths []string
        var symbol, method string
        // Loop until something is printed.
index a8206c475ccc6fc20ba89a32274047c3055ee470..051cf25996aacdfb47d2cc997e56c91d89d9ef20 100644 (file)
 // The build flags are shared by the build, clean, get, install, list, run,
 // and test commands:
 //
+//     -C dir
+//             Change to dir before running the command.
+//             Any files named on the command line are interpreted after
+//             changing directories.
 //     -a
 //             force rebuilding of packages that are already up-to-date.
 //     -n
 // referred to indirectly. For the full set of modules available to a build,
 // use 'go list -m -json all'.
 //
+// Edit also provides the -C, -n, and -x build flags.
+//
 // See https://golang.org/ref/mod#go-mod-edit for more about 'go mod edit'.
 //
 // # Print module requirement graph
 //
 // Usage:
 //
-//     go vet [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]
+//     go vet [-C dir] [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]
 //
 // Vet runs the Go vet command on the packages named by the import paths.
 //
 // For a list of checkers and their flags, see 'go tool vet help'.
 // For details of a specific checker such as 'printf', see 'go tool vet help printf'.
 //
+// The -C flag changes to dir before running the 'go vet' command.
 // The -n flag prints commands that would be executed.
 // The -x flag prints commands as they are executed.
 //
diff --git a/src/cmd/go/chdir_test.go b/src/cmd/go/chdir_test.go
new file mode 100644 (file)
index 0000000..44cbb9c
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright 2022 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 main
+
+import (
+       "cmd/go/internal/base"
+       "os"
+       "strings"
+       "testing"
+)
+
+func TestChdir(t *testing.T) {
+       // We want -C to apply to every go subcommand.
+       // Test that every command either has a -C flag registered
+       // or has CustomFlags set. In the latter case, the command
+       // must be explicitly tested in TestScript/chdir.
+       script, err := os.ReadFile("testdata/script/chdir.txt")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       var walk func(string, *base.Command)
+       walk = func(name string, cmd *base.Command) {
+               if len(cmd.Commands) > 0 {
+                       for _, sub := range cmd.Commands {
+                               walk(name+" "+sub.Name(), sub)
+                       }
+                       return
+               }
+               if !cmd.Runnable() {
+                       return
+               }
+               if cmd.CustomFlags {
+                       if !strings.Contains(string(script), "# "+name+"\n") {
+                               t.Errorf("%s has custom flags, not tested in testdata/script/chdir.txt", name)
+                       }
+                       return
+               }
+               f := cmd.Flag.Lookup("C")
+               if f == nil {
+                       t.Errorf("%s has no -C flag", name)
+               } else if f.Usage != "AddChdirFlag" {
+                       t.Errorf("%s has -C flag but not from AddChdirFlag", name)
+               }
+       }
+       walk("go", base.Go)
+}
index 120420a1265598f9a6e56084abe314cbc512eebc..9d8d1c0c8db53b9a3eac82197e86e99cbec9f993 100644 (file)
@@ -6,6 +6,7 @@ package base
 
 import (
        "flag"
+       "os"
 
        "cmd/go/internal/cfg"
        "cmd/go/internal/fsys"
@@ -57,6 +58,13 @@ func AddBuildFlagsNX(flags *flag.FlagSet) {
        flags.BoolVar(&cfg.BuildX, "x", false, "")
 }
 
+// AddChdirFlag adds the -C flag to the flag set.
+func AddChdirFlag(flags *flag.FlagSet) {
+       // The usage message is never printed, but it's used in chdir_test.go
+       // to identify that the -C flag is from AddChdirFlag.
+       flags.Func("C", "AddChdirFlag", os.Chdir)
+}
+
 // AddModFlag adds the -mod build flag to the flag set.
 func AddModFlag(flags *flag.FlagSet) {
        flags.Var(explicitStringFlag{value: &cfg.BuildMod, explicit: &cfg.BuildModExplicit}, "mod", "")
index e667012fbbb68d87e393bb9e22d31030f6b93811..ed1813605e5de4f2c55f9789cadb18862be94ec3 100644 (file)
@@ -37,6 +37,7 @@ The report includes useful system information.
 
 func init() {
        CmdBug.Flag.BoolVar(&cfg.BuildV, "v", false, "")
+       base.AddChdirFlag(&CmdBug.Flag)
 }
 
 func runBug(ctx context.Context, cmd *base.Command, args []string) {
index f7f065529d6f884cec65b7f7e4eac8548a67772a..10499c2d3efe590c2d4aa184298242d261d7bf9d 100644 (file)
@@ -57,6 +57,7 @@ For more about environment variables, see 'go help environment'.
 
 func init() {
        CmdEnv.Run = runEnv // break init cycle
+       base.AddChdirFlag(&CmdEnv.Flag)
 }
 
 var (
index f6a8d207cdd160bd9279b4d2f7ebca19f97596eb..62b22f6bcfa4079e418ab816b5448d156af91b09 100644 (file)
@@ -21,6 +21,7 @@ import (
 
 func init() {
        base.AddBuildFlagsNX(&CmdFmt.Flag)
+       base.AddChdirFlag(&CmdFmt.Flag)
        base.AddModFlag(&CmdFmt.Flag)
        base.AddModCommonFlags(&CmdFmt.Flag)
 }
index 0b50afb668adccb824a3dbb612134155c3abecc0..f0b62e8b4bb825a5c21a29dc94d2c1ab76d6691f 100644 (file)
@@ -84,6 +84,7 @@ func init() {
 
        // TODO(jayconrod): https://golang.org/issue/35849 Apply -x to other 'go mod' commands.
        cmdDownload.Flag.BoolVar(&cfg.BuildX, "x", false, "")
+       base.AddChdirFlag(&cmdDownload.Flag)
        base.AddModCommonFlags(&cmdDownload.Flag)
 }
 
index edc1b19877c611864c746b4a66278401ec2a935b..5fd13f2627742fe664401401adf59a2118352b4c 100644 (file)
@@ -127,6 +127,8 @@ Note that this only describes the go.mod file itself, not other modules
 referred to indirectly. For the full set of modules available to a build,
 use 'go list -m -json all'.
 
+Edit also provides the -C, -n, and -x build flags.
+
 See https://golang.org/ref/mod#go-mod-edit for more about 'go mod edit'.
        `,
 }
@@ -157,8 +159,9 @@ func init() {
        cmdEdit.Flag.Var(flagFunc(flagRetract), "retract", "")
        cmdEdit.Flag.Var(flagFunc(flagDropRetract), "dropretract", "")
 
-       base.AddModCommonFlags(&cmdEdit.Flag)
        base.AddBuildFlagsNX(&cmdEdit.Flag)
+       base.AddChdirFlag(&cmdEdit.Flag)
+       base.AddModCommonFlags(&cmdEdit.Flag)
 }
 
 func runEdit(ctx context.Context, cmd *base.Command, args []string) {
index 9568c6574044399c6813383625bc932f8eaebfdd..feed6a0005714fa6d4aefed8584c17421fde1522 100644 (file)
@@ -41,6 +41,7 @@ var (
 
 func init() {
        cmdGraph.Flag.Var(&graphGo, "go", "")
+       base.AddChdirFlag(&cmdGraph.Flag)
        base.AddModCommonFlags(&cmdGraph.Flag)
 }
 
index bc4620a2a8d3f2121145ee64038ba0329fda599d..e4be73fab0bc96733cf1d2b1038ea4aa3ed1889d 100644 (file)
@@ -34,6 +34,7 @@ See https://golang.org/ref/mod#go-mod-init for more about 'go mod init'.
 }
 
 func init() {
+       base.AddChdirFlag(&cmdInit.Flag)
        base.AddModCommonFlags(&cmdInit.Flag)
 }
 
index d35476eb5393d3e92c6e61f384c99f5bf04463a7..27889941c72ef939f0db6e38cbd08700ce2036ab 100644 (file)
@@ -64,6 +64,7 @@ func init() {
        cmdTidy.Flag.BoolVar(&tidyE, "e", false, "")
        cmdTidy.Flag.Var(&tidyGo, "go", "")
        cmdTidy.Flag.Var(&tidyCompat, "compat", "")
+       base.AddChdirFlag(&cmdTidy.Flag)
        base.AddModCommonFlags(&cmdTidy.Flag)
 }
 
index a93c52dbb331173fbc696961f977085051094d5b..4f820eb13eca963ce141fffd5670026093436532 100644 (file)
@@ -61,6 +61,7 @@ func init() {
        cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "")
        cmdVendor.Flag.BoolVar(&vendorE, "e", false, "")
        cmdVendor.Flag.StringVar(&vendorO, "o", "", "")
+       base.AddChdirFlag(&cmdVendor.Flag)
        base.AddModCommonFlags(&cmdVendor.Flag)
 }
 
index 459bf5d070060fa372bf2291699943b06e122ebe..a5f7f2456358c526a15842d80eca1c7500a4b5b1 100644 (file)
@@ -38,6 +38,7 @@ See https://golang.org/ref/mod#go-mod-verify for more about 'go mod verify'.
 }
 
 func init() {
+       base.AddChdirFlag(&cmdVerify.Flag)
        base.AddModCommonFlags(&cmdVerify.Flag)
 }
 
index 8e929a00016bfc5b904e4492f001f68021de82d4..729c88f3f12f1b94db357a5421749752e9ef5993 100644 (file)
@@ -58,6 +58,7 @@ var (
 
 func init() {
        cmdWhy.Run = runWhy // break init cycle
+       base.AddChdirFlag(&cmdWhy.Flag)
        base.AddModCommonFlags(&cmdWhy.Flag)
 }
 
index afa3ac404fcff275c0cbb96bf3628f3f1f738794..069968b1b6efa416e50e82fa99489004ffd443d6 100644 (file)
@@ -48,6 +48,7 @@ func isGccgoTool(tool string) bool {
 }
 
 func init() {
+       base.AddChdirFlag(&CmdTool.Flag)
        CmdTool.Flag.BoolVar(&toolN, "n", false, "")
 }
 
index 6bbd48c6e673821fe02e9c2a42e506dddc6f8193..a0f612314977879d27951a275d633d5aad032796 100644 (file)
@@ -44,6 +44,7 @@ See also: go doc runtime/debug.BuildInfo.
 }
 
 func init() {
+       base.AddChdirFlag(&CmdVersion.Flag)
        CmdVersion.Run = runVersion // break init cycle
 }
 
index e5f8af1c37d82e2e56c33709541c3711b72b1d36..c73fa5b424fd0f5f9bdff08bd39d98315719bdd6 100644 (file)
@@ -25,7 +25,7 @@ func init() {
 
 var CmdVet = &base.Command{
        CustomFlags: true,
-       UsageLine:   "go vet [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]",
+       UsageLine:   "go vet [-C dir] [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]",
        Short:       "report likely mistakes in packages",
        Long: `
 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'.
 For a list of checkers and their flags, see 'go tool vet help'.
 For details of a specific checker such as 'printf', see 'go tool vet help printf'.
 
+The -C flag changes to dir before running the 'go vet' command.
 The -n flag prints commands that would be executed.
 The -x flag prints commands as they are executed.
 
index d8b78480718fa80ce3135cd564cd615424c6b7dd..553cd66ef3198ebb77d861f8fc6e2326fd6fd59d 100644 (file)
@@ -58,6 +58,10 @@ will be written to that directory.
 The build flags are shared by the build, clean, get, install, list, run,
 and test commands:
 
+       -C dir
+               Change to dir before running the command.
+               Any files named on the command line are interpreted after
+               changing directories.
        -a
                force rebuilding of packages that are already up-to-date.
        -n
@@ -282,6 +286,7 @@ const (
 // install, list, run, and test commands.
 func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
        base.AddBuildFlagsNX(&cmd.Flag)
+       base.AddChdirFlag(&cmd.Flag)
        cmd.Flag.BoolVar(&cfg.BuildA, "a", false, "")
        cmd.Flag.IntVar(&cfg.BuildP, "p", cfg.BuildP, "")
        if mask&OmitVFlag == 0 {
index 8d1d38318b1e7ebcdc68b90c4005876232a23b3e..f5e3304025b0d1908ea18f4ce4d0d9c07cee5811 100644 (file)
@@ -109,6 +109,7 @@ func init() {
        cmdEdit.Flag.Var(flagFunc(flagEditworkDropUse), "dropuse", "")
        cmdEdit.Flag.Var(flagFunc(flagEditworkReplace), "replace", "")
        cmdEdit.Flag.Var(flagFunc(flagEditworkDropReplace), "dropreplace", "")
+       base.AddChdirFlag(&cmdEdit.Flag)
 }
 
 func runEditwork(ctx context.Context, cmd *base.Command, args []string) {
index c2513bac358479791d40b5f9753b793293b36dea..6fb033ee296018f9d14991469d35e2d894c4eb60 100644 (file)
@@ -34,6 +34,7 @@ for more information.
 }
 
 func init() {
+       base.AddChdirFlag(&cmdInit.Flag)
        base.AddModCommonFlags(&cmdInit.Flag)
 }
 
index 7712eb6b6b8920a58b2a602355c2a9ec6abf386e..9f9962709b588964946f189f4a49cd286563285b 100644 (file)
@@ -41,6 +41,7 @@ for more information.
 }
 
 func init() {
+       base.AddChdirFlag(&cmdSync.Flag)
        base.AddModCommonFlags(&cmdSync.Flag)
 }
 
index fcb4e9e5f186d7a96d3f09b972cdbf3ab0be998f..be90989dddcf744ce21e0f22e5f9c6c53109ec6e 100644 (file)
@@ -43,6 +43,7 @@ var useR = cmdUse.Flag.Bool("r", false, "")
 func init() {
        cmdUse.Run = runUse // break init cycle
 
+       base.AddChdirFlag(&cmdUse.Flag)
        base.AddModCommonFlags(&cmdUse.Flag)
 }
 
diff --git a/src/cmd/go/testdata/script/chdir.txt b/src/cmd/go/testdata/script/chdir.txt
new file mode 100644 (file)
index 0000000..8952d18
--- /dev/null
@@ -0,0 +1,31 @@
+env OLD=$PWD
+
+# basic -C functionality
+cd $GOROOT/src/math
+go list -C ../strings
+stdout strings
+! go list -C ../nonexist
+stderr 'chdir.*nonexist'
+
+# check for -C in subcommands with custom flag parsing
+# cmd/go/chdir_test.go handles the normal ones more directly.
+
+# go doc
+go doc -C ../strings HasPrefix
+
+# go env
+go env -C $OLD/custom GOMOD
+stdout 'custom[\\/]go.mod'
+! go env -C ../nonexist
+stderr '^invalid value "../nonexist" for flag -C: chdir ../nonexist:.*$'
+
+# go test
+go test -n -C ../strings
+stderr 'strings\.test'
+
+# go vet
+go vet -n -C ../strings
+stderr strings_test
+
+-- custom/go.mod --
+module m