...

Source file src/pkg/cmd/go/internal/work/gc.go

     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	
     5	package work
     6	
     7	import (
     8		"bufio"
     9		"bytes"
    10		"fmt"
    11		"io"
    12		"io/ioutil"
    13		"log"
    14		"os"
    15		"path/filepath"
    16		"runtime"
    17		"strings"
    18	
    19		"cmd/go/internal/base"
    20		"cmd/go/internal/cfg"
    21		"cmd/go/internal/load"
    22		"cmd/go/internal/str"
    23		"cmd/internal/objabi"
    24		"cmd/internal/sys"
    25		"crypto/sha1"
    26	)
    27	
    28	// The Go toolchain.
    29	
    30	type gcToolchain struct{}
    31	
    32	func (gcToolchain) compiler() string {
    33		return base.Tool("compile")
    34	}
    35	
    36	func (gcToolchain) linker() string {
    37		return base.Tool("link")
    38	}
    39	
    40	func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
    41		p := a.Package
    42		objdir := a.Objdir
    43		if archive != "" {
    44			ofile = archive
    45		} else {
    46			out := "_go_.o"
    47			ofile = objdir + out
    48		}
    49	
    50		pkgpath := p.ImportPath
    51		if cfg.BuildBuildmode == "plugin" {
    52			pkgpath = pluginPath(a)
    53		} else if p.Name == "main" && !p.Internal.ForceLibrary {
    54			pkgpath = "main"
    55		}
    56		gcargs := []string{"-p", pkgpath}
    57		if p.Module != nil && p.Module.GoVersion != "" && allowedVersion(p.Module.GoVersion) {
    58			gcargs = append(gcargs, "-lang=go"+p.Module.GoVersion)
    59		}
    60		if p.Standard {
    61			gcargs = append(gcargs, "-std")
    62		}
    63		compilingRuntime := p.Standard && (p.ImportPath == "runtime" || strings.HasPrefix(p.ImportPath, "runtime/internal"))
    64		// The runtime package imports a couple of general internal packages.
    65		if p.Standard && (p.ImportPath == "internal/cpu" || p.ImportPath == "internal/bytealg") {
    66			compilingRuntime = true
    67		}
    68		if compilingRuntime {
    69			// runtime compiles with a special gc flag to check for
    70			// memory allocations that are invalid in the runtime package,
    71			// and to implement some special compiler pragmas.
    72			gcargs = append(gcargs, "-+")
    73		}
    74	
    75		// If we're giving the compiler the entire package (no C etc files), tell it that,
    76		// so that it can give good error messages about forward declarations.
    77		// Exceptions: a few standard packages have forward declarations for
    78		// pieces supplied behind-the-scenes by package runtime.
    79		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)
    80		if p.Standard {
    81			switch p.ImportPath {
    82			case "bytes", "internal/poll", "net", "os", "runtime/pprof", "runtime/trace", "sync", "syscall", "time":
    83				extFiles++
    84			}
    85		}
    86		if extFiles == 0 {
    87			gcargs = append(gcargs, "-complete")
    88		}
    89		if cfg.BuildContext.InstallSuffix != "" {
    90			gcargs = append(gcargs, "-installsuffix", cfg.BuildContext.InstallSuffix)
    91		}
    92		if a.buildID != "" {
    93			gcargs = append(gcargs, "-buildid", a.buildID)
    94		}
    95		platform := cfg.Goos + "/" + cfg.Goarch
    96		if p.Internal.OmitDebug || platform == "nacl/amd64p32" || cfg.Goos == "plan9" || cfg.Goarch == "wasm" {
    97			gcargs = append(gcargs, "-dwarf=false")
    98		}
    99		if strings.HasPrefix(runtimeVersion, "go1") && !strings.Contains(os.Args[0], "go_bootstrap") {
   100			gcargs = append(gcargs, "-goversion", runtimeVersion)
   101		}
   102		if symabis != "" {
   103			gcargs = append(gcargs, "-symabis", symabis)
   104		}
   105	
   106		gcflags := str.StringList(forcedGcflags, p.Internal.Gcflags)
   107		if compilingRuntime {
   108			// Remove -N, if present.
   109			// It is not possible to build the runtime with no optimizations,
   110			// because the compiler cannot eliminate enough write barriers.
   111			for i := 0; i < len(gcflags); i++ {
   112				if gcflags[i] == "-N" {
   113					copy(gcflags[i:], gcflags[i+1:])
   114					gcflags = gcflags[:len(gcflags)-1]
   115					i--
   116				}
   117			}
   118		}
   119	
   120		args := []interface{}{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), gcflags, gcargs, "-D", p.Internal.LocalPrefix}
   121		if importcfg != nil {
   122			if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
   123				return "", nil, err
   124			}
   125			args = append(args, "-importcfg", objdir+"importcfg")
   126		}
   127		if ofile == archive {
   128			args = append(args, "-pack")
   129		}
   130		if asmhdr {
   131			args = append(args, "-asmhdr", objdir+"go_asm.h")
   132		}
   133	
   134		// Add -c=N to use concurrent backend compilation, if possible.
   135		if c := gcBackendConcurrency(gcflags); c > 1 {
   136			args = append(args, fmt.Sprintf("-c=%d", c))
   137		}
   138	
   139		for _, f := range gofiles {
   140			args = append(args, mkAbs(p.Dir, f))
   141		}
   142	
   143		output, err = b.runOut(a, p.Dir, nil, args...)
   144		return ofile, output, err
   145	}
   146	
   147	// gcBackendConcurrency returns the backend compiler concurrency level for a package compilation.
   148	func gcBackendConcurrency(gcflags []string) int {
   149		// First, check whether we can use -c at all for this compilation.
   150		canDashC := concurrentGCBackendCompilationEnabledByDefault
   151	
   152		switch e := os.Getenv("GO19CONCURRENTCOMPILATION"); e {
   153		case "0":
   154			canDashC = false
   155		case "1":
   156			canDashC = true
   157		case "":
   158			// Not set. Use default.
   159		default:
   160			log.Fatalf("GO19CONCURRENTCOMPILATION must be 0, 1, or unset, got %q", e)
   161		}
   162	
   163	CheckFlags:
   164		for _, flag := range gcflags {
   165			// Concurrent compilation is presumed incompatible with any gcflags,
   166			// except for a small whitelist of commonly used flags.
   167			// If the user knows better, they can manually add their own -c to the gcflags.
   168			switch flag {
   169			case "-N", "-l", "-S", "-B", "-C", "-I":
   170				// OK
   171			default:
   172				canDashC = false
   173				break CheckFlags
   174			}
   175		}
   176	
   177		// TODO: Test and delete these conditions.
   178		if objabi.Fieldtrack_enabled != 0 || objabi.Preemptibleloops_enabled != 0 {
   179			canDashC = false
   180		}
   181	
   182		if !canDashC {
   183			return 1
   184		}
   185	
   186		// Decide how many concurrent backend compilations to allow.
   187		//
   188		// If we allow too many, in theory we might end up with p concurrent processes,
   189		// each with c concurrent backend compiles, all fighting over the same resources.
   190		// However, in practice, that seems not to happen too much.
   191		// Most build graphs are surprisingly serial, so p==1 for much of the build.
   192		// Furthermore, concurrent backend compilation is only enabled for a part
   193		// of the overall compiler execution, so c==1 for much of the build.
   194		// So don't worry too much about that interaction for now.
   195		//
   196		// However, in practice, setting c above 4 tends not to help very much.
   197		// See the analysis in CL 41192.
   198		//
   199		// TODO(josharian): attempt to detect whether this particular compilation
   200		// is likely to be a bottleneck, e.g. when:
   201		//   - it has no successor packages to compile (usually package main)
   202		//   - all paths through the build graph pass through it
   203		//   - critical path scheduling says it is high priority
   204		// and in such a case, set c to runtime.NumCPU.
   205		// We do this now when p==1.
   206		if cfg.BuildP == 1 {
   207			// No process parallelism. Max out c.
   208			return runtime.NumCPU()
   209		}
   210		// Some process parallelism. Set c to min(4, numcpu).
   211		c := 4
   212		if ncpu := runtime.NumCPU(); ncpu < c {
   213			c = ncpu
   214		}
   215		return c
   216	}
   217	
   218	// trimpath returns the -trimpath argument to use
   219	// when compiling the action.
   220	func (a *Action) trimpath() string {
   221		// Strip the object directory entirely.
   222		objdir := a.Objdir
   223		if len(objdir) > 1 && objdir[len(objdir)-1] == filepath.Separator {
   224			objdir = objdir[:len(objdir)-1]
   225		}
   226		rewrite := objdir + "=>"
   227	
   228		// For "go build -trimpath", rewrite package source directory
   229		// to a file system-independent path (just the import path).
   230		if cfg.BuildTrimpath {
   231			if m := a.Package.Module; m != nil {
   232				rewrite += ";" + m.Dir + "=>" + m.Path + "@" + m.Version
   233			} else {
   234				rewrite += ";" + a.Package.Dir + "=>" + a.Package.ImportPath
   235			}
   236		}
   237	
   238		return rewrite
   239	}
   240	
   241	func asmArgs(a *Action, p *load.Package) []interface{} {
   242		// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
   243		inc := filepath.Join(cfg.GOROOT, "pkg", "include")
   244		args := []interface{}{cfg.BuildToolexec, base.Tool("asm"), "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags}
   245		if p.ImportPath == "runtime" && cfg.Goarch == "386" {
   246			for _, arg := range forcedAsmflags {
   247				if arg == "-dynlink" {
   248					args = append(args, "-D=GOBUILDMODE_shared=1")
   249				}
   250			}
   251		}
   252	
   253		if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" {
   254			// Define GOMIPS_value from cfg.GOMIPS.
   255			args = append(args, "-D", "GOMIPS_"+cfg.GOMIPS)
   256		}
   257	
   258		if cfg.Goarch == "mips64" || cfg.Goarch == "mips64le" {
   259			// Define GOMIPS64_value from cfg.GOMIPS64.
   260			args = append(args, "-D", "GOMIPS64_"+cfg.GOMIPS64)
   261		}
   262	
   263		return args
   264	}
   265	
   266	func (gcToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
   267		p := a.Package
   268		args := asmArgs(a, p)
   269	
   270		var ofiles []string
   271		for _, sfile := range sfiles {
   272			ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o"
   273			ofiles = append(ofiles, ofile)
   274			args1 := append(args, "-o", ofile, mkAbs(p.Dir, sfile))
   275			if err := b.run(a, p.Dir, p.ImportPath, nil, args1...); err != nil {
   276				return nil, err
   277			}
   278		}
   279		return ofiles, nil
   280	}
   281	
   282	func (gcToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) {
   283		mkSymabis := func(p *load.Package, sfiles []string, path string) error {
   284			args := asmArgs(a, p)
   285			args = append(args, "-gensymabis", "-o", path)
   286			for _, sfile := range sfiles {
   287				if p.ImportPath == "runtime/cgo" && strings.HasPrefix(sfile, "gcc_") {
   288					continue
   289				}
   290				args = append(args, mkAbs(p.Dir, sfile))
   291			}
   292	
   293			// Supply an empty go_asm.h as if the compiler had been run.
   294			// -gensymabis parsing is lax enough that we don't need the
   295			// actual definitions that would appear in go_asm.h.
   296			if err := b.writeFile(a.Objdir+"go_asm.h", nil); err != nil {
   297				return err
   298			}
   299	
   300			return b.run(a, p.Dir, p.ImportPath, nil, args...)
   301		}
   302	
   303		var symabis string // Only set if we actually create the file
   304		p := a.Package
   305		if len(sfiles) != 0 {
   306			symabis = a.Objdir + "symabis"
   307			if err := mkSymabis(p, sfiles, symabis); err != nil {
   308				return "", err
   309			}
   310		}
   311	
   312		return symabis, nil
   313	}
   314	
   315	// toolVerify checks that the command line args writes the same output file
   316	// if run using newTool instead.
   317	// Unused now but kept around for future use.
   318	func toolVerify(a *Action, b *Builder, p *load.Package, newTool string, ofile string, args []interface{}) error {
   319		newArgs := make([]interface{}, len(args))
   320		copy(newArgs, args)
   321		newArgs[1] = base.Tool(newTool)
   322		newArgs[3] = ofile + ".new" // x.6 becomes x.6.new
   323		if err := b.run(a, p.Dir, p.ImportPath, nil, newArgs...); err != nil {
   324			return err
   325		}
   326		data1, err := ioutil.ReadFile(ofile)
   327		if err != nil {
   328			return err
   329		}
   330		data2, err := ioutil.ReadFile(ofile + ".new")
   331		if err != nil {
   332			return err
   333		}
   334		if !bytes.Equal(data1, data2) {
   335			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...), " "))
   336		}
   337		os.Remove(ofile + ".new")
   338		return nil
   339	}
   340	
   341	func (gcToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
   342		var absOfiles []string
   343		for _, f := range ofiles {
   344			absOfiles = append(absOfiles, mkAbs(a.Objdir, f))
   345		}
   346		absAfile := mkAbs(a.Objdir, afile)
   347	
   348		// The archive file should have been created by the compiler.
   349		// Since it used to not work that way, verify.
   350		if !cfg.BuildN {
   351			if _, err := os.Stat(absAfile); err != nil {
   352				base.Fatalf("os.Stat of archive file failed: %v", err)
   353			}
   354		}
   355	
   356		p := a.Package
   357		if cfg.BuildN || cfg.BuildX {
   358			cmdline := str.StringList(base.Tool("pack"), "r", absAfile, absOfiles)
   359			b.Showcmd(p.Dir, "%s # internal", joinUnambiguously(cmdline))
   360		}
   361		if cfg.BuildN {
   362			return nil
   363		}
   364		if err := packInternal(absAfile, absOfiles); err != nil {
   365			b.showOutput(a, p.Dir, p.Desc(), err.Error()+"\n")
   366			return errPrintedOutput
   367		}
   368		return nil
   369	}
   370	
   371	func packInternal(afile string, ofiles []string) error {
   372		dst, err := os.OpenFile(afile, os.O_WRONLY|os.O_APPEND, 0)
   373		if err != nil {
   374			return err
   375		}
   376		defer dst.Close() // only for error returns or panics
   377		w := bufio.NewWriter(dst)
   378	
   379		for _, ofile := range ofiles {
   380			src, err := os.Open(ofile)
   381			if err != nil {
   382				return err
   383			}
   384			fi, err := src.Stat()
   385			if err != nil {
   386				src.Close()
   387				return err
   388			}
   389			// Note: Not using %-16.16s format because we care
   390			// about bytes, not runes.
   391			name := fi.Name()
   392			if len(name) > 16 {
   393				name = name[:16]
   394			} else {
   395				name += strings.Repeat(" ", 16-len(name))
   396			}
   397			size := fi.Size()
   398			fmt.Fprintf(w, "%s%-12d%-6d%-6d%-8o%-10d`\n",
   399				name, 0, 0, 0, 0644, size)
   400			n, err := io.Copy(w, src)
   401			src.Close()
   402			if err == nil && n < size {
   403				err = io.ErrUnexpectedEOF
   404			} else if err == nil && n > size {
   405				err = fmt.Errorf("file larger than size reported by stat")
   406			}
   407			if err != nil {
   408				return fmt.Errorf("copying %s to %s: %v", ofile, afile, err)
   409			}
   410			if size&1 != 0 {
   411				w.WriteByte(0)
   412			}
   413		}
   414	
   415		if err := w.Flush(); err != nil {
   416			return err
   417		}
   418		return dst.Close()
   419	}
   420	
   421	// setextld sets the appropriate linker flags for the specified compiler.
   422	func setextld(ldflags []string, compiler []string) []string {
   423		for _, f := range ldflags {
   424			if f == "-extld" || strings.HasPrefix(f, "-extld=") {
   425				// don't override -extld if supplied
   426				return ldflags
   427			}
   428		}
   429		ldflags = append(ldflags, "-extld="+compiler[0])
   430		if len(compiler) > 1 {
   431			extldflags := false
   432			add := strings.Join(compiler[1:], " ")
   433			for i, f := range ldflags {
   434				if f == "-extldflags" && i+1 < len(ldflags) {
   435					ldflags[i+1] = add + " " + ldflags[i+1]
   436					extldflags = true
   437					break
   438				} else if strings.HasPrefix(f, "-extldflags=") {
   439					ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):]
   440					extldflags = true
   441					break
   442				}
   443			}
   444			if !extldflags {
   445				ldflags = append(ldflags, "-extldflags="+add)
   446			}
   447		}
   448		return ldflags
   449	}
   450	
   451	// pluginPath computes the package path for a plugin main package.
   452	//
   453	// This is typically the import path of the main package p, unless the
   454	// plugin is being built directly from source files. In that case we
   455	// combine the package build ID with the contents of the main package
   456	// source files. This allows us to identify two different plugins
   457	// built from two source files with the same name.
   458	func pluginPath(a *Action) string {
   459		p := a.Package
   460		if p.ImportPath != "command-line-arguments" {
   461			return p.ImportPath
   462		}
   463		h := sha1.New()
   464		buildID := a.buildID
   465		if a.Mode == "link" {
   466			// For linking, use the main package's build ID instead of
   467			// the binary's build ID, so it is the same hash used in
   468			// compiling and linking.
   469			// When compiling, we use actionID/actionID (instead of
   470			// actionID/contentID) as a temporary build ID to compute
   471			// the hash. Do the same here. (See buildid.go:useCache)
   472			// The build ID matters because it affects the overall hash
   473			// in the plugin's pseudo-import path returned below.
   474			// We need to use the same import path when compiling and linking.
   475			id := strings.Split(buildID, buildIDSeparator)
   476			buildID = id[1] + buildIDSeparator + id[1]
   477		}
   478		fmt.Fprintf(h, "build ID: %s\n", buildID)
   479		for _, file := range str.StringList(p.GoFiles, p.CgoFiles, p.SFiles) {
   480			data, err := ioutil.ReadFile(filepath.Join(p.Dir, file))
   481			if err != nil {
   482				base.Fatalf("go: %s", err)
   483			}
   484			h.Write(data)
   485		}
   486		return fmt.Sprintf("plugin/unnamed-%x", h.Sum(nil))
   487	}
   488	
   489	func (gcToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error {
   490		cxx := len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
   491		for _, a := range root.Deps {
   492			if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) {
   493				cxx = true
   494			}
   495		}
   496		var ldflags []string
   497		if cfg.BuildContext.InstallSuffix != "" {
   498			ldflags = append(ldflags, "-installsuffix", cfg.BuildContext.InstallSuffix)
   499		}
   500		if root.Package.Internal.OmitDebug {
   501			ldflags = append(ldflags, "-s", "-w")
   502		}
   503		if cfg.BuildBuildmode == "plugin" {
   504			ldflags = append(ldflags, "-pluginpath", pluginPath(root))
   505		}
   506	
   507		// Store BuildID inside toolchain binaries as a unique identifier of the
   508		// tool being run, for use by content-based staleness determination.
   509		if root.Package.Goroot && strings.HasPrefix(root.Package.ImportPath, "cmd/") {
   510			// External linking will include our build id in the external
   511			// linker's build id, which will cause our build id to not
   512			// match the next time the tool is built.
   513			// Rely on the external build id instead.
   514			if !sys.MustLinkExternal(cfg.Goos, cfg.Goarch) {
   515				ldflags = append(ldflags, "-X=cmd/internal/objabi.buildID="+root.buildID)
   516			}
   517		}
   518	
   519		// If the user has not specified the -extld option, then specify the
   520		// appropriate linker. In case of C++ code, use the compiler named
   521		// by the CXX environment variable or defaultCXX if CXX is not set.
   522		// Else, use the CC environment variable and defaultCC as fallback.
   523		var compiler []string
   524		if cxx {
   525			compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
   526		} else {
   527			compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
   528		}
   529		ldflags = append(ldflags, "-buildmode="+ldBuildmode)
   530		if root.buildID != "" {
   531			ldflags = append(ldflags, "-buildid="+root.buildID)
   532		}
   533		ldflags = append(ldflags, forcedLdflags...)
   534		ldflags = append(ldflags, root.Package.Internal.Ldflags...)
   535		ldflags = setextld(ldflags, compiler)
   536	
   537		// On OS X when using external linking to build a shared library,
   538		// the argument passed here to -o ends up recorded in the final
   539		// shared library in the LC_ID_DYLIB load command.
   540		// To avoid putting the temporary output directory name there
   541		// (and making the resulting shared library useless),
   542		// run the link in the output directory so that -o can name
   543		// just the final path element.
   544		// On Windows, DLL file name is recorded in PE file
   545		// export section, so do like on OS X.
   546		dir := "."
   547		if (cfg.Goos == "darwin" || cfg.Goos == "windows") && cfg.BuildBuildmode == "c-shared" {
   548			dir, out = filepath.Split(out)
   549		}
   550	
   551		env := []string{}
   552		if cfg.BuildTrimpath {
   553			env = append(env, "GOROOT_FINAL=go")
   554		}
   555		return b.run(root, dir, root.Package.ImportPath, env, cfg.BuildToolexec, base.Tool("link"), "-o", out, "-importcfg", importcfg, ldflags, mainpkg)
   556	}
   557	
   558	func (gcToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, out, importcfg string, allactions []*Action) error {
   559		ldflags := []string{"-installsuffix", cfg.BuildContext.InstallSuffix}
   560		ldflags = append(ldflags, "-buildmode=shared")
   561		ldflags = append(ldflags, forcedLdflags...)
   562		ldflags = append(ldflags, root.Package.Internal.Ldflags...)
   563		cxx := false
   564		for _, a := range allactions {
   565			if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) {
   566				cxx = true
   567			}
   568		}
   569		// If the user has not specified the -extld option, then specify the
   570		// appropriate linker. In case of C++ code, use the compiler named
   571		// by the CXX environment variable or defaultCXX if CXX is not set.
   572		// Else, use the CC environment variable and defaultCC as fallback.
   573		var compiler []string
   574		if cxx {
   575			compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
   576		} else {
   577			compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
   578		}
   579		ldflags = setextld(ldflags, compiler)
   580		for _, d := range toplevelactions {
   581			if !strings.HasSuffix(d.Target, ".a") { // omit unsafe etc and actions for other shared libraries
   582				continue
   583			}
   584			ldflags = append(ldflags, d.Package.ImportPath+"="+d.Target)
   585		}
   586		return b.run(root, ".", out, nil, cfg.BuildToolexec, base.Tool("link"), "-o", out, "-importcfg", importcfg, ldflags)
   587	}
   588	
   589	func (gcToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
   590		return fmt.Errorf("%s: C source files not supported without cgo", mkAbs(a.Package.Dir, cfile))
   591	}
   592	

View as plain text