Empty interface; prefer specific types or generics for type safety
// TODO(rsc): Change the second key from *load.Package to interface{},
1// Copyright 2011 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45// Action graph creation (planning).67package work89import (10 "bufio"11 "bytes"12 "cmd/internal/par"13 "container/heap"14 "context"15 "debug/elf"16 "encoding/json"17 "fmt"18 "internal/platform"19 "os"20 "path/filepath"21 "slices"22 "strings"23 "sync"24 "time"2526 "cmd/go/internal/base"27 "cmd/go/internal/cache"28 "cmd/go/internal/cfg"29 "cmd/go/internal/load"30 "cmd/go/internal/modload"31 "cmd/go/internal/str"32 "cmd/go/internal/trace"33 "cmd/internal/buildid"34 "cmd/internal/robustio"35)3637// A Builder holds global state about a build.38// It does not hold per-package state, because we39// build packages in parallel, and the builder is shared.40type Builder struct {41 WorkDir string // the temporary work directory (ends in filepath.Separator)42 getVendorDir func() string // TODO(jitsu): remove this after we eliminate global module state43 actionCache map[cacheKey]*Action // a cache of already-constructed actions44 flagCache map[[2]string]bool // a cache of supported compiler flags45 gccCompilerIDCache map[string]cache.ActionID // cache for gccCompilerID4647 IsCmdList bool // running as part of go list; set p.Stale and additional fields below48 NeedError bool // list needs p.Error49 NeedExport bool // list needs p.Export50 NeedCompiledGoFiles bool // list needs p.CompiledGoFiles51 AllowErrors bool // errors don't immediately exit the program5253 objdirSeq int // counter for NewObjdir54 pkgSeq int5556 backgroundSh *Shell // Shell that per-Action Shells are derived from5758 exec sync.Mutex59 readySema chan bool60 ready actionQueue6162 id sync.Mutex63 toolIDCache par.Cache[string, string] // tool name -> tool ID64 gccToolIDCache map[string]string // tool name -> tool ID65 buildIDCache map[string]string // file name -> build ID66}6768// NOTE: Much of Action would not need to be exported if not for test.69// Maybe test functionality should move into this package too?7071// An Actor runs an action.72type Actor interface {73 Act(*Builder, context.Context, *Action) error74}7576// An ActorFunc is an Actor that calls the function.77type ActorFunc func(*Builder, context.Context, *Action) error7879func (f ActorFunc) Act(b *Builder, ctx context.Context, a *Action) error {80 return f(b, ctx, a)81}8283// An Action represents a single action in the action graph.84type Action struct {85 Mode string // description of action operation86 Package *load.Package // the package this action works on87 Deps []*Action // actions that must happen before this one88 Actor Actor // the action itself (nil = no-op)89 IgnoreFail bool // whether to run f even if dependencies fail90 TestOutput *bytes.Buffer // test output buffer91 Args []string // additional args for runProgram9293 Provider any // Additional information to be passed to successive actions. Similar to a Bazel provider.9495 triggers []*Action // inverse of deps9697 buggyInstall bool // is this a buggy install (see -linkshared)?9899 TryCache func(*Builder, *Action, *Action) bool // callback for cache bypass100101 CacheExecutable bool // Whether to cache executables produced by link steps102103 // Generated files, directories.104 Objdir string // directory for intermediate objects105 Target string // goal of the action: the created package or executable106 built string // the actual created package or executable107 cachedExecutable string // the cached executable, if CacheExecutable was set108 actionID cache.ActionID // cache ID of action input109 buildID string // build ID of action output110111 VetxOnly bool // Mode=="vet": only being called to supply info about dependencies112 needVet bool // Mode=="build": need to fill in vet config113 needBuild bool // Mode=="build": need to do actual build (can be false if needVet is true)114 needFix bool // Mode=="vet": need secondary target, a .zip file containing fixes115 vetCfg *vetConfig // vet config116 FixArchive string // the created .zip file containing fixes (if needFix)117 output []byte // output redirect buffer (nil means use b.Print)118119 sh *Shell // lazily created per-Action shell; see Builder.Shell120121 // Execution state.122 pending int // number of deps yet to complete123 priority int // relative execution priority124 Failed *Action // set to root cause if the action failed125 json *actionJSON // action graph information126 nonGoOverlay map[string]string // map from non-.go source files to copied files in objdir. Nil if no overlay is used.127 traceSpan *trace.Span128}129130// BuildActionID returns the action ID section of a's build ID.131func (a *Action) BuildActionID() string { return actionID(a.buildID) }132133// BuildContentID returns the content ID section of a's build ID.134func (a *Action) BuildContentID() string { return contentID(a.buildID) }135136// BuildID returns a's build ID.137func (a *Action) BuildID() string { return a.buildID }138139// BuiltTarget returns the actual file that was built. This differs140// from Target when the result was cached.141func (a *Action) BuiltTarget() string { return a.built }142143// CachedExecutable returns the cached executable, if CacheExecutable144// was set and the executable could be cached, and "" otherwise.145func (a *Action) CachedExecutable() string { return a.cachedExecutable }146147// An actionQueue is a priority queue of actions.148type actionQueue []*Action149150// Implement heap.Interface151func (q *actionQueue) Len() int { return len(*q) }152func (q *actionQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] }153func (q *actionQueue) Less(i, j int) bool { return (*q)[i].priority < (*q)[j].priority }154func (q *actionQueue) Push(x any) { *q = append(*q, x.(*Action)) }155func (q *actionQueue) Pop() any {156 n := len(*q) - 1157 x := (*q)[n]158 *q = (*q)[:n]159 return x160}161162func (q *actionQueue) push(a *Action) {163 if a.json != nil {164 a.json.TimeReady = time.Now()165 }166 heap.Push(q, a)167}168169func (q *actionQueue) pop() *Action {170 return heap.Pop(q).(*Action)171}172173type actionJSON struct {174 ID int175 Mode string176 Package string177 Deps []int `json:",omitempty"`178 IgnoreFail bool `json:",omitempty"`179 Args []string `json:",omitempty"`180 Link bool `json:",omitempty"`181 Objdir string `json:",omitempty"`182 Target string `json:",omitempty"`183 Priority int `json:",omitempty"`184 Failed bool `json:",omitempty"`185 Built string `json:",omitempty"`186 VetxOnly bool `json:",omitempty"`187 NeedVet bool `json:",omitempty"`188 NeedBuild bool `json:",omitempty"`189 ActionID string `json:",omitempty"`190 BuildID string `json:",omitempty"`191 TimeReady time.Time `json:",omitempty"`192 TimeStart time.Time `json:",omitempty"`193 TimeDone time.Time `json:",omitempty"`194195 Cmd []string // `json:",omitempty"`196 CmdReal time.Duration `json:",omitempty"`197 CmdUser time.Duration `json:",omitempty"`198 CmdSys time.Duration `json:",omitempty"`199}200201// cacheKey is the key for the action cache.202type cacheKey struct {203 mode string204 p *load.Package205}206207func actionGraphJSON(a *Action) string {208 var workq []*Action209 var inWorkq = make(map[*Action]int)210211 add := func(a *Action) {212 if _, ok := inWorkq[a]; ok {213 return214 }215 inWorkq[a] = len(workq)216 workq = append(workq, a)217 }218 add(a)219220 for i := 0; i < len(workq); i++ {221 for _, dep := range workq[i].Deps {222 add(dep)223 }224 }225226 list := make([]*actionJSON, 0, len(workq))227 for id, a := range workq {228 if a.json == nil {229 a.json = &actionJSON{230 Mode: a.Mode,231 ID: id,232 IgnoreFail: a.IgnoreFail,233 Args: a.Args,234 Objdir: a.Objdir,235 Target: a.Target,236 Failed: a.Failed != nil,237 Priority: a.priority,238 Built: a.built,239 VetxOnly: a.VetxOnly,240 NeedBuild: a.needBuild,241 NeedVet: a.needVet,242 }243 if a.Package != nil {244 // TODO(rsc): Make this a unique key for a.Package somehow.245 a.json.Package = a.Package.ImportPath246 }247 for _, a1 := range a.Deps {248 a.json.Deps = append(a.json.Deps, inWorkq[a1])249 }250 }251 list = append(list, a.json)252 }253254 js, err := json.MarshalIndent(list, "", "\t")255 if err != nil {256 fmt.Fprintf(os.Stderr, "go: writing debug action graph: %v\n", err)257 return ""258 }259 return string(js)260}261262// BuildMode specifies the build mode:263// are we just building things or also installing the results?264type BuildMode int265266const (267 ModeBuild BuildMode = iota268 ModeInstall269 ModeBuggyInstall270271 ModeVetOnly = 1 << 8272)273274// NewBuilder returns a new Builder ready for use.275//276// If workDir is the empty string, NewBuilder creates a WorkDir if needed277// and arranges for it to be removed in case of an unclean exit.278// The caller must Close the builder explicitly to clean up the WorkDir279// before a clean exit.280func NewBuilder(workDir string, getVendorDir func() string) *Builder {281 b := new(Builder)282 b.getVendorDir = getVendorDir283284 b.actionCache = make(map[cacheKey]*Action)285 b.gccToolIDCache = make(map[string]string)286 b.buildIDCache = make(map[string]string)287288 printWorkDir := false289 if workDir != "" {290 b.WorkDir = workDir291 } else if cfg.BuildN {292 b.WorkDir = "$WORK"293 } else {294 if !buildInitStarted {295 panic("internal error: NewBuilder called before BuildInit")296 }297 tmp, err := os.MkdirTemp(cfg.Getenv("GOTMPDIR"), "go-build")298 if err != nil {299 base.Fatalf("go: creating work dir: %v", err)300 }301 if !filepath.IsAbs(tmp) {302 abs, err := filepath.Abs(tmp)303 if err != nil {304 os.RemoveAll(tmp)305 base.Fatalf("go: creating work dir: %v", err)306 }307 tmp = abs308 }309 b.WorkDir = tmp310 builderWorkDirs.Store(b, b.WorkDir)311 printWorkDir = cfg.BuildX || cfg.BuildWork312 }313314 b.backgroundSh = NewShell(b.WorkDir, nil)315316 if printWorkDir {317 b.BackgroundShell().Printf("WORK=%s\n", b.WorkDir)318 }319320 if err := CheckGOOSARCHPair(cfg.Goos, cfg.Goarch); err != nil {321 fmt.Fprintf(os.Stderr, "go: %v\n", err)322 base.SetExitStatus(2)323 base.Exit()324 }325326 for _, tag := range cfg.BuildContext.BuildTags {327 if strings.Contains(tag, ",") {328 fmt.Fprintf(os.Stderr, "go: -tags space-separated list contains comma\n")329 base.SetExitStatus(2)330 base.Exit()331 }332 }333334 return b335}336337var builderWorkDirs sync.Map // *Builder → WorkDir338339func (b *Builder) Close() error {340 wd, ok := builderWorkDirs.Load(b)341 if !ok {342 return nil343 }344 defer builderWorkDirs.Delete(b)345346 if b.WorkDir != wd.(string) {347 base.Errorf("go: internal error: Builder WorkDir unexpectedly changed from %s to %s", wd, b.WorkDir)348 }349350 if !cfg.BuildWork {351 if err := robustio.RemoveAll(b.WorkDir); err != nil {352 return err353 }354 }355 b.WorkDir = ""356 return nil357}358359func closeBuilders() {360 leakedBuilders := 0361 builderWorkDirs.Range(func(bi, _ any) bool {362 leakedBuilders++363 if err := bi.(*Builder).Close(); err != nil {364 base.Error(err)365 }366 return true367 })368369 if leakedBuilders > 0 && base.GetExitStatus() == 0 {370 fmt.Fprintf(os.Stderr, "go: internal error: Builder leaked on successful exit\n")371 base.SetExitStatus(1)372 }373}374375func CheckGOOSARCHPair(goos, goarch string) error {376 if !platform.BuildModeSupported(cfg.BuildContext.Compiler, "default", goos, goarch) {377 return fmt.Errorf("unsupported GOOS/GOARCH pair %s/%s", goos, goarch)378 }379 return nil380}381382// NewObjdir returns the name of a fresh object directory under b.WorkDir.383// It is up to the caller to call b.Mkdir on the result at an appropriate time.384// The result ends in a slash, so that file names in that directory385// can be constructed with direct string addition.386//387// NewObjdir must be called only from a single goroutine at a time,388// so it is safe to call during action graph construction, but it must not389// be called during action graph execution.390func (b *Builder) NewObjdir() string {391 b.objdirSeq++392 return str.WithFilePathSeparator(filepath.Join(b.WorkDir, fmt.Sprintf("b%03d", b.objdirSeq)))393}394395// readpkglist returns the list of packages that were built into the shared library396// at shlibpath. For the native toolchain this list is stored, newline separated, in397// an ELF note with name "Go\x00\x00" and type 1. For GCCGO it is extracted from the398// .go_export section.399func readpkglist(s *modload.Loader, shlibpath string) (pkgs []*load.Package) {400 var stk load.ImportStack401 if cfg.BuildToolchainName == "gccgo" {402 f, err := elf.Open(shlibpath)403 if err != nil {404 base.Fatal(fmt.Errorf("failed to open shared library: %v", err))405 }406 defer f.Close()407 sect := f.Section(".go_export")408 if sect == nil {409 base.Fatal(fmt.Errorf("%s: missing .go_export section", shlibpath))410 }411 data, err := sect.Data()412 if err != nil {413 base.Fatal(fmt.Errorf("%s: failed to read .go_export section: %v", shlibpath, err))414 }415 pkgpath := []byte("pkgpath ")416 for _, line := range bytes.Split(data, []byte{'\n'}) {417 if path, found := bytes.CutPrefix(line, pkgpath); found {418 path = bytes.TrimSuffix(path, []byte{';'})419 pkgs = append(pkgs, load.LoadPackageWithFlags(s, string(path), base.Cwd(), &stk, nil, 0))420 }421 }422 } else {423 pkglistbytes, err := buildid.ReadELFNote(shlibpath, "Go\x00\x00", 1)424 if err != nil {425 base.Fatalf("readELFNote failed: %v", err)426 }427 scanner := bufio.NewScanner(bytes.NewBuffer(pkglistbytes))428 for scanner.Scan() {429 t := scanner.Text()430 pkgs = append(pkgs, load.LoadPackageWithFlags(s, t, base.Cwd(), &stk, nil, 0))431 }432 }433 return434}435436// cacheAction looks up {mode, p} in the cache and returns the resulting action.437// If the cache has no such action, f() is recorded and returned.438// TODO(rsc): Change the second key from *load.Package to interface{},439// to make the caching in linkShared less awkward?440func (b *Builder) cacheAction(mode string, p *load.Package, f func() *Action) *Action {441 a := b.actionCache[cacheKey{mode, p}]442 if a == nil {443 a = f()444 b.actionCache[cacheKey{mode, p}] = a445 }446 return a447}448449// AutoAction returns the "right" action for go build or go install of p.450func (b *Builder) AutoAction(s *modload.Loader, mode, depMode BuildMode, p *load.Package) *Action {451 if p.Name == "main" {452 return b.LinkAction(s, mode, depMode, p)453 }454 return b.CompileAction(mode, depMode, p)455}456457// buildActor implements the Actor interface for package build458// actions. For most package builds this simply means invoking the459// *Builder.build method.460type buildActor struct{}461462func (ba *buildActor) Act(b *Builder, ctx context.Context, a *Action) error {463 return b.build(ctx, a)464}465466// pgoActionID computes the action ID for a preprocess PGO action.467func (b *Builder) pgoActionID(input string) cache.ActionID {468 h := cache.NewHash("preprocess PGO profile " + input)469470 fmt.Fprintf(h, "preprocess PGO profile\n")471 fmt.Fprintf(h, "preprofile %s\n", b.toolID("preprofile"))472 fmt.Fprintf(h, "input %q\n", b.fileHash(input))473474 return h.Sum()475}476477// pgoActor implements the Actor interface for preprocessing PGO profiles.478type pgoActor struct {479 // input is the path to the original pprof profile.480 input string481}482483func (p *pgoActor) Act(b *Builder, ctx context.Context, a *Action) error {484 if b.useCache(a, b.pgoActionID(p.input), a.Target, !b.IsCmdList) || b.IsCmdList {485 return nil486 }487 defer b.flushOutput(a)488489 sh := b.Shell(a)490491 if err := sh.Mkdir(a.Objdir); err != nil {492 return err493 }494495 if err := sh.run(".", p.input, nil, cfg.BuildToolexec, base.Tool("preprofile"), "-o", a.Target, "-i", p.input); err != nil {496 return err497 }498499 // N.B. Builder.build looks for the out in a.built, regardless of500 // whether this came from cache.501 a.built = a.Target502503 if !cfg.BuildN {504 // Cache the output.505 //506 // N.B. We don't use updateBuildID here, as preprocessed PGO profiles507 // do not contain a build ID. updateBuildID is typically responsible508 // for adding to the cache, thus we must do so ourselves instead.509510 r, err := os.Open(a.Target)511 if err != nil {512 return fmt.Errorf("error opening target for caching: %w", err)513 }514515 c := cache.Default()516 outputID, _, err := c.Put(a.actionID, r)517 r.Close()518 if err != nil {519 return fmt.Errorf("error adding target to cache: %w", err)520 }521 if cfg.BuildX {522 sh.ShowCmd("", "%s # internal", joinUnambiguously(str.StringList("cp", a.Target, c.OutputFile(outputID))))523 }524 }525526 return nil527}528529type coverProvider struct {530 // name of static metadata file fragment emitted by the cover531 // tool as part of the package cover action, for selected532 // "go test -cover" runs.533 covMetaFileName string534535 // coverageConfig is the path to the json-serialized covcmd.CoverPkgConfig536 // provided to the cover tool. The config is created by coverConfig.537 coverageConfig string538539 goSources, cgoSources []string // The go and cgo sources generated by the cover tool, which should be used instead of the raw sources on the package.540}541542// runCgoActor implements the Actor interface for running the cgo command for the package.543type runCgoActor struct {544}545546func (c runCgoActor) Act(b *Builder, ctx context.Context, a *Action) error {547 return b.runCgo(ctx, a)548}549550type cgoCompileActor struct {551 file string552553 compileFunc func(*Action, string, string, []string, string) error554 getFlagsFunc func(*runCgoProvider) []string555}556557func (c cgoCompileActor) Act(b *Builder, ctx context.Context, a *Action) error {558 pr, ok := a.Deps[0].Provider.(*runCgoProvider)559 if !ok {560 base.Fatalf("internal error: missing runCgoProvider")561 }562 a.nonGoOverlay = pr.nonGoOverlay563564 a.actionID = b.cgoCompileActionID(a, c.file, c.getFlagsFunc(pr))565 targetBase := filepath.Base(a.Target)566 if err := b.loadCachedObjdirFile(a, cache.Default(), targetBase); err == nil {567 a.built = a.Target568 return nil569 }570 defer b.flushOutput(a)571572 if err := c.compileFunc(a, a.Objdir, a.Target, c.getFlagsFunc(pr), c.file); err != nil {573 return err574 }575576 if !cfg.BuildN {577 b.cacheObjdirFile(a, cache.Default(), targetBase)578 }579580 return nil581}582583// CompileAction returns the action for compiling and possibly installing584// (according to mode) the given package. The resulting action is only585// for building packages (archives), never for linking executables.586// depMode is the action (build or install) to use when building dependencies.587// To turn package main into an executable, call b.Link instead.588func (b *Builder) CompileAction(mode, depMode BuildMode, p *load.Package) *Action {589 vetOnly := mode&ModeVetOnly != 0590 mode &^= ModeVetOnly591592 if mode != ModeBuild && p.Target == "" {593 // No permanent target.594 mode = ModeBuild595 }596 if mode != ModeBuild && p.Name == "main" {597 // We never install the .a file for a main package.598 mode = ModeBuild599 }600601 // Construct package build action.602 a := b.cacheAction("build", p, func() *Action {603 a := &Action{604 Mode: "build",605 Package: p,606 Actor: &buildActor{},607 Objdir: b.NewObjdir(),608 }609610 if p.Error == nil || !p.Error.IsImportCycle {611 for _, p1 := range p.Internal.Imports {612 a.Deps = append(a.Deps, b.CompileAction(depMode, depMode, p1))613 }614 }615616 if p.Internal.PGOProfile != "" {617 pgoAction := b.cacheAction("preprocess PGO profile "+p.Internal.PGOProfile, nil, func() *Action {618 a := &Action{619 Mode: "preprocess PGO profile",620 Actor: &pgoActor{input: p.Internal.PGOProfile},621 Objdir: b.NewObjdir(),622 }623 a.Target = filepath.Join(a.Objdir, "pgo.preprofile")624625 return a626 })627 a.Deps = append(a.Deps, pgoAction)628 }629630 if p.Standard {631 switch p.ImportPath {632 case "builtin", "unsafe":633 // Fake packages - nothing to build.634 a.Mode = "built-in package"635 a.Actor = nil636 return a637 }638639 // gccgo standard library is "fake" too.640 if cfg.BuildToolchainName == "gccgo" {641 // the target name is needed for cgo.642 a.Mode = "gccgo stdlib"643 a.Target = p.Target644 a.Actor = nil645 return a646 }647 }648649 // Create a cover action if we need to instrument the code for coverage.650 // The cover action always runs in the same go build invocation as the build,651 // and is not cached separately, so it can use the same objdir.652 var coverAction *Action653 if p.Internal.Cover.Mode != "" {654 coverAction = b.cacheAction("cover", p, func() *Action {655 return &Action{656 Mode: "cover",657 Package: p,658 Actor: ActorFunc((*Builder).runCover),659 Objdir: a.Objdir,660 }661 })662 a.Deps = append(a.Deps, coverAction)663 }664665 // Create actions to run swig and cgo if needed. These actions666 // cache their outputs independently under their own action IDs.667 if p.UsesCgo() || p.UsesSwig() {668 var cgoDeps []*Action669 if coverAction != nil {670 cgoDeps = append(cgoDeps, coverAction)671 }672 a.Deps = append(a.Deps, b.cgoAction(p, a.Objdir, cgoDeps, coverAction != nil))673 }674675 return a676 })677678 // Find the build action; the cache entry may have been replaced679 // by the install action during (*Builder).installAction.680 buildAction := a681 switch buildAction.Mode {682 case "build", "built-in package", "gccgo stdlib":683 // ok684 case "build-install":685 buildAction = a.Deps[0]686 default:687 panic("lost build action: " + buildAction.Mode)688 }689 buildAction.needBuild = buildAction.needBuild || !vetOnly690691 // Construct install action.692 if mode == ModeInstall || mode == ModeBuggyInstall {693 a = b.installAction(a, mode)694 }695696 return a697}698699func (b *Builder) cgoAction(p *load.Package, objdir string, deps []*Action, hasCover bool) *Action {700 cgoCollectAction := b.cacheAction("cgo collect", p, func() *Action {701 // Run cgo702 runCgo := b.cacheAction("cgo run", p, func() *Action {703 return &Action{704 Package: p,705 Mode: "cgo run",706 Actor: &runCgoActor{},707 Objdir: objdir,708 Deps: deps,709 }710 })711712 // Determine which files swig will produce in the cgo run action. We'll need to create713 // actions to compile the C and C++ files produced by swig, as well as the C file714 // produced by cgo processing swig's Go file outputs.715 swigGo, swigC, swigCXX := b.swigOutputs(p, objdir)716717 oseq := 0718 nextOfile := func() string {719 oseq++720 return objdir + fmt.Sprintf("_x%03d.o", oseq)721 }722 compileAction := func(file string, getFlagsFunc func(*runCgoProvider) []string, compileFunc func(*Action, string, string, []string, string) error) *Action {723 mode := "cgo compile " + file724 return b.cacheAction(mode, p, func() *Action {725 return &Action{726 Package: p,727 Mode: mode,728 Actor: &cgoCompileActor{file: file, getFlagsFunc: getFlagsFunc, compileFunc: compileFunc},729 Deps: []*Action{runCgo},730 Objdir: objdir,731 Target: nextOfile(),732 }733 })734 }735736 var collectDeps []*Action737738 // Add compile actions for C files generated by cgo.739 cgoFiles := p.CgoFiles740 if hasCover {741 cgoFiles = slices.Clone(cgoFiles)742 for i := range cgoFiles {743 cgoFiles[i] = strings.TrimSuffix(cgoFiles[i], ".go") + ".cover.go"744 }745 }746 cfiles := []string{"_cgo_export.c"}747 for _, fn := range slices.Concat(cgoFiles, swigGo) {748 cfiles = append(cfiles, strings.TrimSuffix(filepath.Base(fn), ".go")+".cgo2.c")749 }750 for _, f := range cfiles {751 collectDeps = append(collectDeps, compileAction(objdir+f, (*runCgoProvider).cflags, b.gcc))752 }753754 // Add compile actions for S files.755 var sfiles []string756 // In a package using cgo, cgo compiles the C, C++ and assembly files with gcc.757 // There is one exception: runtime/cgo's job is to bridge the758 // cgo and non-cgo worlds, so it necessarily has files in both.759 // In that case gcc only gets the gcc_* files.760 if p.Standard && p.ImportPath == "runtime/cgo" {761 for _, f := range p.SFiles {762 if strings.HasPrefix(f, "gcc_") {763 sfiles = append(sfiles, f)764 }765 }766 } else {767 sfiles = p.SFiles768 }769 for _, f := range sfiles {770 collectDeps = append(collectDeps, compileAction(mkAbs(p.Dir, f), (*runCgoProvider).cflags, b.gas))771 }772773 // Add compile actions for C files in the package and M files.774 for _, f := range slices.Concat(p.CFiles, p.MFiles) {775 collectDeps = append(collectDeps, compileAction(filepath.Join(p.Dir, f), (*runCgoProvider).cflags, b.gcc))776 }777 // Add compile actions for C files generated by swig.778 for _, f := range swigC {779 collectDeps = append(collectDeps, compileAction(f, (*runCgoProvider).cflags, b.gcc))780 }781782 // Add compile actions for C++ files in the package.783 for _, f := range p.CXXFiles {784 collectDeps = append(collectDeps, compileAction(filepath.Join(p.Dir, f), (*runCgoProvider).cxxflags, b.gxx))785 }786 // Add compile actions for C++ files generated by swig.787 for _, f := range swigCXX {788 collectDeps = append(collectDeps, compileAction(f, (*runCgoProvider).cxxflags, b.gxx))789 }790791 // Add compile actions for Fortran files in the package.792 for _, f := range p.FFiles {793 collectDeps = append(collectDeps, compileAction(filepath.Join(p.Dir, f), (*runCgoProvider).fflags, b.gfortran))794 }795796 // Add a single convenience action that does nothing to join the previous action,797 // and better separate the cgo action dependencies of the build action from the798 // build actions for its package dependencies.799 return &Action{800 Mode: "collect cgo",801 Actor: ActorFunc(func(b *Builder, ctx context.Context, a *Action) error {802 // Use the cgo run action's provider as our provider output,803 // so it can be easily accessed by the build action.804 a.Provider = a.Deps[0].Deps[0].Provider805 return nil806 }),807 Deps: collectDeps,808 Objdir: objdir,809 }810 })811812 return cgoCollectAction813}814815// VetAction returns the action for running go vet on package p.816// It depends on the action for compiling p.817// If the caller may be causing p to be installed, it is up to the caller818// to make sure that the install depends on (runs after) vet.819func (b *Builder) VetAction(s *modload.Loader, mode, depMode BuildMode, needFix bool, p *load.Package) *Action {820 a := b.vetAction(s, mode, depMode, p)821 a.VetxOnly = false822 a.needFix = needFix823 return a824}825826func (b *Builder) vetAction(s *modload.Loader, mode, depMode BuildMode, p *load.Package) *Action {827 // Construct vet action.828 a := b.cacheAction("vet", p, func() *Action {829 a1 := b.CompileAction(mode|ModeVetOnly, depMode, p)830831 var deps []*Action832 if a1.buggyInstall {833 // (*Builder).vet expects deps[0] to be the package.834 // If we see buggyInstall835 // here then a1 is an install of a shared library,836 // and the real package is a1.Deps[0].837 deps = []*Action{a1.Deps[0], a1}838 } else {839 deps = []*Action{a1}840 }841 for _, p1 := range p.Internal.Imports {842 deps = append(deps, b.vetAction(s, mode, depMode, p1))843 }844845 a := &Action{846 Mode: "vet",847 Package: p,848 Deps: deps,849 Objdir: a1.Objdir,850 VetxOnly: true,851 IgnoreFail: true, // it's OK if vet of dependencies "fails" (reports problems)852 }853 if a1.Actor == nil {854 // Built-in packages like unsafe.855 return a856 }857 deps[0].needVet = true858 a.Actor = ActorFunc((*Builder).vet)859 return a860 })861 return a862}863864// LinkAction returns the action for linking p into an executable865// and possibly installing the result (according to mode).866// depMode is the action (build or install) to use when compiling dependencies.867func (b *Builder) LinkAction(s *modload.Loader, mode, depMode BuildMode, p *load.Package) *Action {868 // Construct link action.869 a := b.cacheAction("link", p, func() *Action {870 a := &Action{871 Mode: "link",872 Package: p,873 }874875 a1 := b.CompileAction(ModeBuild, depMode, p)876 a.Actor = ActorFunc((*Builder).link)877 a.Deps = []*Action{a1}878 a.Objdir = a1.Objdir879880 // An executable file. (This is the name of a temporary file.)881 // Because we run the temporary file in 'go run' and 'go test',882 // the name will show up in ps listings. If the caller has specified883 // a name, use that instead of a.out. The binary is generated884 // in an otherwise empty subdirectory named exe to avoid885 // naming conflicts. The only possible conflict is if we were886 // to create a top-level package named exe.887 name := "a.out"888 if p.Internal.ExeName != "" {889 name = p.Internal.ExeName890 } else if (cfg.Goos == "darwin" || cfg.Goos == "windows") && cfg.BuildBuildmode == "c-shared" && p.Target != "" {891 // On OS X, the linker output name gets recorded in the892 // shared library's LC_ID_DYLIB load command.893 // The code invoking the linker knows to pass only the final894 // path element. Arrange that the path element matches what895 // we'll install it as; otherwise the library is only loadable as "a.out".896 // On Windows, DLL file name is recorded in PE file897 // export section, so do like on OS X.898 _, name = filepath.Split(p.Target)899 }900 a.Target = a.Objdir + filepath.Join("exe", name) + cfg.ExeSuffix901 a.built = a.Target902 b.addTransitiveLinkDeps(s, a, a1, "")903904 // Sequence the build of the main package (a1) strictly after the build905 // of all other dependencies that go into the link. It is likely to be after906 // them anyway, but just make sure. This is required by the build ID-based907 // shortcut in (*Builder).useCache(a1), which will call b.linkActionID(a).908 // In order for that linkActionID call to compute the right action ID, all the909 // dependencies of a (except a1) must have completed building and have910 // recorded their build IDs.911 a1.Deps = append(a1.Deps, &Action{Mode: "nop", Deps: a.Deps[1:]})912 return a913 })914915 if mode == ModeInstall || mode == ModeBuggyInstall {916 a = b.installAction(a, mode)917 }918919 return a920}921922// installAction returns the action for installing the result of a1.923func (b *Builder) installAction(a1 *Action, mode BuildMode) *Action {924 // Because we overwrite the build action with the install action below,925 // a1 may already be an install action fetched from the "build" cache key,926 // and the caller just doesn't realize.927 if strings.HasSuffix(a1.Mode, "-install") {928 if a1.buggyInstall && mode == ModeInstall {929 // Congratulations! The buggy install is now a proper install.930 a1.buggyInstall = false931 }932 return a1933 }934935 // If there's no actual action to build a1,936 // there's nothing to install either.937 // This happens if a1 corresponds to reusing an already-built object.938 if a1.Actor == nil {939 return a1940 }941942 p := a1.Package943 return b.cacheAction(a1.Mode+"-install", p, func() *Action {944 // The install deletes the temporary build result,945 // so we need all other actions, both past and future,946 // that attempt to depend on the build to depend instead947 // on the install.948949 // Make a private copy of a1 (the build action),950 // no longer accessible to any other rules.951 buildAction := new(Action)952 *buildAction = *a1953954 // Overwrite a1 with the install action.955 // This takes care of updating past actions that956 // point at a1 for the build action; now they will957 // point at a1 and get the install action.958 // We also leave a1 in the action cache as the result959 // for "build", so that actions not yet created that960 // try to depend on the build will instead depend961 // on the install.962 *a1 = Action{963 Mode: buildAction.Mode + "-install",964 Actor: ActorFunc(BuildInstallFunc),965 Package: p,966 Objdir: buildAction.Objdir,967 Deps: []*Action{buildAction},968 Target: p.Target,969 built: p.Target,970971 buggyInstall: mode == ModeBuggyInstall,972 }973974 b.addInstallHeaderAction(a1)975 return a1976 })977}978979// addTransitiveLinkDeps adds to the link action a all packages980// that are transitive dependencies of a1.Deps.981// That is, if a is a link of package main, a1 is the compile of package main982// and a1.Deps is the actions for building packages directly imported by983// package main (what the compiler needs). The linker needs all packages984// transitively imported by the whole program; addTransitiveLinkDeps985// makes sure those are present in a.Deps.986// If shlib is non-empty, then a corresponds to the build and installation of shlib,987// so any rebuild of shlib should not be added as a dependency.988func (b *Builder) addTransitiveLinkDeps(s *modload.Loader, a, a1 *Action, shlib string) {989 // Expand Deps to include all built packages, for the linker.990 // Use breadth-first search to find rebuilt-for-test packages991 // before the standard ones.992 // TODO(rsc): Eliminate the standard ones from the action graph,993 // which will require doing a little bit more rebuilding.994 workq := []*Action{a1}995 haveDep := map[string]bool{}996 if a1.Package != nil {997 haveDep[a1.Package.ImportPath] = true998 }999 for i := 0; i < len(workq); i++ {1000 a1 := workq[i]1001 for _, a2 := range a1.Deps {1002 // TODO(rsc): Find a better discriminator than the Mode strings, once the dust settles.1003 if a2.Package == nil || (a2.Mode != "build-install" && a2.Mode != "build") || haveDep[a2.Package.ImportPath] {1004 continue1005 }1006 haveDep[a2.Package.ImportPath] = true1007 a.Deps = append(a.Deps, a2)1008 if a2.Mode == "build-install" {1009 a2 = a2.Deps[0] // walk children of "build" action1010 }1011 workq = append(workq, a2)1012 }1013 }10141015 // If this is go build -linkshared, then the link depends on the shared libraries1016 // in addition to the packages themselves. (The compile steps do not.)1017 if cfg.BuildLinkshared {1018 haveShlib := map[string]bool{shlib: true}1019 for _, a1 := range a.Deps {1020 p1 := a1.Package1021 if p1 == nil || p1.Shlib == "" || haveShlib[filepath.Base(p1.Shlib)] {1022 continue1023 }1024 haveShlib[filepath.Base(p1.Shlib)] = true1025 // TODO(rsc): The use of ModeInstall here is suspect, but if we only do ModeBuild,1026 // we'll end up building an overall library or executable that depends at runtime1027 // on other libraries that are out-of-date, which is clearly not good either.1028 // We call it ModeBuggyInstall to make clear that this is not right.1029 a.Deps = append(a.Deps, b.linkSharedAction(s, ModeBuggyInstall, ModeBuggyInstall, p1.Shlib, nil))1030 }1031 }1032}10331034// addInstallHeaderAction adds an install header action to a, if needed.1035// The action a should be an install action as generated by either1036// b.CompileAction or b.LinkAction with mode=ModeInstall,1037// and so a.Deps[0] is the corresponding build action.1038func (b *Builder) addInstallHeaderAction(a *Action) {1039 // Install header for cgo in c-archive and c-shared modes.1040 p := a.Package1041 if p.UsesCgo() && (cfg.BuildBuildmode == "c-archive" || cfg.BuildBuildmode == "c-shared") {1042 hdrTarget := a.Target[:len(a.Target)-len(filepath.Ext(a.Target))] + ".h"1043 if cfg.BuildContext.Compiler == "gccgo" && cfg.BuildO == "" {1044 // For the header file, remove the "lib"1045 // added by go/build, so we generate pkg.h1046 // rather than libpkg.h.1047 dir, file := filepath.Split(hdrTarget)1048 file = strings.TrimPrefix(file, "lib")1049 hdrTarget = filepath.Join(dir, file)1050 }1051 ah := &Action{1052 Mode: "install header",1053 Package: a.Package,1054 Deps: []*Action{a.Deps[0]},1055 Actor: ActorFunc((*Builder).installHeader),1056 Objdir: a.Deps[0].Objdir,1057 Target: hdrTarget,1058 }1059 a.Deps = append(a.Deps, ah)1060 }1061}10621063// buildmodeShared takes the "go build" action a1 into the building of a shared library of a1.Deps.1064// That is, the input a1 represents "go build pkgs" and the result represents "go build -buildmode=shared pkgs".1065func (b *Builder) buildmodeShared(s *modload.Loader, mode, depMode BuildMode, args []string, pkgs []*load.Package, a1 *Action) *Action {1066 name, err := libname(args, pkgs)1067 if err != nil {1068 base.Fatalf("%v", err)1069 }1070 return b.linkSharedAction(s, mode, depMode, name, a1)1071}10721073// linkSharedAction takes a grouping action a1 corresponding to a list of built packages1074// and returns an action that links them together into a shared library with the name shlib.1075// If a1 is nil, shlib should be an absolute path to an existing shared library,1076// and then linkSharedAction reads that library to find out the package list.1077func (b *Builder) linkSharedAction(s *modload.Loader, mode, depMode BuildMode, shlib string, a1 *Action) *Action {1078 fullShlib := shlib1079 shlib = filepath.Base(shlib)1080 a := b.cacheAction("build-shlib "+shlib, nil, func() *Action {1081 if a1 == nil {1082 // TODO(rsc): Need to find some other place to store config,1083 // not in pkg directory. See golang.org/issue/22196.1084 pkgs := readpkglist(s, fullShlib)1085 a1 = &Action{1086 Mode: "shlib packages",1087 }1088 for _, p := range pkgs {1089 a1.Deps = append(a1.Deps, b.CompileAction(mode, depMode, p))1090 }1091 }10921093 // Fake package to hold ldflags.1094 // As usual shared libraries are a kludgy, abstraction-violating special case:1095 // we let them use the flags specified for the command-line arguments.1096 p := &load.Package{}1097 p.Internal.CmdlinePkg = true1098 p.Internal.Ldflags = load.BuildLdflags.For(s, p)1099 p.Internal.Gccgoflags = load.BuildGccgoflags.For(s, p)11001101 // Add implicit dependencies to pkgs list.1102 // Currently buildmode=shared forces external linking mode, and1103 // external linking mode forces an import of runtime/cgo (and1104 // math on arm). So if it was not passed on the command line and1105 // it is not present in another shared library, add it here.1106 // TODO(rsc): Maybe this should only happen if "runtime" is in the original package set.1107 // TODO(rsc): This should probably be changed to use load.LinkerDeps(p).1108 // TODO(rsc): We don't add standard library imports for gccgo1109 // because they are all always linked in anyhow.1110 // Maybe load.LinkerDeps should be used and updated.1111 a := &Action{1112 Mode: "go build -buildmode=shared",1113 Package: p,1114 Objdir: b.NewObjdir(),1115 Actor: ActorFunc((*Builder).linkShared),1116 Deps: []*Action{a1},1117 }1118 a.Target = filepath.Join(a.Objdir, shlib)1119 if cfg.BuildToolchainName != "gccgo" {1120 add := func(a1 *Action, pkg string, force bool) {1121 for _, a2 := range a1.Deps {1122 if a2.Package != nil && a2.Package.ImportPath == pkg {1123 return1124 }1125 }1126 var stk load.ImportStack1127 p := load.LoadPackageWithFlags(s, pkg, base.Cwd(), &stk, nil, 0)1128 if p.Error != nil {1129 base.Fatalf("load %s: %v", pkg, p.Error)1130 }1131 // Assume that if pkg (runtime/cgo or math)1132 // is already accounted for in a different shared library,1133 // then that shared library also contains runtime,1134 // so that anything we do will depend on that library,1135 // so we don't need to include pkg in our shared library.1136 if force || p.Shlib == "" || filepath.Base(p.Shlib) == pkg {1137 a1.Deps = append(a1.Deps, b.CompileAction(depMode, depMode, p))1138 }1139 }1140 add(a1, "runtime/cgo", false)1141 if cfg.Goarch == "arm" {1142 add(a1, "math", false)1143 }11441145 // The linker step still needs all the usual linker deps.1146 // (For example, the linker always opens runtime.a.)1147 ldDeps, err := load.LinkerDeps(s, nil)1148 if err != nil {1149 base.Error(err)1150 }1151 for _, dep := range ldDeps {1152 add(a, dep, true)1153 }1154 }1155 b.addTransitiveLinkDeps(s, a, a1, shlib)1156 return a1157 })11581159 // Install result.1160 if (mode == ModeInstall || mode == ModeBuggyInstall) && a.Actor != nil {1161 buildAction := a11621163 a = b.cacheAction("install-shlib "+shlib, nil, func() *Action {1164 // Determine the eventual install target.1165 // The install target is root/pkg/shlib, where root is the source root1166 // in which all the packages lie.1167 // TODO(rsc): Perhaps this cross-root check should apply to the full1168 // transitive package dependency list, not just the ones named1169 // on the command line?1170 pkgDir := a1.Deps[0].Package.Internal.Build.PkgTargetRoot1171 for _, a2 := range a1.Deps {1172 if dir := a2.Package.Internal.Build.PkgTargetRoot; dir != pkgDir {1173 base.Fatalf("installing shared library: cannot use packages %s and %s from different roots %s and %s",1174 a1.Deps[0].Package.ImportPath,1175 a2.Package.ImportPath,1176 pkgDir,1177 dir)1178 }1179 }1180 // TODO(rsc): Find out and explain here why gccgo is different.1181 if cfg.BuildToolchainName == "gccgo" {1182 pkgDir = filepath.Join(pkgDir, "shlibs")1183 }1184 target := filepath.Join(pkgDir, shlib)11851186 a := &Action{1187 Mode: "go install -buildmode=shared",1188 Objdir: buildAction.Objdir,1189 Actor: ActorFunc(BuildInstallFunc),1190 Deps: []*Action{buildAction},1191 Target: target,1192 }1193 for _, a2 := range buildAction.Deps[0].Deps {1194 p := a2.Package1195 pkgTargetRoot := p.Internal.Build.PkgTargetRoot1196 if pkgTargetRoot == "" {1197 continue1198 }1199 a.Deps = append(a.Deps, &Action{1200 Mode: "shlibname",1201 Package: p,1202 Actor: ActorFunc((*Builder).installShlibname),1203 Target: filepath.Join(pkgTargetRoot, p.ImportPath+".shlibname"),1204 Deps: []*Action{a.Deps[0]},1205 })1206 }1207 return a1208 })1209 }12101211 return a1212}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.