diff options
Diffstat (limited to 'src/cmd/go/internal/work/build.go')
-rw-r--r-- | src/cmd/go/internal/work/build.go | 956 |
1 files changed, 956 insertions, 0 deletions
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go new file mode 100644 index 0000000..e2e0e07 --- /dev/null +++ b/src/cmd/go/internal/work/build.go @@ -0,0 +1,956 @@ +// Copyright 2011 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 work + +import ( + "context" + "errors" + "flag" + "fmt" + "go/build" + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "strings" + + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/fsys" + "cmd/go/internal/load" + "cmd/go/internal/modload" + "cmd/go/internal/search" + "cmd/go/internal/trace" +) + +var CmdBuild = &base.Command{ + UsageLine: "go build [-o output] [build flags] [packages]", + Short: "compile packages and dependencies", + Long: ` +Build compiles the packages named by the import paths, +along with their dependencies, but it does not install the results. + +If the arguments to build are a list of .go files from a single directory, +build treats them as a list of source files specifying a single package. + +When compiling packages, build ignores files that end in '_test.go'. + +When compiling a single main package, build writes +the resulting executable to an output file named after +the first source file ('go build ed.go rx.go' writes 'ed' or 'ed.exe') +or the source code directory ('go build unix/sam' writes 'sam' or 'sam.exe'). +The '.exe' suffix is added when writing a Windows executable. + +When compiling multiple packages or a single non-main package, +build compiles the packages but discards the resulting object, +serving only as a check that the packages can be built. + +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 an existing directory or +ends with a slash or backslash, then any resulting executables +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. + If used, this flag must be the first one in the command line. + -a + force rebuilding of packages that are already up-to-date. + -n + print the commands but do not run them. + -p n + the number of programs, such as build commands or + test binaries, that can be run in parallel. + The default is GOMAXPROCS, normally the number of CPUs available. + -race + enable data race detection. + Supported only on linux/amd64, freebsd/amd64, darwin/amd64, darwin/arm64, windows/amd64, + linux/ppc64le and linux/arm64 (only for 48-bit VMA). + -msan + enable interoperation with memory sanitizer. + Supported only on linux/amd64, linux/arm64, freebsd/amd64 + and only with Clang/LLVM as the host C compiler. + PIE build mode will be used on all platforms except linux/amd64. + -asan + enable interoperation with address sanitizer. + Supported only on linux/arm64, linux/amd64. + Supported only on linux/amd64 or linux/arm64 and only with GCC 7 and higher + or Clang/LLVM 9 and higher. + -cover + enable code coverage instrumentation. + -covermode set,count,atomic + set the mode for coverage analysis. + The default is "set" unless -race is enabled, + in which case it is "atomic". + The values: + set: bool: does this statement run? + count: int: how many times does this statement run? + atomic: int: count, but correct in multithreaded tests; + significantly more expensive. + Sets -cover. + -coverpkg pattern1,pattern2,pattern3 + For a build that targets package 'main' (e.g. building a Go + executable), apply coverage analysis to each package matching + the patterns. The default is to apply coverage analysis to + packages in the main Go module. See 'go help packages' for a + description of package patterns. Sets -cover. + -v + print the names of packages as they are compiled. + -work + print the name of the temporary work directory and + do not delete it when exiting. + -x + print the commands. + -asmflags '[pattern=]arg list' + arguments to pass on each go tool asm invocation. + -buildmode mode + build mode to use. See 'go help buildmode' for more. + -buildvcs + Whether to stamp binaries with version control information + ("true", "false", or "auto"). By default ("auto"), version control + information is stamped into a binary if the main package, the main module + containing it, and the current directory are all in the same repository. + Use -buildvcs=false to always omit version control information, or + -buildvcs=true to error out if version control information is available but + cannot be included due to a missing tool or ambiguous directory structure. + -compiler name + name of compiler to use, as in runtime.Compiler (gccgo or gc). + -gccgoflags '[pattern=]arg list' + arguments to pass on each gccgo compiler/linker invocation. + -gcflags '[pattern=]arg list' + arguments to pass on each go tool compile invocation. + -installsuffix suffix + a suffix to use in the name of the package installation directory, + in order to keep output separate from default builds. + If using the -race flag, the install suffix is automatically set to race + or, if set explicitly, has _race appended to it. Likewise for the -msan + and -asan flags. Using a -buildmode option that requires non-default compile + flags has a similar effect. + -ldflags '[pattern=]arg list' + arguments to pass on each go tool link invocation. + -linkshared + build code that will be linked against shared libraries previously + created with -buildmode=shared. + -mod mode + module download mode to use: readonly, vendor, or mod. + By default, if a vendor directory is present and the go version in go.mod + is 1.14 or higher, the go command acts as if -mod=vendor were set. + Otherwise, the go command acts as if -mod=readonly were set. + See https://golang.org/ref/mod#build-commands for details. + -modcacherw + leave newly-created directories in the module cache read-write + instead of making them read-only. + -modfile file + in module aware mode, read (and possibly write) an alternate go.mod + file instead of the one in the module root directory. A file named + "go.mod" must still be present in order to determine the module root + directory, but it is not accessed. When -modfile is specified, an + alternate go.sum file is also used: its path is derived from the + -modfile flag by trimming the ".mod" extension and appending ".sum". + -overlay file + read a JSON config file that provides an overlay for build operations. + The file is a JSON struct with a single field, named 'Replace', that + maps each disk file path (a string) to its backing file path, so that + a build will run as if the disk file path exists with the contents + given by the backing file paths, or as if the disk file path does not + exist if its backing file path is empty. Support for the -overlay flag + has some limitations: importantly, cgo files included from outside the + include path must be in the same directory as the Go package they are + included from, and overlays will not appear when binaries and tests are + run through go run and go test respectively. + -pgo file + specify the file path of a profile for profile-guided optimization (PGO). + When the special name "auto" is specified, for each main package in the + build, the go command selects a file named "default.pgo" in the package's + directory if that file exists, and applies it to the (transitive) + dependencies of the main package (other packages are not affected). + Special name "off" turns off PGO. The default is "auto". + -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 comma-separated list of additional build tags to consider satisfied + during the build. For more information about build tags, see + 'go help buildconstraint'. (Earlier versions of Go used a + space-separated list, and that form is deprecated but still recognized.) + -trimpath + remove all file system paths from the resulting executable. + Instead of absolute file system paths, the recorded file names + will begin either a module path@version (when using modules), + or a plain import path (when using the standard library, or GOPATH). + -toolexec 'cmd args' + a program to use to invoke toolchain programs like vet and asm. + For example, instead of running asm, the go command will run + 'cmd args /path/to/asm <arguments for asm>'. + The TOOLEXEC_IMPORTPATH environment variable will be set, + matching 'go list -f {{.ImportPath}}' for the package being built. + +The -asmflags, -gccgoflags, -gcflags, and -ldflags flags accept a +space-separated list of arguments to pass to an underlying tool +during the build. To embed spaces in an element in the list, surround +it with either single or double quotes. The argument list may be +preceded by a package pattern and an equal sign, which restricts +the use of that argument list to the building of packages matching +that pattern (see 'go help packages' for a description of package +patterns). Without a pattern, the argument list applies only to the +packages named on the command line. The flags may be repeated +with different patterns in order to specify different arguments for +different sets of packages. If a package matches patterns given in +multiple flags, the latest match on the command line wins. +For example, 'go build -gcflags=-S fmt' prints the disassembly +only for package fmt, while 'go build -gcflags=all=-S fmt' +prints the disassembly for fmt and all its dependencies. + +For more about specifying packages, see 'go help packages'. +For more about where packages and binaries are installed, +run 'go help gopath'. +For more about calling between Go and C/C++, run 'go help c'. + +Note: Build adheres to certain conventions such as those described +by 'go help gopath'. Not all projects can follow these conventions, +however. Installations that have their own conventions or that use +a separate software build system may choose to use lower-level +invocations such as 'go tool compile' and 'go tool link' to avoid +some of the overheads and design decisions of the build tool. + +See also: go install, go get, go clean. + `, +} + +const concurrentGCBackendCompilationEnabledByDefault = true + +func init() { + // break init cycle + CmdBuild.Run = runBuild + CmdInstall.Run = runInstall + + CmdBuild.Flag.StringVar(&cfg.BuildO, "o", "", "output file or directory") + + AddBuildFlags(CmdBuild, DefaultBuildFlags) + AddBuildFlags(CmdInstall, DefaultBuildFlags) + if cfg.Experiment != nil && cfg.Experiment.CoverageRedesign { + AddCoverFlags(CmdBuild, nil) + AddCoverFlags(CmdInstall, nil) + } +} + +// Note that flags consulted by other parts of the code +// (for example, buildV) are in cmd/go/internal/cfg. + +var ( + forcedAsmflags []string // internally-forced flags for cmd/asm + forcedGcflags []string // internally-forced flags for cmd/compile + forcedLdflags []string // internally-forced flags for cmd/link + forcedGccgoflags []string // internally-forced flags for gccgo +) + +var BuildToolchain toolchain = noToolchain{} +var ldBuildmode string + +// buildCompiler implements flag.Var. +// It implements Set by updating both +// BuildToolchain and buildContext.Compiler. +type buildCompiler struct{} + +func (c buildCompiler) Set(value string) error { + switch value { + case "gc": + BuildToolchain = gcToolchain{} + case "gccgo": + BuildToolchain = gccgoToolchain{} + default: + return fmt.Errorf("unknown compiler %q", value) + } + cfg.BuildToolchainName = value + cfg.BuildToolchainCompiler = BuildToolchain.compiler + cfg.BuildToolchainLinker = BuildToolchain.linker + cfg.BuildContext.Compiler = value + return nil +} + +func (c buildCompiler) String() string { + return cfg.BuildContext.Compiler +} + +func init() { + switch build.Default.Compiler { + case "gc", "gccgo": + buildCompiler{}.Set(build.Default.Compiler) + } +} + +type BuildFlagMask int + +const ( + DefaultBuildFlags BuildFlagMask = 0 + OmitModFlag BuildFlagMask = 1 << iota + OmitModCommonFlags + OmitVFlag +) + +// AddBuildFlags adds the flags common to the build, clean, get, +// 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 { + cmd.Flag.BoolVar(&cfg.BuildV, "v", false, "") + } + + cmd.Flag.Var(&load.BuildAsmflags, "asmflags", "") + cmd.Flag.Var(buildCompiler{}, "compiler", "") + cmd.Flag.StringVar(&cfg.BuildBuildmode, "buildmode", "default", "") + cmd.Flag.Var(&load.BuildGcflags, "gcflags", "") + cmd.Flag.Var(&load.BuildGccgoflags, "gccgoflags", "") + if mask&OmitModFlag == 0 { + base.AddModFlag(&cmd.Flag) + } + if mask&OmitModCommonFlags == 0 { + base.AddModCommonFlags(&cmd.Flag) + } else { + // Add the overlay flag even when we don't add the rest of the mod common flags. + // This only affects 'go get' in GOPATH mode, but add the flag anyway for + // consistency. + cmd.Flag.StringVar(&fsys.OverlayFile, "overlay", "", "") + } + cmd.Flag.StringVar(&cfg.BuildContext.InstallSuffix, "installsuffix", "", "") + cmd.Flag.Var(&load.BuildLdflags, "ldflags", "") + cmd.Flag.BoolVar(&cfg.BuildLinkshared, "linkshared", false, "") + cmd.Flag.StringVar(&cfg.BuildPGO, "pgo", "auto", "") + cmd.Flag.StringVar(&cfg.BuildPkgdir, "pkgdir", "", "") + cmd.Flag.BoolVar(&cfg.BuildRace, "race", false, "") + cmd.Flag.BoolVar(&cfg.BuildMSan, "msan", false, "") + cmd.Flag.BoolVar(&cfg.BuildASan, "asan", false, "") + cmd.Flag.Var((*tagsFlag)(&cfg.BuildContext.BuildTags), "tags", "") + cmd.Flag.Var((*base.StringsFlag)(&cfg.BuildToolexec), "toolexec", "") + cmd.Flag.BoolVar(&cfg.BuildTrimpath, "trimpath", false, "") + cmd.Flag.BoolVar(&cfg.BuildWork, "work", false, "") + cmd.Flag.Var((*buildvcsFlag)(&cfg.BuildBuildvcs), "buildvcs", "") + + // Undocumented, unstable debugging flags. + cmd.Flag.StringVar(&cfg.DebugActiongraph, "debug-actiongraph", "", "") + cmd.Flag.StringVar(&cfg.DebugTrace, "debug-trace", "", "") + cmd.Flag.StringVar(&cfg.DebugRuntimeTrace, "debug-runtime-trace", "", "") +} + +// AddCoverFlags adds coverage-related flags to "cmd". If the +// CoverageRedesign experiment is enabled, we add -cover{mode,pkg} to +// the build command and only -coverprofile to the test command. If +// the CoverageRedesign experiment is disabled, -cover* flags are +// added only to the test command. +func AddCoverFlags(cmd *base.Command, coverProfileFlag *string) { + addCover := false + if cfg.Experiment != nil && cfg.Experiment.CoverageRedesign { + // New coverage enabled: both build and test commands get + // coverage flags. + addCover = true + } else { + // New coverage disabled: only test command gets cover flags. + addCover = coverProfileFlag != nil + } + if addCover { + cmd.Flag.BoolVar(&cfg.BuildCover, "cover", false, "") + cmd.Flag.Var(coverFlag{(*coverModeFlag)(&cfg.BuildCoverMode)}, "covermode", "") + cmd.Flag.Var(coverFlag{commaListFlag{&cfg.BuildCoverPkg}}, "coverpkg", "") + } + if coverProfileFlag != nil { + cmd.Flag.Var(coverFlag{V: stringFlag{coverProfileFlag}}, "coverprofile", "") + } +} + +// tagsFlag is the implementation of the -tags flag. +type tagsFlag []string + +func (v *tagsFlag) Set(s string) error { + // For compatibility with Go 1.12 and earlier, allow "-tags='a b c'" or even just "-tags='a'". + if strings.Contains(s, " ") || strings.Contains(s, "'") { + return (*base.StringsFlag)(v).Set(s) + } + + // Split on commas, ignore empty strings. + *v = []string{} + for _, s := range strings.Split(s, ",") { + if s != "" { + *v = append(*v, s) + } + } + return nil +} + +func (v *tagsFlag) String() string { + return "<TagsFlag>" +} + +// buildvcsFlag is the implementation of the -buildvcs flag. +type buildvcsFlag string + +func (f *buildvcsFlag) IsBoolFlag() bool { return true } // allow -buildvcs (without arguments) + +func (f *buildvcsFlag) Set(s string) error { + // https://go.dev/issue/51748: allow "-buildvcs=auto", + // in addition to the usual "true" and "false". + if s == "" || s == "auto" { + *f = "auto" + return nil + } + + b, err := strconv.ParseBool(s) + if err != nil { + return errors.New("value is neither 'auto' nor a valid bool") + } + *f = (buildvcsFlag)(strconv.FormatBool(b)) // convert to canonical "true" or "false" + return nil +} + +func (f *buildvcsFlag) String() string { return string(*f) } + +// fileExtSplit expects a filename and returns the name +// and ext (without the dot). If the file has no +// extension, ext will be empty. +func fileExtSplit(file string) (name, ext string) { + dotExt := filepath.Ext(file) + name = file[:len(file)-len(dotExt)] + if dotExt != "" { + ext = dotExt[1:] + } + return +} + +func pkgsMain(pkgs []*load.Package) (res []*load.Package) { + for _, p := range pkgs { + if p.Name == "main" { + res = append(res, p) + } + } + return res +} + +func pkgsNotMain(pkgs []*load.Package) (res []*load.Package) { + for _, p := range pkgs { + if p.Name != "main" { + res = append(res, p) + } + } + return res +} + +func oneMainPkg(pkgs []*load.Package) []*load.Package { + if len(pkgs) != 1 || pkgs[0].Name != "main" { + base.Fatalf("-buildmode=%s requires exactly one main package", cfg.BuildBuildmode) + } + return pkgs +} + +var pkgsFilter = func(pkgs []*load.Package) []*load.Package { return pkgs } + +func runBuild(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() + BuildInit() + b := NewBuilder("") + defer func() { + if err := b.Close(); err != nil { + base.Fatal(err) + } + }() + + pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{AutoVCS: true}, args) + load.CheckPackageErrors(pkgs) + + explicitO := len(cfg.BuildO) > 0 + + if len(pkgs) == 1 && pkgs[0].Name == "main" && cfg.BuildO == "" { + cfg.BuildO = pkgs[0].DefaultExecName() + cfg.BuildO += cfg.ExeSuffix + } + + // sanity check some often mis-used options + switch cfg.BuildContext.Compiler { + case "gccgo": + if load.BuildGcflags.Present() { + fmt.Println("go build: when using gccgo toolchain, please pass compiler flags using -gccgoflags, not -gcflags") + } + if load.BuildLdflags.Present() { + fmt.Println("go build: when using gccgo toolchain, please pass linker flags using -gccgoflags, not -ldflags") + } + case "gc": + if load.BuildGccgoflags.Present() { + fmt.Println("go build: when using gc toolchain, please pass compile flags using -gcflags, and linker flags using -ldflags") + } + } + + depMode := ModeBuild + + pkgs = omitTestOnly(pkgsFilter(pkgs)) + + // Special case -o /dev/null by not writing at all. + if base.IsNull(cfg.BuildO) { + cfg.BuildO = "" + } + + if cfg.Experiment.CoverageRedesign && cfg.BuildCover { + load.PrepareForCoverageBuild(pkgs) + } + + if cfg.BuildO != "" { + // If the -o name exists and is a directory or + // ends with a slash or backslash, then + // write all main packages to that directory. + // Otherwise require only a single package be built. + if fi, err := os.Stat(cfg.BuildO); (err == nil && fi.IsDir()) || + strings.HasSuffix(cfg.BuildO, "/") || + strings.HasSuffix(cfg.BuildO, string(os.PathSeparator)) { + if !explicitO { + base.Fatalf("go: build output %q already exists and is a directory", cfg.BuildO) + } + a := &Action{Mode: "go build"} + for _, p := range pkgs { + if p.Name != "main" { + continue + } + + p.Target = filepath.Join(cfg.BuildO, p.DefaultExecName()) + 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: no main packages to build") + } + b.Do(ctx, a) + return + } + if len(pkgs) > 1 { + base.Fatalf("go: cannot write multiple packages to non-directory %s", cfg.BuildO) + } else if len(pkgs) == 0 { + base.Fatalf("no packages to build") + } + p := pkgs[0] + p.Target = cfg.BuildO + p.Stale = true // must build - not up to date + p.StaleReason = "build -o flag in use" + a := b.AutoAction(ModeInstall, depMode, p) + b.Do(ctx, a) + return + } + + a := &Action{Mode: "go build"} + for _, p := range pkgs { + a.Deps = append(a.Deps, b.AutoAction(ModeBuild, depMode, p)) + } + if cfg.BuildBuildmode == "shared" { + a = b.buildmodeShared(ModeBuild, depMode, args, pkgs, a) + } + b.Do(ctx, a) +} + +var CmdInstall = &base.Command{ + UsageLine: "go install [build flags] [packages]", + Short: "compile and install packages and dependencies", + Long: ` +Install compiles and installs the packages named by the import paths. + +Executables are installed in the directory named by the GOBIN environment +variable, which defaults to $GOPATH/bin or $HOME/go/bin if the GOPATH +environment variable is not set. Executables in $GOROOT +are installed in $GOROOT/bin or $GOTOOLDIR instead of $GOBIN. + +If the arguments have version suffixes (like @latest or @v1.0.0), "go install" +builds packages in module-aware mode, ignoring the go.mod file in the current +directory or any parent directory, if there is one. This is useful for +installing executables without affecting the dependencies of the main module. +To eliminate ambiguity about which module versions are used in the build, the +arguments must satisfy the following constraints: + +- Arguments must be package paths or package patterns (with "..." wildcards). +They must not be standard packages (like fmt), meta-patterns (std, cmd, +all), or relative or absolute file paths. + +- All arguments must have the same version suffix. Different queries are not +allowed, even if they refer to the same version. + +- All arguments must refer to packages in the same module at the same version. + +- Package path arguments must refer to main packages. Pattern arguments +will only match main packages. + +- No module is considered the "main" module. If the module containing +packages named on the command line has a go.mod file, it must not contain +directives (replace and exclude) that would cause it to be interpreted +differently than if it were the main module. The module must not require +a higher version of itself. + +- Vendor directories are not used in any module. (Vendor directories are not +included in the module zip files downloaded by 'go install'.) + +If the arguments don't have version suffixes, "go install" may run in +module-aware mode or GOPATH mode, depending on the GO111MODULE environment +variable and the presence of a go.mod file. See 'go help modules' for details. +If module-aware mode is enabled, "go install" runs in the context of the main +module. + +When module-aware mode is disabled, non-main packages are installed in the +directory $GOPATH/pkg/$GOOS_$GOARCH. When module-aware mode is enabled, +non-main packages are built and cached but not installed. + +Before Go 1.20, the standard library was installed to +$GOROOT/pkg/$GOOS_$GOARCH. +Starting in Go 1.20, the standard library is built and cached but not installed. +Setting GODEBUG=installgoroot=all restores the use of +$GOROOT/pkg/$GOOS_$GOARCH. + +For more about build flags, see 'go help build'. + +For more about specifying packages, see 'go help packages'. + +See also: go build, go get, go clean. + `, +} + +// libname returns the filename to use for the shared library when using +// -buildmode=shared. The rules we use are: +// Use arguments for special 'meta' packages: +// +// std --> libstd.so +// std cmd --> libstd,cmd.so +// +// A single non-meta argument with trailing "/..." is special cased: +// +// foo/... --> libfoo.so +// (A relative path like "./..." expands the "." first) +// +// Use import paths for other cases, changing '/' to '-': +// +// somelib --> libsubdir-somelib.so +// ./ or ../ --> libsubdir-somelib.so +// gopkg.in/tomb.v2 -> libgopkg.in-tomb.v2.so +// a/... b/... ---> liba/c,b/d.so - all matching import paths +// +// Name parts are joined with ','. +func libname(args []string, pkgs []*load.Package) (string, error) { + var libname string + appendName := func(arg string) { + if libname == "" { + libname = arg + } else { + libname += "," + arg + } + } + var haveNonMeta bool + for _, arg := range args { + if search.IsMetaPackage(arg) { + appendName(arg) + } else { + haveNonMeta = true + } + } + if len(libname) == 0 { // non-meta packages only. use import paths + if len(args) == 1 && strings.HasSuffix(args[0], "/...") { + // Special case of "foo/..." as mentioned above. + arg := strings.TrimSuffix(args[0], "/...") + if build.IsLocalImport(arg) { + cwd, _ := os.Getwd() + bp, _ := cfg.BuildContext.ImportDir(filepath.Join(cwd, arg), build.FindOnly) + if bp.ImportPath != "" && bp.ImportPath != "." { + arg = bp.ImportPath + } + } + appendName(strings.ReplaceAll(arg, "/", "-")) + } else { + for _, pkg := range pkgs { + appendName(strings.ReplaceAll(pkg.ImportPath, "/", "-")) + } + } + } else if haveNonMeta { // have both meta package and a non-meta one + return "", errors.New("mixing of meta and non-meta packages is not allowed") + } + // TODO(mwhudson): Needs to change for platforms that use different naming + // conventions... + return "lib" + libname + ".so", nil +} + +func runInstall(ctx context.Context, cmd *base.Command, args []string) { + for _, arg := range args { + if strings.Contains(arg, "@") && !build.IsLocalImport(arg) && !filepath.IsAbs(arg) { + installOutsideModule(ctx, args) + return + } + } + + modload.InitWorkfile() + BuildInit() + pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{AutoVCS: true}, args) + if cfg.ModulesEnabled && !modload.HasModRoot() { + haveErrors := false + allMissingErrors := true + for _, pkg := range pkgs { + if pkg.Error == nil { + continue + } + haveErrors = true + if missingErr := (*modload.ImportMissingError)(nil); !errors.As(pkg.Error, &missingErr) { + allMissingErrors = false + break + } + } + if haveErrors && allMissingErrors { + latestArgs := make([]string, len(args)) + for i := range args { + latestArgs[i] = args[i] + "@latest" + } + hint := strings.Join(latestArgs, " ") + base.Fatalf("go: 'go install' requires a version when current directory is not in a module\n\tTry 'go install %s' to install the latest version", hint) + } + } + load.CheckPackageErrors(pkgs) + + if cfg.Experiment.CoverageRedesign && cfg.BuildCover { + load.PrepareForCoverageBuild(pkgs) + } + + InstallPackages(ctx, args, pkgs) +} + +// omitTestOnly returns pkgs with test-only packages removed. +func omitTestOnly(pkgs []*load.Package) []*load.Package { + var list []*load.Package + for _, p := range pkgs { + if len(p.GoFiles)+len(p.CgoFiles) == 0 && !p.Internal.CmdlinePkgLiteral { + // Package has no source files, + // perhaps due to build tags or perhaps due to only having *_test.go files. + // Also, it is only being processed as the result of a wildcard match + // like ./..., not because it was listed as a literal path on the command line. + // Ignore it. + continue + } + list = append(list, p) + } + return list +} + +func InstallPackages(ctx context.Context, patterns []string, pkgs []*load.Package) { + ctx, span := trace.StartSpan(ctx, "InstallPackages "+strings.Join(patterns, " ")) + defer span.Done() + + if cfg.GOBIN != "" && !filepath.IsAbs(cfg.GOBIN) { + base.Fatalf("cannot install, GOBIN must be an absolute path") + } + + pkgs = omitTestOnly(pkgsFilter(pkgs)) + for _, p := range pkgs { + if p.Target == "" { + switch { + case p.Name != "main" && p.Internal.Local && p.ConflictDir == "": + // Non-executables outside GOPATH need not have a target: + // we can use the cache to hold the built package archive for use in future builds. + // The ones inside GOPATH should have a target (in GOPATH/pkg) + // or else something is wrong and worth reporting (like a ConflictDir). + case p.Name != "main" && p.Module != nil: + // Non-executables have no target (except the cache) when building with modules. + case p.Name != "main" && p.Standard && p.Internal.Build.PkgObj == "": + // Most packages in std do not need an installed .a, because they can be + // rebuilt and used directly from the build cache. + // A few targets (notably those using cgo) still do need to be installed + // in case the user's environment lacks a C compiler. + case p.Internal.GobinSubdir: + base.Errorf("go: cannot install cross-compiled binaries when GOBIN is set") + case p.Internal.CmdlineFiles: + base.Errorf("go: no install location for .go files listed on command line (GOBIN not set)") + case p.ConflictDir != "": + base.Errorf("go: no install location for %s: hidden by %s", p.Dir, p.ConflictDir) + default: + base.Errorf("go: no install location for directory %s outside GOPATH\n"+ + "\tFor more details see: 'go help gopath'", p.Dir) + } + } + } + base.ExitIfErrors() + + b := NewBuilder("") + defer func() { + if err := b.Close(); err != nil { + base.Fatal(err) + } + }() + + depMode := ModeBuild + a := &Action{Mode: "go install"} + var tools []*Action + for _, p := range pkgs { + // If p is a tool, delay the installation until the end of the build. + // This avoids installing assemblers/compilers that are being executed + // by other steps in the build. + a1 := b.AutoAction(ModeInstall, depMode, p) + if load.InstallTargetDir(p) == load.ToTool { + a.Deps = append(a.Deps, a1.Deps...) + a1.Deps = append(a1.Deps, a) + tools = append(tools, a1) + continue + } + a.Deps = append(a.Deps, a1) + } + if len(tools) > 0 { + a = &Action{ + Mode: "go install (tools)", + Deps: tools, + } + } + + if cfg.BuildBuildmode == "shared" { + // Note: If buildmode=shared then only non-main packages + // are present in the pkgs list, so all the special case code about + // tools above did not apply, and a is just a simple Action + // with a list of Deps, one per package named in pkgs, + // the same as in runBuild. + a = b.buildmodeShared(ModeInstall, ModeInstall, patterns, pkgs, a) + } + + b.Do(ctx, a) + base.ExitIfErrors() + + // Success. If this command is 'go install' with no arguments + // and the current directory (the implicit argument) is a command, + // remove any leftover command binary from a previous 'go build'. + // The binary is installed; it's not needed here anymore. + // And worse it might be a stale copy, which you don't want to find + // instead of the installed one if $PATH contains dot. + // One way to view this behavior is that it is as if 'go install' first + // runs 'go build' and the moves the generated file to the install dir. + // See issue 9645. + if len(patterns) == 0 && len(pkgs) == 1 && pkgs[0].Name == "main" { + // Compute file 'go build' would have created. + // If it exists and is an executable file, remove it. + targ := pkgs[0].DefaultExecName() + targ += cfg.ExeSuffix + if filepath.Join(pkgs[0].Dir, targ) != pkgs[0].Target { // maybe $GOBIN is the current directory + fi, err := os.Stat(targ) + if err == nil { + m := fi.Mode() + if m.IsRegular() { + if m&0111 != 0 || cfg.Goos == "windows" { // windows never sets executable bit + os.Remove(targ) + } + } + } + } + } +} + +// installOutsideModule implements 'go install pkg@version'. It builds and +// installs one or more main packages in module mode while ignoring any go.mod +// in the current directory or parent directories. +// +// See golang.org/issue/40276 for details and rationale. +func installOutsideModule(ctx context.Context, args []string) { + modload.ForceUseModules = true + modload.RootMode = modload.NoRoot + modload.AllowMissingModuleImports() + modload.Init() + BuildInit() + + // Load packages. Ignore non-main packages. + // Print a warning if an argument contains "..." and matches no main packages. + // PackagesAndErrors already prints warnings for patterns that don't match any + // packages, so be careful not to double print. + // TODO(golang.org/issue/40276): don't report errors loading non-main packages + // matched by a pattern. + pkgOpts := load.PackageOpts{MainOnly: true} + pkgs, err := load.PackagesAndErrorsOutsideModule(ctx, pkgOpts, args) + if err != nil { + base.Fatal(err) + } + load.CheckPackageErrors(pkgs) + patterns := make([]string, len(args)) + for i, arg := range args { + patterns[i] = arg[:strings.Index(arg, "@")] + } + + // Build and install the packages. + InstallPackages(ctx, patterns, pkgs) +} + +// ExecCmd is the command to use to run user binaries. +// Normally it is empty, meaning run the binaries directly. +// If cross-compiling and running on a remote system or +// simulator, it is typically go_GOOS_GOARCH_exec, with +// the target GOOS and GOARCH substituted. +// The -exec flag overrides these defaults. +var ExecCmd []string + +// FindExecCmd derives the value of ExecCmd to use. +// It returns that value and leaves ExecCmd set for direct use. +func FindExecCmd() []string { + if ExecCmd != nil { + return ExecCmd + } + ExecCmd = []string{} // avoid work the second time + if cfg.Goos == runtime.GOOS && cfg.Goarch == runtime.GOARCH { + return ExecCmd + } + path, err := exec.LookPath(fmt.Sprintf("go_%s_%s_exec", cfg.Goos, cfg.Goarch)) + if err == nil { + ExecCmd = []string{path} + } + return ExecCmd +} + +// A coverFlag is a flag.Value that also implies -cover. +type coverFlag struct{ V flag.Value } + +func (f coverFlag) String() string { return f.V.String() } + +func (f coverFlag) Set(value string) error { + if err := f.V.Set(value); err != nil { + return err + } + cfg.BuildCover = true + return nil +} + +type coverModeFlag string + +func (f *coverModeFlag) String() string { return string(*f) } +func (f *coverModeFlag) Set(value string) error { + switch value { + case "", "set", "count", "atomic": + *f = coverModeFlag(value) + cfg.BuildCoverMode = value + return nil + default: + return errors.New(`valid modes are "set", "count", or "atomic"`) + } +} + +// A commaListFlag is a flag.Value representing a comma-separated list. +type commaListFlag struct{ Vals *[]string } + +func (f commaListFlag) String() string { return strings.Join(*f.Vals, ",") } + +func (f commaListFlag) Set(value string) error { + if value == "" { + *f.Vals = nil + } else { + *f.Vals = strings.Split(value, ",") + } + return nil +} + +// A stringFlag is a flag.Value representing a single string. +type stringFlag struct{ val *string } + +func (f stringFlag) String() string { return *f.val } +func (f stringFlag) Set(value string) error { + *f.val = value + return nil +} |