src/cmd/go/internal/work/action.go GO 1,213 lines View on github.com → Search inside
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}

Code quality findings 42

Empty interface; prefer specific types or generics for type safety
empty-interface
// TODO(rsc): Change the second key from *load.Package to interface{},
Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
warning correctness defer-in-loop
defer f.Close()
Use of unsafe package detected; ensure it’s necessary, justified in comments, and bounds-checked to avoid memory corruption
warning safety unsafe-package
// Built-in packages like unsafe.
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for id, a := range workq {
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
a.json.Deps = append(a.json.Deps, inWorkq[a1])
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
list = append(list, a.json)
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
return str.WithFilePathSeparator(filepath.Join(b.WorkDir, fmt.Sprintf("b%03d", b.objdirSeq)))
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if sect == nil {
String to byte slice conversion inside loop allocates a new slice each iteration; convert once before the loop
info correctness string-to-byte-in-loop
pkgpath := []byte("pkgpath ")
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
pkgs = append(pkgs, load.LoadPackageWithFlags(s, string(path), base.Cwd(), &stk, nil, 0))
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
pkgs = append(pkgs, load.LoadPackageWithFlags(s, t, base.Cwd(), &stk, nil, 0))
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if p.Error == nil || !p.Error.IsImportCycle {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for _, p1 := range p.Internal.Imports {
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
a.Deps = append(a.Deps, b.CompileAction(depMode, depMode, p1))
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
a.Target = filepath.Join(a.Objdir, "pgo.preprofile")
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
cfiles = append(cfiles, strings.TrimSuffix(filepath.Base(fn), ".go")+".cgo2.c")
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
collectDeps = append(collectDeps, compileAction(objdir+f, (*runCgoProvider).cflags, b.gcc))
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
sfiles = append(sfiles, f)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
collectDeps = append(collectDeps, compileAction(mkAbs(p.Dir, f), (*runCgoProvider).cflags, b.gas))
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
collectDeps = append(collectDeps, compileAction(filepath.Join(p.Dir, f), (*runCgoProvider).cflags, b.gcc))
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
collectDeps = append(collectDeps, compileAction(filepath.Join(p.Dir, f), (*runCgoProvider).cflags, b.gcc))
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
collectDeps = append(collectDeps, compileAction(f, (*runCgoProvider).cflags, b.gcc))
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
collectDeps = append(collectDeps, compileAction(filepath.Join(p.Dir, f), (*runCgoProvider).cxxflags, b.gxx))
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
collectDeps = append(collectDeps, compileAction(filepath.Join(p.Dir, f), (*runCgoProvider).cxxflags, b.gxx))
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
collectDeps = append(collectDeps, compileAction(f, (*runCgoProvider).cxxflags, b.gxx))
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
collectDeps = append(collectDeps, compileAction(filepath.Join(p.Dir, f), (*runCgoProvider).fflags, b.gfortran))
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
collectDeps = append(collectDeps, compileAction(filepath.Join(p.Dir, f), (*runCgoProvider).fflags, b.gfortran))
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
deps = append(deps, b.vetAction(s, mode, depMode, p1))
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
a.Target = a.Objdir + filepath.Join("exe", name) + cfg.ExeSuffix
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
a1.Deps = append(a1.Deps, &Action{Mode: "nop", Deps: a.Deps[1:]})
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
a.Deps = append(a.Deps, a2)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
workq = append(workq, a2)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
a.Deps = append(a.Deps, b.linkSharedAction(s, ModeBuggyInstall, ModeBuggyInstall, p1.Shlib, nil))
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
hdrTarget = filepath.Join(dir, file)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
a1.Deps = append(a1.Deps, b.CompileAction(mode, depMode, p))
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
a.Target = filepath.Join(a.Objdir, shlib)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
a1.Deps = append(a1.Deps, b.CompileAction(depMode, depMode, p))
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
pkgDir = filepath.Join(pkgDir, "shlibs")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
target := filepath.Join(pkgDir, shlib)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
a.Deps = append(a.Deps, &Action{
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
Target: filepath.Join(pkgTargetRoot, p.ImportPath+".shlibname"),

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.