cmd/go: add -pkgdir build flag

Fixes #10210.

Change-Id: I82ddd665bca31773b1fb1b056338c04818ef68f5
Reviewed-on: https://go-review.googlesource.com/12171
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index 33ea685..b85e924 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -96,11 +96,10 @@
 	-x
 		print the commands.
 
+	-asmflags 'flag list'
+		arguments to pass on each go tool asm invocation.
 	-buildmode mode
 		build mode to use. See 'go help buildmode' for more.
-	-linkshared
-		link against shared libraries previously created with
-		-buildmode=shared
 	-compiler name
 		name of compiler to use, as in runtime.Compiler (gccgo or gc).
 	-gccgoflags 'arg list'
@@ -115,8 +114,13 @@
 		option that requires non-default compile flags has a similar effect.
 	-ldflags 'flag list'
 		arguments to pass on each go tool link invocation.
-	-asmflags 'flag list'
-		arguments to pass on each go tool asm invocation.
+	-linkshared
+		link against shared libraries previously created with
+		-buildmode=shared
+	-pkgdir dir
+		install and load all packages from dir instead of the usual locations.
+		For example, when building with a non-standard configuration,
+		use -pkgdir to keep generated packages in a separate location.
 	-tags 'tag list'
 		a list of build tags to consider satisfied during the build.
 		For more information about build tags, see the description of
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index 93aa454..54944a6 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -76,11 +76,10 @@
 	-x
 		print the commands.
 
+	-asmflags 'flag list'
+		arguments to pass on each go tool asm invocation.
 	-buildmode mode
 		build mode to use. See 'go help buildmode' for more.
-	-linkshared
-		link against shared libraries previously created with
-		-buildmode=shared
 	-compiler name
 		name of compiler to use, as in runtime.Compiler (gccgo or gc).
 	-gccgoflags 'arg list'
@@ -95,8 +94,13 @@
 		option that requires non-default compile flags has a similar effect.
 	-ldflags 'flag list'
 		arguments to pass on each go tool link invocation.
-	-asmflags 'flag list'
-		arguments to pass on each go tool asm invocation.
+	-linkshared
+		link against shared libraries previously created with
+		-buildmode=shared
+	-pkgdir dir
+		install and load all packages from dir instead of the usual locations.
+		For example, when building with a non-standard configuration,
+		use -pkgdir to keep generated packages in a separate location.
 	-tags 'tag list'
 		a list of build tags to consider satisfied during the build.
 		For more information about build tags, see the description of
@@ -157,6 +161,7 @@
 var buildToolExec []string   // -toolexec flag
 var buildBuildmode string    // -buildmode flag
 var buildLinkshared bool     // -linkshared flag
+var buildPkgdir string       // -pkgdir flag
 
 var buildContext = build.Default
 var buildToolchain toolchain = noToolchain{}
@@ -196,21 +201,22 @@
 // addBuildFlags adds the flags common to the build, clean, get,
 // install, list, run, and test commands.
 func addBuildFlags(cmd *Command) {
-	// NOTE: If you add flags here, also add them to testflag.go.
 	cmd.Flag.BoolVar(&buildA, "a", false, "")
 	cmd.Flag.BoolVar(&buildN, "n", false, "")
 	cmd.Flag.IntVar(&buildP, "p", buildP, "")
 	cmd.Flag.BoolVar(&buildV, "v", false, "")
 	cmd.Flag.BoolVar(&buildX, "x", false, "")
+
 	cmd.Flag.Var((*stringsFlag)(&buildAsmflags), "asmflags", "")
+	cmd.Flag.Var(buildCompiler{}, "compiler", "")
+	cmd.Flag.StringVar(&buildBuildmode, "buildmode", "default", "")
 	cmd.Flag.Var((*stringsFlag)(&buildGcflags), "gcflags", "")
-	cmd.Flag.Var((*stringsFlag)(&buildLdflags), "ldflags", "")
 	cmd.Flag.Var((*stringsFlag)(&buildGccgoflags), "gccgoflags", "")
 	cmd.Flag.StringVar(&buildContext.InstallSuffix, "installsuffix", "", "")
-	cmd.Flag.Var(buildCompiler{}, "compiler", "")
-	cmd.Flag.BoolVar(&buildRace, "race", false, "")
-	cmd.Flag.StringVar(&buildBuildmode, "buildmode", "default", "")
+	cmd.Flag.Var((*stringsFlag)(&buildLdflags), "ldflags", "")
 	cmd.Flag.BoolVar(&buildLinkshared, "linkshared", false, "")
+	cmd.Flag.StringVar(&buildPkgdir, "pkgdir", "", "")
+	cmd.Flag.BoolVar(&buildRace, "race", false, "")
 	cmd.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "")
 	cmd.Flag.Var((*stringsFlag)(&buildToolExec), "toolexec", "")
 	cmd.Flag.BoolVar(&buildWork, "work", false, "")
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index 768cf02..61eecbe 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -2065,3 +2065,15 @@
 	tg.runFail("run", "sub/sub.go", "x.go")
 	tg.grepStderr("named files must all be in one directory; have sub/ and .", "wrong output")
 }
+
+func TestGoInstallPkgdir(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.makeTempdir()
+	pkg := tg.path(".")
+	tg.run("install", "-pkgdir", pkg, "errors")
+	_, err := os.Stat(filepath.Join(pkg, "errors.a"))
+	tg.must(err)
+	_, err = os.Stat(filepath.Join(pkg, "runtime.a"))
+	tg.must(err)
+}
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index 432a98b..03858d9 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -110,6 +110,13 @@
 func (p *Package) copyBuild(pp *build.Package) {
 	p.build = pp
 
+	if pp.PkgTargetRoot != "" && buildPkgdir != "" {
+		old := pp.PkgTargetRoot
+		pp.PkgRoot = buildPkgdir
+		pp.PkgTargetRoot = buildPkgdir
+		pp.PkgObj = filepath.Join(buildPkgdir, strings.TrimPrefix(pp.PkgObj, old))
+	}
+
 	p.Dir = pp.Dir
 	p.ImportPath = pp.ImportPath
 	p.ImportComment = pp.ImportComment