From d9e6835b1d0ac90d82c62e33a2aa0daf602940b0 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 13 Jan 2017 11:49:16 -0500 Subject: [PATCH] cmd/go: break a few dependencies This CL makes a few naming changes to break dependencies between different parts of the go command, to make it easier to split into different packages. This is the first CL in a long sequence of changes to break up the go command from one package into a plausible group of packages. This sequence is concerned only with moving code, not changing or cleaning up code. There will still be more cleanup after this sequence. The entire sequence will be submitted together: it is not a goal for the tree to build at every step. For #18653. Change-Id: I69a98b9ea48e61b1e1cda95273d29860b525415f Reviewed-on: https://go-review.googlesource.com/36129 Reviewed-by: David Crawshaw --- src/cmd/go/build.go | 77 ++++++++++++++++++++++++--------------------- src/cmd/go/main.go | 66 +++++++++++++++++++++----------------- src/cmd/go/pkg.go | 24 +++++++------- src/cmd/go/test.go | 28 ++++++++--------- 4 files changed, 105 insertions(+), 90 deletions(-) diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 98a650918a..d00090cf19 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -143,6 +143,7 @@ func init() { cmdInstall.Run = runInstall cmdBuild.Flag.BoolVar(&buildI, "i", false, "") + cmdBuild.Flag.StringVar(&buildO, "o", "", "output file") addBuildFlags(cmdBuild) addBuildFlags(cmdInstall) @@ -155,21 +156,24 @@ var buildP = runtime.NumCPU() // -p flag var buildV bool // -v flag var buildX bool // -x flag var buildI bool // -i flag -var buildO = cmdBuild.Flag.String("o", "", "output file") -var buildWork bool // -work flag -var buildAsmflags []string // -asmflags flag -var buildGcflags []string // -gcflags flag -var buildLdflags []string // -ldflags flag -var buildGccgoflags []string // -gccgoflags flag -var buildRace bool // -race flag -var buildMSan bool // -msan flag -var buildToolExec []string // -toolexec flag -var buildBuildmode string // -buildmode flag -var buildLinkshared bool // -linkshared flag -var buildPkgdir string // -pkgdir flag +var buildO string // -o flag +var buildWork bool // -work flag +var buildAsmflags []string // -asmflags flag +var buildGcflags []string // -gcflags flag +var buildLdflags []string // -ldflags flag +var buildGccgoflags []string // -gccgoflags flag +var buildRace bool // -race flag +var buildMSan bool // -msan flag +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{} +var buildToolchainName string +var buildToolchainCompiler string +var buildToolchainLinker string var ldBuildmode string // buildCompiler implements flag.Var. @@ -186,6 +190,9 @@ func (c buildCompiler) Set(value string) error { default: return fmt.Errorf("unknown compiler %q", value) } + buildToolchainName = value + buildToolchainCompiler = buildToolchain.compiler() + buildToolchainLinker = buildToolchain.linker() buildContext.Compiler = value return nil } @@ -196,10 +203,8 @@ func (c buildCompiler) String() string { func init() { switch build.Default.Compiler { - case "gc": - buildToolchain = gcToolchain{} - case "gccgo": - buildToolchain = gccgoToolchain{} + case "gc", "gccgo": + buildCompiler{}.Set(build.Default.Compiler) } } @@ -321,7 +326,7 @@ func pkgsNotMain(pkgs []*Package) (res []*Package) { var pkgsFilter = func(pkgs []*Package) []*Package { return pkgs } func buildModeInit() { - _, gccgo := buildToolchain.(gccgoToolchain) + gccgo := buildToolchainName == "gccgo" var codegenArg string platform := goos + "/" + goarch switch buildBuildmode { @@ -402,7 +407,7 @@ func buildModeInit() { } codegenArg = "-dynlink" } - if *buildO != "" { + if buildO != "" { fatalf("-buildmode=shared and -o not supported together") } ldBuildmode = "shared" @@ -464,14 +469,14 @@ func runBuild(cmd *Command, args []string) { pkgs := packagesForBuild(args) - if len(pkgs) == 1 && pkgs[0].Name == "main" && *buildO == "" { - _, *buildO = path.Split(pkgs[0].ImportPath) - *buildO += exeSuffix + if len(pkgs) == 1 && pkgs[0].Name == "main" && buildO == "" { + _, buildO = path.Split(pkgs[0].ImportPath) + buildO += exeSuffix } // Special case -o /dev/null by not writing at all. - if *buildO == os.DevNull { - *buildO = "" + if buildO == os.DevNull { + buildO = "" } // sanity check some often mis-used options @@ -494,14 +499,14 @@ func runBuild(cmd *Command, args []string) { depMode = modeInstall } - if *buildO != "" { + if buildO != "" { if len(pkgs) > 1 { fatalf("go build: cannot use -o with multiple packages") } else if len(pkgs) == 0 { fatalf("no packages to build") } p := pkgs[0] - p.target = *buildO + p.target = buildO p.Stale = true // must build - not up to date p.StaleReason = "build -o flag in use" a := b.action(modeInstall, depMode, p) @@ -874,8 +879,8 @@ func goFilesPackage(gofiles []string) *Package { if pkg.Name == "main" { _, elem := filepath.Split(gofiles[0]) exe := elem[:len(elem)-len(".go")] + exeSuffix - if *buildO == "" { - *buildO = exe + if buildO == "" { + buildO = exe } if gobin != "" { pkg.target = filepath.Join(gobin, exe) @@ -896,7 +901,7 @@ func goFilesPackage(gofiles []string) *Package { // .go_export section. func readpkglist(shlibpath string) (pkgs []*Package) { var stk importStack - if _, gccgo := buildToolchain.(gccgoToolchain); gccgo { + if buildToolchainName == "gccgo" { f, _ := elf.Open(shlibpath) sect := f.Section(".go_export") data, _ := sect.Data() @@ -1010,7 +1015,7 @@ func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, looksha return a } // gccgo standard library is "fake" too. - if _, ok := buildToolchain.(gccgoToolchain); ok { + if buildToolchainName == "gccgo" { // the target name is needed for cgo. a.target = p.target return a @@ -1114,7 +1119,7 @@ func (b *builder) libaction(libname string, pkgs []*Package, mode, depMode build // external linking mode forces an import of runtime/cgo (and // math on arm). So if it was not passed on the command line and // it is not present in another shared library, add it here. - _, gccgo := buildToolchain.(gccgoToolchain) + gccgo := buildToolchainName == "gccgo" if !gccgo { seencgo := false for _, p := range pkgs { @@ -1490,7 +1495,7 @@ func (b *builder) build(a *action) (err error) { if err != nil { return err } - if _, ok := buildToolchain.(gccgoToolchain); ok { + if buildToolchainName == "gccgo" { cgoObjects = append(cgoObjects, filepath.Join(a.objdir, "_cgo_flags")) } cgoObjects = append(cgoObjects, outObj...) @@ -3324,7 +3329,7 @@ func (b *builder) cgo(a *action, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofil cgoenv = []string{"CGO_LDFLAGS=" + strings.Join(flags, " ")} } - if _, ok := buildToolchain.(gccgoToolchain); ok { + if buildToolchainName == "gccgo" { switch goarch { case "386", "amd64": cgoCFLAGS = append(cgoCFLAGS, "-fsplit-stack") @@ -3396,8 +3401,8 @@ func (b *builder) cgo(a *action, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofil outObj = append(outObj, ofile) } - switch buildToolchain.(type) { - case gcToolchain: + switch buildToolchainName { + case "gc": importGo := obj + "_cgo_import.go" if err := b.dynimport(p, obj, importGo, cgoExe, cflags, cgoLDFLAGS, outObj); err != nil { return nil, nil, err @@ -3410,7 +3415,7 @@ func (b *builder) cgo(a *action, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofil } outObj = []string{ofile} - case gccgoToolchain: + case "gccgo": defunC := obj + "_cgo_defun.c" defunObj := obj + "_cgo_defun.o" if err := buildToolchain.cc(b, p, obj, defunObj, defunC); err != nil { @@ -3684,7 +3689,7 @@ func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx b gccExt = "cxx" } - _, gccgo := buildToolchain.(gccgoToolchain) + gccgo := buildToolchainName == "gccgo" // swig args := []string{ diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index 07fc4e2a90..dfd0e9aa1f 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -74,33 +74,37 @@ func (c *Command) Runnable() bool { // Commands lists the available commands and help topics. // The order here is the order in which they are printed by 'go help'. -var commands = []*Command{ - cmdBuild, - cmdClean, - cmdDoc, - cmdEnv, - cmdBug, - cmdFix, - cmdFmt, - cmdGenerate, - cmdGet, - cmdInstall, - cmdList, - cmdRun, - cmdTest, - cmdTool, - cmdVersion, - cmdVet, - - helpC, - helpBuildmode, - helpFileType, - helpGopath, - helpEnvironment, - helpImportPath, - helpPackages, - helpTestflag, - helpTestfunc, +var commands []*Command + +func init() { + commands = []*Command{ + cmdBuild, + cmdClean, + cmdDoc, + cmdEnv, + cmdBug, + cmdFix, + cmdFmt, + cmdGenerate, + cmdGet, + cmdInstall, + cmdList, + cmdRun, + cmdTest, + cmdTool, + cmdVersion, + cmdVet, + + helpC, + helpBuildmode, + helpFileType, + helpGopath, + helpEnvironment, + helpImportPath, + helpPackages, + helpTestflag, + helpTestfunc, + } } var exitStatus = 0 @@ -307,7 +311,13 @@ func printUsage(w io.Writer) { bw.Flush() } -func usage() { +var usage func() + +func init() { + usage = mainUsage +} + +func mainUsage() { // special case "go test -h" if len(os.Args) > 1 && os.Args[1] == "test" { os.Stderr.WriteString(testUsage + "\n\n" + diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index e40f9420c7..8fb6bddde1 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -1124,7 +1124,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package if p.BinaryOnly { // For binary-only package, use build ID from supplied package binary. - buildID, err := readBuildID(p) + buildID, err := readBuildID(p.Name, p.Target) if err == nil { p.buildID = buildID } @@ -1495,7 +1495,7 @@ func isStale(p *Package) (bool, string) { // It also catches changes in toolchain, like when flipping between // two versions of Go compiling a single GOPATH. // See issue 8290 and issue 10702. - targetBuildID, err := readBuildID(p) + targetBuildID, err := readBuildID(p.Name, p.Target) if err == nil && targetBuildID != p.buildID { return true, "build ID mismatch" } @@ -1559,10 +1559,10 @@ func isStale(p *Package) (bool, string) { // Excluding $GOROOT used to also fix issue 4106, but that's now // taken care of above (at least when the installed Go is a released version). if p.Root != goroot { - if olderThan(buildToolchain.compiler()) { + if olderThan(buildToolchainCompiler) { return true, "newer compiler" } - if p.build.IsCommand() && olderThan(buildToolchain.linker()) { + if p.build.IsCommand() && olderThan(buildToolchainLinker) { return true, "newer linker" } } @@ -1865,20 +1865,20 @@ var ( // readBuildID reads the build ID from an archive or binary. // It only supports the gc toolchain. // Other toolchain maintainers should adjust this function. -func readBuildID(p *Package) (id string, err error) { - if buildToolchain != (gcToolchain{}) { +func readBuildID(name, target string) (id string, err error) { + if buildToolchainName != "gc" { return "", errBuildIDToolchain } // For commands, read build ID directly from binary. - if p.Name == "main" { - return ReadBuildIDFromBinary(p.Target) + if name == "main" { + return ReadBuildIDFromBinary(target) } // Otherwise, we expect to have an archive (.a) file, // and we can read the build ID from the Go export data. - if !strings.HasSuffix(p.Target, ".a") { - return "", &os.PathError{Op: "parse", Path: p.Target, Err: errBuildIDUnknown} + if !strings.HasSuffix(target, ".a") { + return "", &os.PathError{Op: "parse", Path: target, Err: errBuildIDUnknown} } // Read just enough of the target to fetch the build ID. @@ -1891,7 +1891,7 @@ func readBuildID(p *Package) (id string, err error) { // // The variable-sized strings are GOOS, GOARCH, and the experiment list (X:none). // Reading the first 1024 bytes should be plenty. - f, err := os.Open(p.Target) + f, err := os.Open(target) if err != nil { return "", err } @@ -1904,7 +1904,7 @@ func readBuildID(p *Package) (id string, err error) { } bad := func() (string, error) { - return "", &os.PathError{Op: "parse", Path: p.Target, Err: errBuildIDMalformed} + return "", &os.PathError{Op: "parse", Path: target, Err: errBuildIDMalformed} } // Archive header. diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index 6482f0fd32..1445e9f395 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -545,7 +545,7 @@ func runTest(cmd *Command, args []string) { // Prepare build + run + print actions for all packages being tested. for _, p := range pkgs { - buildTest, runTest, printTest, err := b.test(p) + buildTest, runTest, printTest, err := builderTest(&b, p) if err != nil { str := err.Error() if strings.HasPrefix(str, "\n") { @@ -652,11 +652,11 @@ var windowsBadWords = []string{ "update", } -func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, err error) { +func builderTest(b *builder, p *Package) (buildAction, runAction, printAction *action, err error) { if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 { build := b.action(modeBuild, modeBuild, p) run := &action{p: p, deps: []*action{build}} - print := &action{f: (*builder).notest, p: p, deps: []*action{run}} + print := &action{f: builderNoTest, p: p, deps: []*action{run}} return build, run, print, nil } @@ -991,18 +991,18 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, } else { // run test runAction = &action{ - f: (*builder).runTest, + f: builderRunTest, deps: []*action{buildAction}, p: p, ignoreFail: true, } cleanAction := &action{ - f: (*builder).cleanTest, + f: builderCleanTest, deps: []*action{runAction}, p: p, } printAction = &action{ - f: (*builder).printTest, + f: builderPrintTest, deps: []*action{cleanAction}, p: p, } @@ -1101,8 +1101,8 @@ func declareCoverVars(importPath string, files ...string) map[string]*CoverVar { var noTestsToRun = []byte("\ntesting: warning: no tests to run\n") -// runTest is the action for running a test binary. -func (b *builder) runTest(a *action) error { +// builderRunTest is the action for running a test binary. +func builderRunTest(b *builder, a *action) error { args := stringList(findExecCmd(), a.deps[0].target, testArgs) a.testOutput = new(bytes.Buffer) @@ -1233,8 +1233,8 @@ func coveragePercentage(out []byte) string { return fmt.Sprintf("\tcoverage: %s", matches[1]) } -// cleanTest is the action for cleaning up after a test. -func (b *builder) cleanTest(a *action) error { +// builderCleanTest is the action for cleaning up after a test. +func builderCleanTest(b *builder, a *action) error { if buildWork { return nil } @@ -1244,8 +1244,8 @@ func (b *builder) cleanTest(a *action) error { return nil } -// printTest is the action for printing a test result. -func (b *builder) printTest(a *action) error { +// builderPrintTest is the action for printing a test result. +func builderPrintTest(b *builder, a *action) error { clean := a.deps[0] run := clean.deps[0] os.Stdout.Write(run.testOutput.Bytes()) @@ -1253,8 +1253,8 @@ func (b *builder) printTest(a *action) error { return nil } -// notest is the action for testing a package with no test files. -func (b *builder) notest(a *action) error { +// builderNoTest is the action for testing a package with no test files. +func builderNoTest(b *builder, a *action) error { fmt.Printf("? \t%s\t[no test files]\n", a.p.ImportPath) return nil } -- 2.48.1