diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 55371c12159c0..141f13c63e5b0 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -89,10 +89,10 @@ // // When compiling packages, build ignores files that end in '_test.go'. // -// The -o flag, only allowed when compiling a single package, -// forces build to write the resulting executable or object -// to the named output file, instead of the default behavior described -// in the last two paragraphs. +// The -o flag forces build to write the resulting executable or object +// to the named output file or directory, instead of the default behavior described +// in the last two paragraphs. If the named output is a directory that exists, +// then any resulting executables will be written to that directory. // // The -i flag installs the packages that are dependencies of the target. // diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go index d89ee899f095b..82ac7d692f0e8 100644 --- a/src/cmd/go/internal/work/build.go +++ b/src/cmd/go/internal/work/build.go @@ -42,10 +42,10 @@ serving only as a check that the packages can be built. When compiling packages, build ignores files that end in '_test.go'. -The -o flag, only allowed when compiling a single package, -forces build to write the resulting executable or object -to the named output file, instead of the default behavior described -in the last two paragraphs. +The -o flag forces build to write the resulting executable or object +to the named output file or directory, instead of the default behavior described +in the last two paragraphs. If the named output is a directory that exists, +then any resulting executables will be written to that directory. The -i flag installs the packages that are dependencies of the target. @@ -153,7 +153,7 @@ func init() { CmdInstall.Run = runInstall CmdBuild.Flag.BoolVar(&cfg.BuildI, "i", false, "") - CmdBuild.Flag.StringVar(&cfg.BuildO, "o", "", "output file") + CmdBuild.Flag.StringVar(&cfg.BuildO, "o", "", "output file or directory") CmdInstall.Flag.BoolVar(&cfg.BuildI, "i", false, "") @@ -316,8 +316,29 @@ func runBuild(cmd *base.Command, args []string) { } if cfg.BuildO != "" { + // If the -o name exists and is a directory, then + // write all main packages to that directory. + // Otherwise require only a single package be built. + if bs, err := os.Stat(cfg.BuildO); err == nil && bs.IsDir() { + a := &Action{Mode: "go build"} + for _, p := range pkgs { + if p.Name != "main" { + continue + } + p.Target = filepath.Join(cfg.BuildO, load.DefaultExecName(p)) + p.Target += cfg.ExeSuffix + p.Stale = true + p.StaleReason = "build -o flag in use" + a.Deps = append(a.Deps, b.AutoAction(ModeInstall, depMode, p)) + } + if len(a.Deps) == 0 { + base.Fatalf("go build: no main packages to build") + } + b.Do(a) + return + } if len(pkgs) > 1 { - base.Fatalf("go build: cannot use -o with multiple packages") + base.Fatalf("go build: cannot write multiple packages to non-directory %s", cfg.BuildO) } else if len(pkgs) == 0 { base.Fatalf("no packages to build") } diff --git a/src/cmd/go/testdata/script/build_multi_main.txt b/src/cmd/go/testdata/script/build_multi_main.txt new file mode 100644 index 0000000000000..734e8d88d2881 --- /dev/null +++ b/src/cmd/go/testdata/script/build_multi_main.txt @@ -0,0 +1,27 @@ +# Verify build -o can output multiple executables to a directory. + +mkdir $WORK/bin +go build -o $WORK/bin ./cmd/c1 ./cmd/c2 +! stderr 'multiple packages' + +! go build -o $WORK/bin ./pkg1 ./pkg1 +stderr 'no main packages' + +-- go.mod -- +module exmod + +-- cmd/c1/main.go -- +package main + +func main() {} + +-- cmd/c2/main.go -- +package main + +func main() {} + +-- pkg1/pkg1.go -- +package pkg1 + +-- pkg2/pkg2.go -- +package pkg2