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	"bufio"
9	"bytes"
10	"fmt"
11	"internal/buildcfg"
12	"internal/platform"
13	"io"
14	"log"
15	"os"
16	"path/filepath"
17	"runtime"
18	"strings"
19
20	"cmd/go/internal/base"
21	"cmd/go/internal/cfg"
22	"cmd/go/internal/fsys"
23	"cmd/go/internal/gover"
24	"cmd/go/internal/load"
25	"cmd/go/internal/str"
26	"cmd/internal/quoted"
27	"crypto/sha1"
28)
29
30// Tests can override this by setting $TESTGO_TOOLCHAIN_VERSION.
31var ToolchainVersion = runtime.Version()
32
33// The Go toolchain.
34
35type gcToolchain struct{}
36
37func (gcToolchain) compiler() string {
38	return base.Tool("compile")
39}
40
41func (gcToolchain) linker() string {
42	return base.Tool("link")
43}
44
45func pkgPath(a *Action) string {
46	p := a.Package
47	ppath := p.ImportPath
48	if cfg.BuildBuildmode == "plugin" {
49		ppath = pluginPath(a)
50	} else if p.Name == "main" && !p.Internal.ForceLibrary {
51		ppath = "main"
52	}
53	return ppath
54}
55
56func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg []byte, symabis string, asmhdr bool, pgoProfile string, gofiles []string) (ofile string, output []byte, err error) {
57	p := a.Package
58	sh := b.Shell(a)
59	objdir := a.Objdir
60	if archive != "" {
61		ofile = archive
62	} else {
63		out := "_go_.o"
64		ofile = objdir + out
65	}
66
67	pkgpath := pkgPath(a)
68	defaultGcFlags := []string{"-p", pkgpath}
69	vers := gover.Local()
70	if p.Module != nil {
71		v := p.Module.GoVersion
72		if v == "" {
73			v = gover.DefaultGoModVersion
74		}
75		// TODO(samthanawalla): Investigate when allowedVersion is not true.
76		if allowedVersion(v) {
77			vers = v
78		}
79	}
80	defaultGcFlags = append(defaultGcFlags, "-lang=go"+gover.Lang(vers))
81	if p.Standard {
82		defaultGcFlags = append(defaultGcFlags, "-std")
83	}
84
85	// If we're giving the compiler the entire package (no C etc files), tell it that,
86	// so that it can give good error messages about forward declarations.
87	// Exceptions: a few standard packages have forward declarations for
88	// pieces supplied behind-the-scenes by package runtime.
89	extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.FFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
90	if p.Standard {
91		switch p.ImportPath {
92		case "bytes", "internal/poll", "net", "os":
93			fallthrough
94		case "runtime/metrics", "runtime/pprof", "runtime/trace":
95			fallthrough
96		case "sync", "syscall", "time":
97			extFiles++
98		}
99	}
100	if extFiles == 0 {
101		defaultGcFlags = append(defaultGcFlags, "-complete")
102	}
103	if cfg.BuildContext.InstallSuffix != "" {
104		defaultGcFlags = append(defaultGcFlags, "-installsuffix", cfg.BuildContext.InstallSuffix)
105	}
106	if a.buildID != "" {
107		defaultGcFlags = append(defaultGcFlags, "-buildid", a.buildID)
108	}
109	if p.Internal.OmitDebug || cfg.Goos == "plan9" || cfg.Goarch == "wasm" {
110		defaultGcFlags = append(defaultGcFlags, "-dwarf=false")
111	}
112	if strings.HasPrefix(ToolchainVersion, "go1") && !strings.Contains(os.Args[0], "go_bootstrap") {
113		defaultGcFlags = append(defaultGcFlags, "-goversion", ToolchainVersion)
114	}
115	if p.Internal.Cover.Cfg != "" {
116		defaultGcFlags = append(defaultGcFlags, "-coveragecfg="+p.Internal.Cover.Cfg)
117	}
118	if pgoProfile != "" {
119		defaultGcFlags = append(defaultGcFlags, "-pgoprofile="+pgoProfile)
120	}
121	if symabis != "" {
122		defaultGcFlags = append(defaultGcFlags, "-symabis", symabis)
123	}
124
125	gcflags := str.StringList(forcedGcflags, p.Internal.Gcflags)
126	if p.Internal.FuzzInstrument {
127		gcflags = append(gcflags, fuzzInstrumentFlags()...)
128	}
129	// Add -c=N to use concurrent backend compilation, if possible.
130	if c := gcBackendConcurrency(gcflags); c > 1 {
131		defaultGcFlags = append(defaultGcFlags, fmt.Sprintf("-c=%d", c))
132	}
133
134	args := []any{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), defaultGcFlags, gcflags}
135	if p.Internal.LocalPrefix == "" {
136		args = append(args, "-nolocalimports")
137	} else {
138		args = append(args, "-D", p.Internal.LocalPrefix)
139	}
140	if importcfg != nil {
141		if err := sh.writeFile(objdir+"importcfg", importcfg); err != nil {
142			return "", nil, err
143		}
144		args = append(args, "-importcfg", objdir+"importcfg")
145	}
146	if embedcfg != nil {
147		if err := sh.writeFile(objdir+"embedcfg", embedcfg); err != nil {
148			return "", nil, err
149		}
150		args = append(args, "-embedcfg", objdir+"embedcfg")
151	}
152	if ofile == archive {
153		args = append(args, "-pack")
154	}
155	if asmhdr {
156		args = append(args, "-asmhdr", objdir+"go_asm.h")
157	}
158
159	for _, f := range gofiles {
160		f := mkAbs(p.Dir, f)
161
162		// Handle overlays. Convert path names using OverlayPath
163		// so these paths can be handed directly to tools.
164		// Deleted files won't show up in when scanning directories earlier,
165		// so OverlayPath will never return "" (meaning a deleted file) here.
166		// TODO(#39958): Handle cases where the package directory
167		// doesn't exist on disk (this can happen when all the package's
168		// files are in an overlay): the code expects the package directory
169		// to exist and runs some tools in that directory.
170		// TODO(#39958): Process the overlays when the
171		// gofiles, cgofiles, cfiles, sfiles, and cxxfiles variables are
172		// created in (*Builder).build. Doing that requires rewriting the
173		// code that uses those values to expect absolute paths.
174		f, _ = fsys.OverlayPath(f)
175
176		args = append(args, f)
177	}
178
179	output, err = sh.runOut(base.Cwd(), nil, args...)
180	return ofile, output, err
181}
182
183// gcBackendConcurrency returns the backend compiler concurrency level for a package compilation.
184func gcBackendConcurrency(gcflags []string) int {
185	// First, check whether we can use -c at all for this compilation.
186	canDashC := concurrentGCBackendCompilationEnabledByDefault
187
188	switch e := os.Getenv("GO19CONCURRENTCOMPILATION"); e {
189	case "0":
190		canDashC = false
191	case "1":
192		canDashC = true
193	case "":
194		// Not set. Use default.
195	default:
196		log.Fatalf("GO19CONCURRENTCOMPILATION must be 0, 1, or unset, got %q", e)
197	}
198
199	// TODO: Test and delete these conditions.
200	if cfg.ExperimentErr != nil || cfg.Experiment.FieldTrack || cfg.Experiment.PreemptibleLoops {
201		canDashC = false
202	}
203
204	if !canDashC {
205		return 1
206	}
207
208	// Decide how many concurrent backend compilations to allow.
209	//
210	// If we allow too many, in theory we might end up with p concurrent processes,
211	// each with c concurrent backend compiles, all fighting over the same resources.
212	// However, in practice, that seems not to happen too much.
213	// Most build graphs are surprisingly serial, so p==1 for much of the build.
214	// Furthermore, concurrent backend compilation is only enabled for a part
215	// of the overall compiler execution, so c==1 for much of the build.
216	// So don't worry too much about that interaction for now.
217	//
218	// However, in practice, setting c above 4 tends not to help very much.
219	// See the analysis in CL 41192.
220	//
221	// TODO(josharian): attempt to detect whether this particular compilation
222	// is likely to be a bottleneck, e.g. when:
223	//   - it has no successor packages to compile (usually package main)
224	//   - all paths through the build graph pass through it
225	//   - critical path scheduling says it is high priority
226	// and in such a case, set c to runtime.GOMAXPROCS(0).
227	// By default this is the same as runtime.NumCPU.
228	// We do this now when p==1.
229	// To limit parallelism, set GOMAXPROCS below numCPU; this may be useful
230	// on a low-memory builder, or if a deterministic build order is required.
231	c := runtime.GOMAXPROCS(0)
232	if cfg.BuildP == 1 {
233		// No process parallelism, do not cap compiler parallelism.
234		return c
235	}
236	// Some process parallelism. Set c to min(4, maxprocs).
237	if c > 4 {
238		c = 4
239	}
240	return c
241}
242
243// trimpath returns the -trimpath argument to use
244// when compiling the action.
245func (a *Action) trimpath() string {
246	// Keep in sync with Builder.ccompile
247	// The trimmed paths are a little different, but we need to trim in the
248	// same situations.
249
250	// Strip the object directory entirely.
251	objdir := a.Objdir
252	if len(objdir) > 1 && objdir[len(objdir)-1] == filepath.Separator {
253		objdir = objdir[:len(objdir)-1]
254	}
255	rewrite := ""
256
257	rewriteDir := a.Package.Dir
258	if cfg.BuildTrimpath {
259		importPath := a.Package.Internal.OrigImportPath
260		if m := a.Package.Module; m != nil && m.Version != "" {
261			rewriteDir = m.Path + "@" + m.Version + strings.TrimPrefix(importPath, m.Path)
262		} else {
263			rewriteDir = importPath
264		}
265		rewrite += a.Package.Dir + "=>" + rewriteDir + ";"
266	}
267
268	// Add rewrites for overlays. The 'from' and 'to' paths in overlays don't need to have
269	// same basename, so go from the overlay contents file path (passed to the compiler)
270	// to the path the disk path would be rewritten to.
271
272	cgoFiles := make(map[string]bool)
273	for _, f := range a.Package.CgoFiles {
274		cgoFiles[f] = true
275	}
276
277	// TODO(matloob): Higher up in the stack, when the logic for deciding when to make copies
278	// of c/c++/m/f/hfiles is consolidated, use the same logic that Build uses to determine
279	// whether to create the copies in objdir to decide whether to rewrite objdir to the
280	// package directory here.
281	var overlayNonGoRewrites string // rewrites for non-go files
282	hasCgoOverlay := false
283	if fsys.OverlayFile != "" {
284		for _, filename := range a.Package.AllFiles() {
285			path := filename
286			if !filepath.IsAbs(path) {
287				path = filepath.Join(a.Package.Dir, path)
288			}
289			base := filepath.Base(path)
290			isGo := strings.HasSuffix(filename, ".go") || strings.HasSuffix(filename, ".s")
291			isCgo := cgoFiles[filename] || !isGo
292			overlayPath, isOverlay := fsys.OverlayPath(path)
293			if isCgo && isOverlay {
294				hasCgoOverlay = true
295			}
296			if !isCgo && isOverlay {
297				rewrite += overlayPath + "=>" + filepath.Join(rewriteDir, base) + ";"
298			} else if isCgo {
299				// Generate rewrites for non-Go files copied to files in objdir.
300				if filepath.Dir(path) == a.Package.Dir {
301					// This is a file copied to objdir.
302					overlayNonGoRewrites += filepath.Join(objdir, base) + "=>" + filepath.Join(rewriteDir, base) + ";"
303				}
304			} else {
305				// Non-overlay Go files are covered by the a.Package.Dir rewrite rule above.
306			}
307		}
308	}
309	if hasCgoOverlay {
310		rewrite += overlayNonGoRewrites
311	}
312	rewrite += objdir + "=>"
313
314	return rewrite
315}
316
317func asmArgs(a *Action, p *load.Package) []any {
318	// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
319	inc := filepath.Join(cfg.GOROOT, "pkg", "include")
320	pkgpath := pkgPath(a)
321	args := []any{cfg.BuildToolexec, base.Tool("asm"), "-p", pkgpath, "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags}
322	if p.ImportPath == "runtime" && cfg.Goarch == "386" {
323		for _, arg := range forcedAsmflags {
324			if arg == "-dynlink" {
325				args = append(args, "-D=GOBUILDMODE_shared=1")
326			}
327		}
328	}
329
330	if cfg.Goarch == "386" {
331		// Define GO386_value from cfg.GO386.
332		args = append(args, "-D", "GO386_"+cfg.GO386)
333	}
334
335	if cfg.Goarch == "amd64" {
336		// Define GOAMD64_value from cfg.GOAMD64.
337		args = append(args, "-D", "GOAMD64_"+cfg.GOAMD64)
338	}
339
340	if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" {
341		// Define GOMIPS_value from cfg.GOMIPS.
342		args = append(args, "-D", "GOMIPS_"+cfg.GOMIPS)
343	}
344
345	if cfg.Goarch == "mips64" || cfg.Goarch == "mips64le" {
346		// Define GOMIPS64_value from cfg.GOMIPS64.
347		args = append(args, "-D", "GOMIPS64_"+cfg.GOMIPS64)
348	}
349
350	if cfg.Goarch == "ppc64" || cfg.Goarch == "ppc64le" {
351		// Define GOPPC64_power8..N from cfg.PPC64.
352		// We treat each powerpc version as a superset of functionality.
353		switch cfg.GOPPC64 {
354		case "power10":
355			args = append(args, "-D", "GOPPC64_power10")
356			fallthrough
357		case "power9":
358			args = append(args, "-D", "GOPPC64_power9")
359			fallthrough
360		default: // This should always be power8.
361			args = append(args, "-D", "GOPPC64_power8")
362		}
363	}
364
365	if cfg.Goarch == "riscv64" {
366		// Define GORISCV64_value from cfg.GORISCV64.
367		args = append(args, "-D", "GORISCV64_"+cfg.GORISCV64)
368	}
369
370	if cfg.Goarch == "arm" {
371		// Define GOARM_value from cfg.GOARM, which can be either a version
372		// like "6", or a version and a FP mode, like "7,hardfloat".
373		switch {
374		case strings.Contains(cfg.GOARM, "7"):
375			args = append(args, "-D", "GOARM_7")
376			fallthrough
377		case strings.Contains(cfg.GOARM, "6"):
378			args = append(args, "-D", "GOARM_6")
379			fallthrough
380		default:
381			args = append(args, "-D", "GOARM_5")
382		}
383	}
384
385	if cfg.Goarch == "arm64" {
386		g, err := buildcfg.ParseGoarm64(cfg.GOARM64)
387		if err == nil && g.LSE {
388			args = append(args, "-D", "GOARM64_LSE")
389		}
390	}
391
392	return args
393}
394
395func (gcToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
396	p := a.Package
397	args := asmArgs(a, p)
398
399	var ofiles []string
400	for _, sfile := range sfiles {
401		overlayPath, _ := fsys.OverlayPath(mkAbs(p.Dir, sfile))
402		ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o"
403		ofiles = append(ofiles, ofile)
404		args1 := append(args, "-o", ofile, overlayPath)
405		if err := b.Shell(a).run(p.Dir, p.ImportPath, nil, args1...); err != nil {
406			return nil, err
407		}
408	}
409	return ofiles, nil
410}
411
412func (gcToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) {
413	sh := b.Shell(a)
414
415	mkSymabis := func(p *load.Package, sfiles []string, path string) error {
416		args := asmArgs(a, p)
417		args = append(args, "-gensymabis", "-o", path)
418		for _, sfile := range sfiles {
419			if p.ImportPath == "runtime/cgo" && strings.HasPrefix(sfile, "gcc_") {
420				continue
421			}
422			op, _ := fsys.OverlayPath(mkAbs(p.Dir, sfile))
423			args = append(args, op)
424		}
425
426		// Supply an empty go_asm.h as if the compiler had been run.
427		// -gensymabis parsing is lax enough that we don't need the
428		// actual definitions that would appear in go_asm.h.
429		if err := sh.writeFile(a.Objdir+"go_asm.h", nil); err != nil {
430			return err
431		}
432
433		return sh.run(p.Dir, p.ImportPath, nil, args...)
434	}
435
436	var symabis string // Only set if we actually create the file
437	p := a.Package
438	if len(sfiles) != 0 {
439		symabis = a.Objdir + "symabis"
440		if err := mkSymabis(p, sfiles, symabis); err != nil {
441			return "", err
442		}
443	}
444
445	return symabis, nil
446}
447
448// toolVerify checks that the command line args writes the same output file
449// if run using newTool instead.
450// Unused now but kept around for future use.
451func toolVerify(a *Action, b *Builder, p *load.Package, newTool string, ofile string, args []any) error {
452	newArgs := make([]any, len(args))
453	copy(newArgs, args)
454	newArgs[1] = base.Tool(newTool)
455	newArgs[3] = ofile + ".new" // x.6 becomes x.6.new
456	if err := b.Shell(a).run(p.Dir, p.ImportPath, nil, newArgs...); err != nil {
457		return err
458	}
459	data1, err := os.ReadFile(ofile)
460	if err != nil {
461		return err
462	}
463	data2, err := os.ReadFile(ofile + ".new")
464	if err != nil {
465		return err
466	}
467	if !bytes.Equal(data1, data2) {
468		return fmt.Errorf("%s and %s produced different output files:\n%s\n%s", filepath.Base(args[1].(string)), newTool, strings.Join(str.StringList(args...), " "), strings.Join(str.StringList(newArgs...), " "))
469	}
470	os.Remove(ofile + ".new")
471	return nil
472}
473
474func (gcToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
475	var absOfiles []string
476	for _, f := range ofiles {
477		absOfiles = append(absOfiles, mkAbs(a.Objdir, f))
478	}
479	absAfile := mkAbs(a.Objdir, afile)
480
481	// The archive file should have been created by the compiler.
482	// Since it used to not work that way, verify.
483	if !cfg.BuildN {
484		if _, err := os.Stat(absAfile); err != nil {
485			base.Fatalf("os.Stat of archive file failed: %v", err)
486		}
487	}
488
489	p := a.Package
490	sh := b.Shell(a)
491	if cfg.BuildN || cfg.BuildX {
492		cmdline := str.StringList(base.Tool("pack"), "r", absAfile, absOfiles)
493		sh.ShowCmd(p.Dir, "%s # internal", joinUnambiguously(cmdline))
494	}
495	if cfg.BuildN {
496		return nil
497	}
498	if err := packInternal(absAfile, absOfiles); err != nil {
499		return sh.reportCmd("", "", nil, err)
500	}
501	return nil
502}
503
504func packInternal(afile string, ofiles []string) error {
505	dst, err := os.OpenFile(afile, os.O_WRONLY|os.O_APPEND, 0)
506	if err != nil {
507		return err
508	}
509	defer dst.Close() // only for error returns or panics
510	w := bufio.NewWriter(dst)
511
512	for _, ofile := range ofiles {
513		src, err := os.Open(ofile)
514		if err != nil {
515			return err
516		}
517		fi, err := src.Stat()
518		if err != nil {
519			src.Close()
520			return err
521		}
522		// Note: Not using %-16.16s format because we care
523		// about bytes, not runes.
524		name := fi.Name()
525		if len(name) > 16 {
526			name = name[:16]
527		} else {
528			name += strings.Repeat(" ", 16-len(name))
529		}
530		size := fi.Size()
531		fmt.Fprintf(w, "%s%-12d%-6d%-6d%-8o%-10d`\n",
532			name, 0, 0, 0, 0644, size)
533		n, err := io.Copy(w, src)
534		src.Close()
535		if err == nil && n < size {
536			err = io.ErrUnexpectedEOF
537		} else if err == nil && n > size {
538			err = fmt.Errorf("file larger than size reported by stat")
539		}
540		if err != nil {
541			return fmt.Errorf("copying %s to %s: %v", ofile, afile, err)
542		}
543		if size&1 != 0 {
544			w.WriteByte(0)
545		}
546	}
547
548	if err := w.Flush(); err != nil {
549		return err
550	}
551	return dst.Close()
552}
553
554// setextld sets the appropriate linker flags for the specified compiler.
555func setextld(ldflags []string, compiler []string) ([]string, error) {
556	for _, f := range ldflags {
557		if f == "-extld" || strings.HasPrefix(f, "-extld=") {
558			// don't override -extld if supplied
559			return ldflags, nil
560		}
561	}
562	joined, err := quoted.Join(compiler)
563	if err != nil {
564		return nil, err
565	}
566	return append(ldflags, "-extld="+joined), nil
567}
568
569// pluginPath computes the package path for a plugin main package.
570//
571// This is typically the import path of the main package p, unless the
572// plugin is being built directly from source files. In that case we
573// combine the package build ID with the contents of the main package
574// source files. This allows us to identify two different plugins
575// built from two source files with the same name.
576func pluginPath(a *Action) string {
577	p := a.Package
578	if p.ImportPath != "command-line-arguments" {
579		return p.ImportPath
580	}
581	h := sha1.New()
582	buildID := a.buildID
583	if a.Mode == "link" {
584		// For linking, use the main package's build ID instead of
585		// the binary's build ID, so it is the same hash used in
586		// compiling and linking.
587		// When compiling, we use actionID/actionID (instead of
588		// actionID/contentID) as a temporary build ID to compute
589		// the hash. Do the same here. (See buildid.go:useCache)
590		// The build ID matters because it affects the overall hash
591		// in the plugin's pseudo-import path returned below.
592		// We need to use the same import path when compiling and linking.
593		id := strings.Split(buildID, buildIDSeparator)
594		buildID = id[1] + buildIDSeparator + id[1]
595	}
596	fmt.Fprintf(h, "build ID: %s\n", buildID)
597	for _, file := range str.StringList(p.GoFiles, p.CgoFiles, p.SFiles) {
598		data, err := os.ReadFile(filepath.Join(p.Dir, file))
599		if err != nil {
600			base.Fatalf("go: %s", err)
601		}
602		h.Write(data)
603	}
604	return fmt.Sprintf("plugin/unnamed-%x", h.Sum(nil))
605}
606
607func (gcToolchain) ld(b *Builder, root *Action, targetPath, importcfg, mainpkg string) error {
608	cxx := len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
609	for _, a := range root.Deps {
610		if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) {
611			cxx = true
612		}
613	}
614	var ldflags []string
615	if cfg.BuildContext.InstallSuffix != "" {
616		ldflags = append(ldflags, "-installsuffix", cfg.BuildContext.InstallSuffix)
617	}
618	if root.Package.Internal.OmitDebug {
619		ldflags = append(ldflags, "-s", "-w")
620	}
621	if cfg.BuildBuildmode == "plugin" {
622		ldflags = append(ldflags, "-pluginpath", pluginPath(root))
623	}
624
625	// Store BuildID inside toolchain binaries as a unique identifier of the
626	// tool being run, for use by content-based staleness determination.
627	if root.Package.Goroot && strings.HasPrefix(root.Package.ImportPath, "cmd/") {
628		// External linking will include our build id in the external
629		// linker's build id, which will cause our build id to not
630		// match the next time the tool is built.
631		// Rely on the external build id instead.
632		if !platform.MustLinkExternal(cfg.Goos, cfg.Goarch, false) {
633			ldflags = append(ldflags, "-X=cmd/internal/objabi.buildID="+root.buildID)
634		}
635	}
636
637	// Store default GODEBUG in binaries.
638	if root.Package.DefaultGODEBUG != "" {
639		ldflags = append(ldflags, "-X=runtime.godebugDefault="+root.Package.DefaultGODEBUG)
640	}
641
642	// If the user has not specified the -extld option, then specify the
643	// appropriate linker. In case of C++ code, use the compiler named
644	// by the CXX environment variable or defaultCXX if CXX is not set.
645	// Else, use the CC environment variable and defaultCC as fallback.
646	var compiler []string
647	if cxx {
648		compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
649	} else {
650		compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
651	}
652	ldflags = append(ldflags, "-buildmode="+ldBuildmode)
653	if root.buildID != "" {
654		ldflags = append(ldflags, "-buildid="+root.buildID)
655	}
656	ldflags = append(ldflags, forcedLdflags...)
657	ldflags = append(ldflags, root.Package.Internal.Ldflags...)
658	ldflags, err := setextld(ldflags, compiler)
659	if err != nil {
660		return err
661	}
662
663	// On OS X when using external linking to build a shared library,
664	// the argument passed here to -o ends up recorded in the final
665	// shared library in the LC_ID_DYLIB load command.
666	// To avoid putting the temporary output directory name there
667	// (and making the resulting shared library useless),
668	// run the link in the output directory so that -o can name
669	// just the final path element.
670	// On Windows, DLL file name is recorded in PE file
671	// export section, so do like on OS X.
672	// On Linux, for a shared object, at least with the Gold linker,
673	// the output file path is recorded in the .gnu.version_d section.
674	dir := "."
675	if cfg.BuildBuildmode == "c-shared" || cfg.BuildBuildmode == "plugin" {
676		dir, targetPath = filepath.Split(targetPath)
677	}
678
679	env := []string{}
680	// When -trimpath is used, GOROOT is cleared
681	if cfg.BuildTrimpath {
682		env = append(env, "GOROOT=")
683	} else {
684		env = append(env, "GOROOT="+cfg.GOROOT)
685	}
686	return b.Shell(root).run(dir, root.Package.ImportPath, env, cfg.BuildToolexec, base.Tool("link"), "-o", targetPath, "-importcfg", importcfg, ldflags, mainpkg)
687}
688
689func (gcToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, targetPath, importcfg string, allactions []*Action) error {
690	ldflags := []string{"-installsuffix", cfg.BuildContext.InstallSuffix}
691	ldflags = append(ldflags, "-buildmode=shared")
692	ldflags = append(ldflags, forcedLdflags...)
693	ldflags = append(ldflags, root.Package.Internal.Ldflags...)
694	cxx := false
695	for _, a := range allactions {
696		if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) {
697			cxx = true
698		}
699	}
700	// If the user has not specified the -extld option, then specify the
701	// appropriate linker. In case of C++ code, use the compiler named
702	// by the CXX environment variable or defaultCXX if CXX is not set.
703	// Else, use the CC environment variable and defaultCC as fallback.
704	var compiler []string
705	if cxx {
706		compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
707	} else {
708		compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
709	}
710	ldflags, err := setextld(ldflags, compiler)
711	if err != nil {
712		return err
713	}
714	for _, d := range toplevelactions {
715		if !strings.HasSuffix(d.Target, ".a") { // omit unsafe etc and actions for other shared libraries
716			continue
717		}
718		ldflags = append(ldflags, d.Package.ImportPath+"="+d.Target)
719	}
720	return b.Shell(root).run(".", targetPath, nil, cfg.BuildToolexec, base.Tool("link"), "-o", targetPath, "-importcfg", importcfg, ldflags)
721}
722
723func (gcToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
724	return fmt.Errorf("%s: C source files not supported without cgo", mkAbs(a.Package.Dir, cfile))
725}
726