1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package work
6
7import (
8	"errors"
9	"fmt"
10	"go/build"
11	"os"
12	"os/exec"
13	"path/filepath"
14	"runtime"
15	"strings"
16
17	"cmd/go/internal/base"
18	"cmd/go/internal/cfg"
19	"cmd/go/internal/load"
20	"cmd/go/internal/search"
21)
22
23var CmdBuild = &base.Command{
24	UsageLine: "go build [-o output] [-i] [build flags] [packages]",
25	Short:     "compile packages and dependencies",
26	Long: `
27Build compiles the packages named by the import paths,
28along with their dependencies, but it does not install the results.
29
30If the arguments to build are a list of .go files from a single directory,
31build treats them as a list of source files specifying a single package.
32
33When compiling packages, build ignores files that end in '_test.go'.
34
35When compiling a single main package, build writes
36the resulting executable to an output file named after
37the first source file ('go build ed.go rx.go' writes 'ed' or 'ed.exe')
38or the source code directory ('go build unix/sam' writes 'sam' or 'sam.exe').
39The '.exe' suffix is added when writing a Windows executable.
40
41When compiling multiple packages or a single non-main package,
42build compiles the packages but discards the resulting object,
43serving only as a check that the packages can be built.
44
45The -o flag forces build to write the resulting executable or object
46to the named output file or directory, instead of the default behavior described
47in the last two paragraphs. If the named output is a directory that exists,
48then any resulting executables will be written to that directory.
49
50The -i flag installs the packages that are dependencies of the target.
51
52The build flags are shared by the build, clean, get, install, list, run,
53and test commands:
54
55	-a
56		force rebuilding of packages that are already up-to-date.
57	-n
58		print the commands but do not run them.
59	-p n
60		the number of programs, such as build commands or
61		test binaries, that can be run in parallel.
62		The default is the number of CPUs available.
63	-race
64		enable data race detection.
65		Supported only on linux/amd64, freebsd/amd64, darwin/amd64, windows/amd64,
66		linux/ppc64le and linux/arm64 (only for 48-bit VMA).
67	-msan
68		enable interoperation with memory sanitizer.
69		Supported only on linux/amd64, linux/arm64
70		and only with Clang/LLVM as the host C compiler.
71		On linux/arm64, pie build mode will be used.
72	-v
73		print the names of packages as they are compiled.
74	-work
75		print the name of the temporary work directory and
76		do not delete it when exiting.
77	-x
78		print the commands.
79
80	-asmflags '[pattern=]arg list'
81		arguments to pass on each go tool asm invocation.
82	-buildmode mode
83		build mode to use. See 'go help buildmode' for more.
84	-compiler name
85		name of compiler to use, as in runtime.Compiler (gccgo or gc).
86	-gccgoflags '[pattern=]arg list'
87		arguments to pass on each gccgo compiler/linker invocation.
88	-gcflags '[pattern=]arg list'
89		arguments to pass on each go tool compile invocation.
90	-installsuffix suffix
91		a suffix to use in the name of the package installation directory,
92		in order to keep output separate from default builds.
93		If using the -race flag, the install suffix is automatically set to race
94		or, if set explicitly, has _race appended to it. Likewise for the -msan
95		flag. Using a -buildmode option that requires non-default compile flags
96		has a similar effect.
97	-ldflags '[pattern=]arg list'
98		arguments to pass on each go tool link invocation.
99	-linkshared
100		build code that will be linked against shared libraries previously
101		created with -buildmode=shared.
102	-mod mode
103		module download mode to use: readonly, vendor, or mod.
104		See 'go help modules' for more.
105	-modcacherw
106		leave newly-created directories in the module cache read-write
107		instead of making them read-only.
108	-modfile file
109		in module aware mode, read (and possibly write) an alternate go.mod
110		file instead of the one in the module root directory. A file named
111		"go.mod" must still be present in order to determine the module root
112		directory, but it is not accessed. When -modfile is specified, an
113		alternate go.sum file is also used: its path is derived from the
114		-modfile flag by trimming the ".mod" extension and appending ".sum".
115	-pkgdir dir
116		install and load all packages from dir instead of the usual locations.
117		For example, when building with a non-standard configuration,
118		use -pkgdir to keep generated packages in a separate location.
119	-tags tag,list
120		a comma-separated list of build tags to consider satisfied during the
121		build. For more information about build tags, see the description of
122		build constraints in the documentation for the go/build package.
123		(Earlier versions of Go used a space-separated list, and that form
124		is deprecated but still recognized.)
125	-trimpath
126		remove all file system paths from the resulting executable.
127		Instead of absolute file system paths, the recorded file names
128		will begin with either "go" (for the standard library),
129		or a module path@version (when using modules),
130		or a plain import path (when using GOPATH).
131	-toolexec 'cmd args'
132		a program to use to invoke toolchain programs like vet and asm.
133		For example, instead of running asm, the go command will run
134		'cmd args /path/to/asm <arguments for asm>'.
135
136The -asmflags, -gccgoflags, -gcflags, and -ldflags flags accept a
137space-separated list of arguments to pass to an underlying tool
138during the build. To embed spaces in an element in the list, surround
139it with either single or double quotes. The argument list may be
140preceded by a package pattern and an equal sign, which restricts
141the use of that argument list to the building of packages matching
142that pattern (see 'go help packages' for a description of package
143patterns). Without a pattern, the argument list applies only to the
144packages named on the command line. The flags may be repeated
145with different patterns in order to specify different arguments for
146different sets of packages. If a package matches patterns given in
147multiple flags, the latest match on the command line wins.
148For example, 'go build -gcflags=-S fmt' prints the disassembly
149only for package fmt, while 'go build -gcflags=all=-S fmt'
150prints the disassembly for fmt and all its dependencies.
151
152For more about specifying packages, see 'go help packages'.
153For more about where packages and binaries are installed,
154run 'go help gopath'.
155For more about calling between Go and C/C++, run 'go help c'.
156
157Note: Build adheres to certain conventions such as those described
158by 'go help gopath'. Not all projects can follow these conventions,
159however. Installations that have their own conventions or that use
160a separate software build system may choose to use lower-level
161invocations such as 'go tool compile' and 'go tool link' to avoid
162some of the overheads and design decisions of the build tool.
163
164See also: go install, go get, go clean.
165	`,
166}
167
168const concurrentGCBackendCompilationEnabledByDefault = true
169
170func init() {
171	// break init cycle
172	CmdBuild.Run = runBuild
173	CmdInstall.Run = runInstall
174
175	CmdBuild.Flag.BoolVar(&cfg.BuildI, "i", false, "")
176	CmdBuild.Flag.StringVar(&cfg.BuildO, "o", "", "output file or directory")
177
178	CmdInstall.Flag.BoolVar(&cfg.BuildI, "i", false, "")
179
180	AddBuildFlags(CmdBuild, DefaultBuildFlags)
181	AddBuildFlags(CmdInstall, DefaultBuildFlags)
182}
183
184// Note that flags consulted by other parts of the code
185// (for example, buildV) are in cmd/go/internal/cfg.
186
187var (
188	forcedAsmflags   []string // internally-forced flags for cmd/asm
189	forcedGcflags    []string // internally-forced flags for cmd/compile
190	forcedLdflags    []string // internally-forced flags for cmd/link
191	forcedGccgoflags []string // internally-forced flags for gccgo
192)
193
194var BuildToolchain toolchain = noToolchain{}
195var ldBuildmode string
196
197// buildCompiler implements flag.Var.
198// It implements Set by updating both
199// BuildToolchain and buildContext.Compiler.
200type buildCompiler struct{}
201
202func (c buildCompiler) Set(value string) error {
203	switch value {
204	case "gc":
205		BuildToolchain = gcToolchain{}
206	case "gccgo":
207		BuildToolchain = gccgoToolchain{}
208	default:
209		return fmt.Errorf("unknown compiler %q", value)
210	}
211	cfg.BuildToolchainName = value
212	cfg.BuildToolchainCompiler = BuildToolchain.compiler
213	cfg.BuildToolchainLinker = BuildToolchain.linker
214	cfg.BuildContext.Compiler = value
215	return nil
216}
217
218func (c buildCompiler) String() string {
219	return cfg.BuildContext.Compiler
220}
221
222func init() {
223	switch build.Default.Compiler {
224	case "gc", "gccgo":
225		buildCompiler{}.Set(build.Default.Compiler)
226	}
227}
228
229type BuildFlagMask int
230
231const (
232	DefaultBuildFlags BuildFlagMask = 0
233	OmitModFlag       BuildFlagMask = 1 << iota
234	OmitModCommonFlags
235)
236
237// AddBuildFlags adds the flags common to the build, clean, get,
238// install, list, run, and test commands.
239func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
240	cmd.Flag.BoolVar(&cfg.BuildA, "a", false, "")
241	cmd.Flag.BoolVar(&cfg.BuildN, "n", false, "")
242	cmd.Flag.IntVar(&cfg.BuildP, "p", cfg.BuildP, "")
243	cmd.Flag.BoolVar(&cfg.BuildV, "v", false, "")
244	cmd.Flag.BoolVar(&cfg.BuildX, "x", false, "")
245
246	cmd.Flag.Var(&load.BuildAsmflags, "asmflags", "")
247	cmd.Flag.Var(buildCompiler{}, "compiler", "")
248	cmd.Flag.StringVar(&cfg.BuildBuildmode, "buildmode", "default", "")
249	cmd.Flag.Var(&load.BuildGcflags, "gcflags", "")
250	cmd.Flag.Var(&load.BuildGccgoflags, "gccgoflags", "")
251	if mask&OmitModFlag == 0 {
252		cmd.Flag.StringVar(&cfg.BuildMod, "mod", "", "")
253	}
254	if mask&OmitModCommonFlags == 0 {
255		AddModCommonFlags(cmd)
256	}
257	cmd.Flag.StringVar(&cfg.BuildContext.InstallSuffix, "installsuffix", "", "")
258	cmd.Flag.Var(&load.BuildLdflags, "ldflags", "")
259	cmd.Flag.BoolVar(&cfg.BuildLinkshared, "linkshared", false, "")
260	cmd.Flag.StringVar(&cfg.BuildPkgdir, "pkgdir", "", "")
261	cmd.Flag.BoolVar(&cfg.BuildRace, "race", false, "")
262	cmd.Flag.BoolVar(&cfg.BuildMSan, "msan", false, "")
263	cmd.Flag.Var((*tagsFlag)(&cfg.BuildContext.BuildTags), "tags", "")
264	cmd.Flag.Var((*base.StringsFlag)(&cfg.BuildToolexec), "toolexec", "")
265	cmd.Flag.BoolVar(&cfg.BuildTrimpath, "trimpath", false, "")
266	cmd.Flag.BoolVar(&cfg.BuildWork, "work", false, "")
267
268	// Undocumented, unstable debugging flags.
269	cmd.Flag.StringVar(&cfg.DebugActiongraph, "debug-actiongraph", "", "")
270}
271
272// AddModCommonFlags adds the module-related flags common to build commands
273// and 'go mod' subcommands.
274func AddModCommonFlags(cmd *base.Command) {
275	cmd.Flag.BoolVar(&cfg.ModCacheRW, "modcacherw", false, "")
276	cmd.Flag.StringVar(&cfg.ModFile, "modfile", "", "")
277}
278
279// tagsFlag is the implementation of the -tags flag.
280type tagsFlag []string
281
282func (v *tagsFlag) Set(s string) error {
283	// For compatibility with Go 1.12 and earlier, allow "-tags='a b c'" or even just "-tags='a'".
284	if strings.Contains(s, " ") || strings.Contains(s, "'") {
285		return (*base.StringsFlag)(v).Set(s)
286	}
287
288	// Split on commas, ignore empty strings.
289	*v = []string{}
290	for _, s := range strings.Split(s, ",") {
291		if s != "" {
292			*v = append(*v, s)
293		}
294	}
295	return nil
296}
297
298func (v *tagsFlag) String() string {
299	return "<TagsFlag>"
300}
301
302// fileExtSplit expects a filename and returns the name
303// and ext (without the dot). If the file has no
304// extension, ext will be empty.
305func fileExtSplit(file string) (name, ext string) {
306	dotExt := filepath.Ext(file)
307	name = file[:len(file)-len(dotExt)]
308	if dotExt != "" {
309		ext = dotExt[1:]
310	}
311	return
312}
313
314func pkgsMain(pkgs []*load.Package) (res []*load.Package) {
315	for _, p := range pkgs {
316		if p.Name == "main" {
317			res = append(res, p)
318		}
319	}
320	return res
321}
322
323func pkgsNotMain(pkgs []*load.Package) (res []*load.Package) {
324	for _, p := range pkgs {
325		if p.Name != "main" {
326			res = append(res, p)
327		}
328	}
329	return res
330}
331
332func oneMainPkg(pkgs []*load.Package) []*load.Package {
333	if len(pkgs) != 1 || pkgs[0].Name != "main" {
334		base.Fatalf("-buildmode=%s requires exactly one main package", cfg.BuildBuildmode)
335	}
336	return pkgs
337}
338
339var pkgsFilter = func(pkgs []*load.Package) []*load.Package { return pkgs }
340
341var runtimeVersion = runtime.Version()
342
343func runBuild(cmd *base.Command, args []string) {
344	BuildInit()
345	var b Builder
346	b.Init()
347
348	pkgs := load.PackagesForBuild(args)
349
350	explicitO := len(cfg.BuildO) > 0
351
352	if len(pkgs) == 1 && pkgs[0].Name == "main" && cfg.BuildO == "" {
353		cfg.BuildO = pkgs[0].DefaultExecName()
354		cfg.BuildO += cfg.ExeSuffix
355	}
356
357	// sanity check some often mis-used options
358	switch cfg.BuildContext.Compiler {
359	case "gccgo":
360		if load.BuildGcflags.Present() {
361			fmt.Println("go build: when using gccgo toolchain, please pass compiler flags using -gccgoflags, not -gcflags")
362		}
363		if load.BuildLdflags.Present() {
364			fmt.Println("go build: when using gccgo toolchain, please pass linker flags using -gccgoflags, not -ldflags")
365		}
366	case "gc":
367		if load.BuildGccgoflags.Present() {
368			fmt.Println("go build: when using gc toolchain, please pass compile flags using -gcflags, and linker flags using -ldflags")
369		}
370	}
371
372	depMode := ModeBuild
373	if cfg.BuildI {
374		depMode = ModeInstall
375	}
376
377	pkgs = omitTestOnly(pkgsFilter(load.Packages(args)))
378
379	// Special case -o /dev/null by not writing at all.
380	if cfg.BuildO == os.DevNull {
381		cfg.BuildO = ""
382	}
383
384	if cfg.BuildO != "" {
385		// If the -o name exists and is a directory, then
386		// write all main packages to that directory.
387		// Otherwise require only a single package be built.
388		if fi, err := os.Stat(cfg.BuildO); err == nil && fi.IsDir() {
389			if !explicitO {
390				base.Fatalf("go build: build output %q already exists and is a directory", cfg.BuildO)
391			}
392			a := &Action{Mode: "go build"}
393			for _, p := range pkgs {
394				if p.Name != "main" {
395					continue
396				}
397
398				p.Target = filepath.Join(cfg.BuildO, p.DefaultExecName())
399				p.Target += cfg.ExeSuffix
400				p.Stale = true
401				p.StaleReason = "build -o flag in use"
402				a.Deps = append(a.Deps, b.AutoAction(ModeInstall, depMode, p))
403			}
404			if len(a.Deps) == 0 {
405				base.Fatalf("go build: no main packages to build")
406			}
407			b.Do(a)
408			return
409		}
410		if len(pkgs) > 1 {
411			base.Fatalf("go build: cannot write multiple packages to non-directory %s", cfg.BuildO)
412		} else if len(pkgs) == 0 {
413			base.Fatalf("no packages to build")
414		}
415		p := pkgs[0]
416		p.Target = cfg.BuildO
417		p.Stale = true // must build - not up to date
418		p.StaleReason = "build -o flag in use"
419		a := b.AutoAction(ModeInstall, depMode, p)
420		b.Do(a)
421		return
422	}
423
424	a := &Action{Mode: "go build"}
425	for _, p := range pkgs {
426		a.Deps = append(a.Deps, b.AutoAction(ModeBuild, depMode, p))
427	}
428	if cfg.BuildBuildmode == "shared" {
429		a = b.buildmodeShared(ModeBuild, depMode, args, pkgs, a)
430	}
431	b.Do(a)
432}
433
434var CmdInstall = &base.Command{
435	UsageLine: "go install [-i] [build flags] [packages]",
436	Short:     "compile and install packages and dependencies",
437	Long: `
438Install compiles and installs the packages named by the import paths.
439
440Executables are installed in the directory named by the GOBIN environment
441variable, which defaults to $GOPATH/bin or $HOME/go/bin if the GOPATH
442environment variable is not set. Executables in $GOROOT
443are installed in $GOROOT/bin or $GOTOOLDIR instead of $GOBIN.
444
445When module-aware mode is disabled, other packages are installed in the
446directory $GOPATH/pkg/$GOOS_$GOARCH. When module-aware mode is enabled,
447other packages are built and cached but not installed.
448
449The -i flag installs the dependencies of the named packages as well.
450
451For more about the build flags, see 'go help build'.
452For more about specifying packages, see 'go help packages'.
453
454See also: go build, go get, go clean.
455	`,
456}
457
458// libname returns the filename to use for the shared library when using
459// -buildmode=shared. The rules we use are:
460// Use arguments for special 'meta' packages:
461//	std --> libstd.so
462//	std cmd --> libstd,cmd.so
463// A single non-meta argument with trailing "/..." is special cased:
464//	foo/... --> libfoo.so
465//	(A relative path like "./..."  expands the "." first)
466// Use import paths for other cases, changing '/' to '-':
467//	somelib --> libsubdir-somelib.so
468//	./ or ../ --> libsubdir-somelib.so
469//	gopkg.in/tomb.v2 -> libgopkg.in-tomb.v2.so
470//	a/... b/... ---> liba/c,b/d.so - all matching import paths
471// Name parts are joined with ','.
472func libname(args []string, pkgs []*load.Package) (string, error) {
473	var libname string
474	appendName := func(arg string) {
475		if libname == "" {
476			libname = arg
477		} else {
478			libname += "," + arg
479		}
480	}
481	var haveNonMeta bool
482	for _, arg := range args {
483		if search.IsMetaPackage(arg) {
484			appendName(arg)
485		} else {
486			haveNonMeta = true
487		}
488	}
489	if len(libname) == 0 { // non-meta packages only. use import paths
490		if len(args) == 1 && strings.HasSuffix(args[0], "/...") {
491			// Special case of "foo/..." as mentioned above.
492			arg := strings.TrimSuffix(args[0], "/...")
493			if build.IsLocalImport(arg) {
494				cwd, _ := os.Getwd()
495				bp, _ := cfg.BuildContext.ImportDir(filepath.Join(cwd, arg), build.FindOnly)
496				if bp.ImportPath != "" && bp.ImportPath != "." {
497					arg = bp.ImportPath
498				}
499			}
500			appendName(strings.ReplaceAll(arg, "/", "-"))
501		} else {
502			for _, pkg := range pkgs {
503				appendName(strings.ReplaceAll(pkg.ImportPath, "/", "-"))
504			}
505		}
506	} else if haveNonMeta { // have both meta package and a non-meta one
507		return "", errors.New("mixing of meta and non-meta packages is not allowed")
508	}
509	// TODO(mwhudson): Needs to change for platforms that use different naming
510	// conventions...
511	return "lib" + libname + ".so", nil
512}
513
514func runInstall(cmd *base.Command, args []string) {
515	BuildInit()
516	InstallPackages(args, load.PackagesForBuild(args))
517}
518
519// omitTestOnly returns pkgs with test-only packages removed.
520func omitTestOnly(pkgs []*load.Package) []*load.Package {
521	var list []*load.Package
522	for _, p := range pkgs {
523		if len(p.GoFiles)+len(p.CgoFiles) == 0 && !p.Internal.CmdlinePkgLiteral {
524			// Package has no source files,
525			// perhaps due to build tags or perhaps due to only having *_test.go files.
526			// Also, it is only being processed as the result of a wildcard match
527			// like ./..., not because it was listed as a literal path on the command line.
528			// Ignore it.
529			continue
530		}
531		list = append(list, p)
532	}
533	return list
534}
535
536func InstallPackages(patterns []string, pkgs []*load.Package) {
537	if cfg.GOBIN != "" && !filepath.IsAbs(cfg.GOBIN) {
538		base.Fatalf("cannot install, GOBIN must be an absolute path")
539	}
540
541	pkgs = omitTestOnly(pkgsFilter(pkgs))
542	for _, p := range pkgs {
543		if p.Target == "" {
544			switch {
545			case p.Standard && p.ImportPath == "unsafe":
546				// unsafe is a built-in package, has no target
547			case p.Name != "main" && p.Internal.Local && p.ConflictDir == "":
548				// Non-executables outside GOPATH need not have a target:
549				// we can use the cache to hold the built package archive for use in future builds.
550				// The ones inside GOPATH should have a target (in GOPATH/pkg)
551				// or else something is wrong and worth reporting (like a ConflictDir).
552			case p.Name != "main" && p.Module != nil:
553				// Non-executables have no target (except the cache) when building with modules.
554			case p.Internal.GobinSubdir:
555				base.Errorf("go %s: cannot install cross-compiled binaries when GOBIN is set", cfg.CmdName)
556			case p.Internal.CmdlineFiles:
557				base.Errorf("go %s: no install location for .go files listed on command line (GOBIN not set)", cfg.CmdName)
558			case p.ConflictDir != "":
559				base.Errorf("go %s: no install location for %s: hidden by %s", cfg.CmdName, p.Dir, p.ConflictDir)
560			default:
561				base.Errorf("go %s: no install location for directory %s outside GOPATH\n"+
562					"\tFor more details see: 'go help gopath'", cfg.CmdName, p.Dir)
563			}
564		}
565	}
566	base.ExitIfErrors()
567
568	var b Builder
569	b.Init()
570	depMode := ModeBuild
571	if cfg.BuildI {
572		depMode = ModeInstall
573	}
574	a := &Action{Mode: "go install"}
575	var tools []*Action
576	for _, p := range pkgs {
577		// If p is a tool, delay the installation until the end of the build.
578		// This avoids installing assemblers/compilers that are being executed
579		// by other steps in the build.
580		a1 := b.AutoAction(ModeInstall, depMode, p)
581		if load.InstallTargetDir(p) == load.ToTool {
582			a.Deps = append(a.Deps, a1.Deps...)
583			a1.Deps = append(a1.Deps, a)
584			tools = append(tools, a1)
585			continue
586		}
587		a.Deps = append(a.Deps, a1)
588	}
589	if len(tools) > 0 {
590		a = &Action{
591			Mode: "go install (tools)",
592			Deps: tools,
593		}
594	}
595
596	if cfg.BuildBuildmode == "shared" {
597		// Note: If buildmode=shared then only non-main packages
598		// are present in the pkgs list, so all the special case code about
599		// tools above did not apply, and a is just a simple Action
600		// with a list of Deps, one per package named in pkgs,
601		// the same as in runBuild.
602		a = b.buildmodeShared(ModeInstall, ModeInstall, patterns, pkgs, a)
603	}
604
605	b.Do(a)
606	base.ExitIfErrors()
607
608	// Success. If this command is 'go install' with no arguments
609	// and the current directory (the implicit argument) is a command,
610	// remove any leftover command binary from a previous 'go build'.
611	// The binary is installed; it's not needed here anymore.
612	// And worse it might be a stale copy, which you don't want to find
613	// instead of the installed one if $PATH contains dot.
614	// One way to view this behavior is that it is as if 'go install' first
615	// runs 'go build' and the moves the generated file to the install dir.
616	// See issue 9645.
617	if len(patterns) == 0 && len(pkgs) == 1 && pkgs[0].Name == "main" {
618		// Compute file 'go build' would have created.
619		// If it exists and is an executable file, remove it.
620		targ := pkgs[0].DefaultExecName()
621		targ += cfg.ExeSuffix
622		if filepath.Join(pkgs[0].Dir, targ) != pkgs[0].Target { // maybe $GOBIN is the current directory
623			fi, err := os.Stat(targ)
624			if err == nil {
625				m := fi.Mode()
626				if m.IsRegular() {
627					if m&0111 != 0 || cfg.Goos == "windows" { // windows never sets executable bit
628						os.Remove(targ)
629					}
630				}
631			}
632		}
633	}
634}
635
636// ExecCmd is the command to use to run user binaries.
637// Normally it is empty, meaning run the binaries directly.
638// If cross-compiling and running on a remote system or
639// simulator, it is typically go_GOOS_GOARCH_exec, with
640// the target GOOS and GOARCH substituted.
641// The -exec flag overrides these defaults.
642var ExecCmd []string
643
644// FindExecCmd derives the value of ExecCmd to use.
645// It returns that value and leaves ExecCmd set for direct use.
646func FindExecCmd() []string {
647	if ExecCmd != nil {
648		return ExecCmd
649	}
650	ExecCmd = []string{} // avoid work the second time
651	if cfg.Goos == runtime.GOOS && cfg.Goarch == runtime.GOARCH {
652		return ExecCmd
653	}
654	path, err := exec.LookPath(fmt.Sprintf("go_%s_%s_exec", cfg.Goos, cfg.Goarch))
655	if err == nil {
656		ExecCmd = []string{path}
657	}
658	return ExecCmd
659}
660