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 execution.67package work89import (10 "bytes"11 "cmd/internal/cov/covcmd"12 "cmd/internal/pathcache"13 "context"14 "crypto/sha256"15 "encoding/json"16 "errors"17 "fmt"18 "go/token"19 "internal/lazyregexp"20 "io"21 "io/fs"22 "log"23 "math/rand"24 "os"25 "os/exec"26 "path/filepath"27 "regexp"28 "runtime"29 "slices"30 "sort"31 "strconv"32 "strings"33 "sync"34 "time"3536 "cmd/go/internal/base"37 "cmd/go/internal/cache"38 "cmd/go/internal/cfg"39 "cmd/go/internal/fsys"40 "cmd/go/internal/gover"41 "cmd/go/internal/load"42 "cmd/go/internal/modinfo"43 "cmd/go/internal/modload"44 "cmd/go/internal/str"45 "cmd/go/internal/trace"46 "cmd/internal/buildid"47 "cmd/internal/quoted"48 "cmd/internal/sys"4950 "golang.org/x/tools/go/analysis"51)5253const DefaultCFlags = "-O2 -g"5455// actionList returns the list of actions in the dag rooted at root56// as visited in a depth-first post-order traversal.57func actionList(root *Action) []*Action {58 seen := map[*Action]bool{}59 all := []*Action{}60 var walk func(*Action)61 walk = func(a *Action) {62 if seen[a] {63 return64 }65 seen[a] = true66 for _, a1 := range a.Deps {67 walk(a1)68 }69 all = append(all, a)70 }71 walk(root)72 return all73}7475// Do runs the action graph rooted at root.76func (b *Builder) Do(ctx context.Context, root *Action) {77 ctx, span := trace.StartSpan(ctx, "exec.Builder.Do ("+root.Mode+" "+root.Target+")")78 defer span.Done()7980 if !b.IsCmdList {81 // If we're doing real work, take time at the end to trim the cache.82 c := cache.Default()83 defer func() {84 if err := c.Close(); err != nil {85 base.Fatalf("go: failed to trim cache: %v", err)86 }87 }()88 }8990 // Build list of all actions, assigning depth-first post-order priority.91 // The original implementation here was a true queue92 // (using a channel) but it had the effect of getting93 // distracted by low-level leaf actions to the detriment94 // of completing higher-level actions. The order of95 // work does not matter much to overall execution time,96 // but when running "go test std" it is nice to see each test97 // results as soon as possible. The priorities assigned98 // ensure that, all else being equal, the execution prefers99 // to do what it would have done first in a simple depth-first100 // dependency order traversal.101 all := actionList(root)102 for i, a := range all {103 a.priority = i104 }105106 // Write action graph, without timing information, in case we fail and exit early.107 writeActionGraph := func() {108 if file := cfg.DebugActiongraph; file != "" {109 if strings.HasSuffix(file, ".go") {110 // Do not overwrite Go source code in:111 // go build -debug-actiongraph x.go112 base.Fatalf("go: refusing to write action graph to %v\n", file)113 }114 js := actionGraphJSON(root)115 if err := os.WriteFile(file, []byte(js), 0666); err != nil {116 fmt.Fprintf(os.Stderr, "go: writing action graph: %v\n", err)117 base.SetExitStatus(1)118 }119 }120 }121 writeActionGraph()122123 b.readySema = make(chan bool, len(all))124125 // Initialize per-action execution state.126 for _, a := range all {127 for _, a1 := range a.Deps {128 a1.triggers = append(a1.triggers, a)129 }130 a.pending = len(a.Deps)131 if a.pending == 0 {132 b.ready.push(a)133 b.readySema <- true134 }135 }136137 // Handle runs a single action and takes care of triggering138 // any actions that are runnable as a result.139 handle := func(ctx context.Context, a *Action) {140 if a.json != nil {141 a.json.TimeStart = time.Now()142 }143 var err error144 if a.Actor != nil && (a.Failed == nil || a.IgnoreFail) {145 // TODO(matloob): Better action descriptions146 desc := "Executing action (" + a.Mode147 if a.Package != nil {148 desc += " " + a.Package.Desc()149 }150 desc += ")"151 ctx, span := trace.StartSpan(ctx, desc)152 a.traceSpan = span153 for _, d := range a.Deps {154 trace.Flow(ctx, d.traceSpan, a.traceSpan)155 }156 err = a.Actor.Act(b, ctx, a)157 span.Done()158 }159 if a.json != nil {160 a.json.TimeDone = time.Now()161 }162163 // The actions run in parallel but all the updates to the164 // shared work state are serialized through b.exec.165 b.exec.Lock()166 defer b.exec.Unlock()167168 if err != nil {169 if b.AllowErrors && a.Package != nil {170 if a.Package.Error == nil {171 a.Package.Error = &load.PackageError{Err: err}172 a.Package.Incomplete = true173 }174 } else {175 if a.Package != nil {176 if ipe, ok := errors.AsType[load.ImportPathError](err); !ok || ipe.ImportPath() != a.Package.ImportPath {177 err = fmt.Errorf("%s: %v", a.Package.ImportPath, err)178 }179 }180 sh := b.Shell(a)181 sh.Errorf("%s", err)182 }183 if a.Failed == nil {184 a.Failed = a185 }186 }187188 for _, a0 := range a.triggers {189 if a.Failed != nil {190 if a0.Mode == "test barrier" {191 // If this action was triggered by a test, there192 // will be a test barrier action in between the test193 // and the true trigger. But there will be other194 // triggers that are other barriers that are waiting195 // for this one. Propagate the failure to the true196 // trigger, but not to the other barriers.197 for _, bt := range a0.triggers {198 if bt.Mode != "test barrier" {199 bt.Failed = a.Failed200 }201 }202 } else {203 a0.Failed = a.Failed204 }205 }206 if a0.pending--; a0.pending == 0 {207 b.ready.push(a0)208 b.readySema <- true209 }210 }211212 if a == root {213 close(b.readySema)214 }215 }216217 var wg sync.WaitGroup218219 // Kick off goroutines according to parallelism.220 // If we are using the -n flag (just printing commands)221 // drop the parallelism to 1, both to make the output222 // deterministic and because there is no real work anyway.223 par := cfg.BuildP224 if cfg.BuildN {225 par = 1226 }227 for i := 0; i < par; i++ {228 wg.Add(1)229 go func() {230 ctx := trace.StartGoroutine(ctx)231 defer wg.Done()232 for {233 select {234 case _, ok := <-b.readySema:235 if !ok {236 return237 }238 // Receiving a value from b.readySema entitles239 // us to take from the ready queue.240 b.exec.Lock()241 a := b.ready.pop()242 b.exec.Unlock()243 handle(ctx, a)244 case <-base.Interrupted:245 base.SetExitStatus(1)246 return247 }248 }249 }()250 }251252 wg.Wait()253254 if tokens != totalTokens || concurrentProcesses != 0 {255 base.Fatalf("internal error: tokens not restored at end of build: tokens: %d, totalTokens: %d, concurrentProcesses: %d",256 tokens, totalTokens, concurrentProcesses)257 }258259 // Write action graph again, this time with timing information.260 writeActionGraph()261}262263// buildActionID computes the action ID for a build action.264func (b *Builder) buildActionID(a *Action) cache.ActionID {265 p := a.Package266 h := cache.NewHash("build " + p.ImportPath)267268 // Configuration independent of compiler toolchain.269 // Note: buildmode has already been accounted for in buildGcflags270 // and should not be inserted explicitly. Most buildmodes use the271 // same compiler settings and can reuse each other's results.272 // If not, the reason is already recorded in buildGcflags.273 fmt.Fprintf(h, "compile\n")274275 b.addPackageOrigin(h, p)276277 if p.Module != nil {278 fmt.Fprintf(h, "go %s\n", p.Module.GoVersion)279 }280 fmt.Fprintf(h, "goos %s goarch %s\n", cfg.Goos, cfg.Goarch)281 fmt.Fprintf(h, "import %q\n", p.ImportPath)282 fmt.Fprintf(h, "omitdebug %v standard %v local %v prefix %q\n", p.Internal.OmitDebug, p.Standard, p.Internal.Local, p.Internal.LocalPrefix)283 if cfg.BuildTrimpath {284 fmt.Fprintln(h, "trimpath")285 }286 if p.Internal.ForceLibrary {287 fmt.Fprintf(h, "forcelibrary\n")288 }289 b.addCToolchainIDs(h, p)290 if p.Internal.Cover.Mode != "" {291 fmt.Fprintf(h, "cover %q %q\n", p.Internal.Cover.Mode, b.toolID("cover"))292 }293 if p.Internal.FuzzInstrument {294 if fuzzFlags := fuzzInstrumentFlags(); fuzzFlags != nil {295 fmt.Fprintf(h, "fuzz %q\n", fuzzFlags)296 }297 }298 if p.Internal.BuildInfo != nil {299 fmt.Fprintf(h, "modinfo %q\n", p.Internal.BuildInfo.String())300 }301302 // Configuration specific to compiler toolchain.303 switch cfg.BuildToolchainName {304 default:305 base.Fatalf("buildActionID: unknown build toolchain %q", cfg.BuildToolchainName)306 case "gc":307 fmt.Fprintf(h, "compile %s %q %q\n", b.toolID("compile"), forcedGcflags, p.Internal.Gcflags)308 if len(p.SFiles) > 0 {309 fmt.Fprintf(h, "asm %q %q %q\n", b.toolID("asm"), forcedAsmflags, p.Internal.Asmflags)310 }311312 // GOARM, GOMIPS, etc.313 key, val, _ := cfg.GetArchEnv()314 fmt.Fprintf(h, "%s=%s\n", key, val)315316 if cfg.CleanGOEXPERIMENT != "" {317 fmt.Fprintf(h, "GOEXPERIMENT=%q\n", cfg.CleanGOEXPERIMENT)318 }319320 // TODO(rsc): Convince compiler team not to add more magic environment variables,321 // or perhaps restrict the environment variables passed to subprocesses.322 // Because these are clumsy, undocumented special-case hacks323 // for debugging the compiler, they are not settable using 'go env -w',324 // and so here we use os.Getenv, not cfg.Getenv.325 magic := []string{326 "GOCLOBBERDEADHASH",327 "GOSSAFUNC",328 "GOSSADIR",329 "GOCOMPILEDEBUG",330 }331 for _, env := range magic {332 if x := os.Getenv(env); x != "" {333 fmt.Fprintf(h, "magic %s=%s\n", env, x)334 }335 }336337 case "gccgo":338 id, _, err := b.gccgoToolID(BuildToolchain.compiler(), "go")339 if err != nil {340 base.Fatalf("%v", err)341 }342 fmt.Fprintf(h, "compile %s %q %q\n", id, forcedGccgoflags, p.Internal.Gccgoflags)343 fmt.Fprintf(h, "pkgpath %s\n", gccgoPkgpath(p))344 fmt.Fprintf(h, "ar %q\n", BuildToolchain.(gccgoToolchain).ar())345 if len(p.SFiles) > 0 {346 id, _, _ = b.gccgoToolID(BuildToolchain.compiler(), "assembler-with-cpp")347 // Ignore error; different assembler versions348 // are unlikely to make any difference anyhow.349 fmt.Fprintf(h, "asm %q\n", id)350 }351 }352353 // Input files.354 // TODO(matloob): once the build action depends on the cgo actions, we can355 // use those actions' outputs instead of the file names and hashes.356 inputFiles := str.StringList(357 p.GoFiles,358 p.CgoFiles,359 p.CFiles,360 p.CXXFiles,361 p.FFiles,362 p.MFiles,363 p.HFiles,364 p.SFiles,365 p.SysoFiles,366 p.SwigFiles,367 p.SwigCXXFiles,368 p.EmbedFiles,369 )370 for _, file := range inputFiles {371 fmt.Fprintf(h, "file %s %s\n", file, b.fileHash(filepath.Join(p.Dir, file)))372 }373 for _, a1 := range a.Deps {374 p1 := a1.Package375 if p1 != nil && p1 != p { // p can show up in its own action deps in a cache or cgo action376 fmt.Fprintf(h, "import %s %s\n", p1.ImportPath, contentID(a1.buildID))377 }378 if a1.Mode == "preprocess PGO profile" {379 fmt.Fprintf(h, "pgofile %s\n", b.fileHash(a1.built))380 }381 }382383 return h.Sum()384}385386// addPackageOrigin writes information about the origin of the package that387// may be embedded in the debug info for the object file. It is used by388// buildActionID.389func (b *Builder) addPackageOrigin(h io.Writer, p *load.Package) {390 if cfg.BuildTrimpath {391 // When -trimpath is used with a package built from the module cache,392 // its debug information refers to the module path and version393 // instead of the directory.394 if p.Module != nil {395 fmt.Fprintf(h, "module %s@%s\n", p.Module.Path, p.Module.Version)396 }397 } else if p.Goroot {398 // The Go compiler always hides the exact value of $GOROOT399 // when building things in GOROOT.400 //401 // The C compiler does not, but for packages in GOROOT we rewrite the path402 // as though -trimpath were set. This used to be so that we did not invalidate403 // the build cache (and especially precompiled archive files) when changing404 // GOROOT_FINAL, but we no longer ship precompiled archive files as of Go 1.20405 // (https://go.dev/issue/47257) and no longer support GOROOT_FINAL406 // (https://go.dev/issue/62047).407 // TODO(bcmills): Figure out whether this behavior is still useful.408 //409 // b.WorkDir is always either trimmed or rewritten to410 // the literal string "/tmp/go-build".411 } else if !strings.HasPrefix(p.Dir, b.WorkDir) {412 // -trimpath is not set and no other rewrite rules apply,413 // so the object file may refer to the absolute directory414 // containing the package.415 fmt.Fprintf(h, "dir %s\n", p.Dir)416 }417}418419// addCToolchainIDs adds the C toolchain hashes and flags to the hash writer.420// It is used by buildActionID.421func (b *Builder) addCToolchainIDs(h io.Writer, p *load.Package) {422 if len(p.CgoFiles)+len(p.SwigFiles)+len(p.SwigCXXFiles) > 0 {423 fmt.Fprintf(h, "cgo %q\n", b.toolID("cgo"))424 cppflags, cflags, cxxflags, fflags, ldflags, _ := b.CFlags(p)425426 ccExe := b.ccExe()427 fmt.Fprintf(h, "CC=%q %q %q %q\n", ccExe, cppflags, cflags, ldflags)428 // Include the C compiler tool ID so that if the C429 // compiler changes we rebuild the package.430 if ccID, _, err := b.gccToolID(ccExe[0], "c"); err == nil {431 fmt.Fprintf(h, "CC ID=%q\n", ccID)432 } else {433 fmt.Fprintf(h, "CC ID ERROR=%q\n", err)434 }435 if len(p.CXXFiles)+len(p.SwigCXXFiles) > 0 {436 cxxExe := b.cxxExe()437 fmt.Fprintf(h, "CXX=%q %q\n", cxxExe, cxxflags)438 if cxxID, _, err := b.gccToolID(cxxExe[0], "c++"); err == nil {439 fmt.Fprintf(h, "CXX ID=%q\n", cxxID)440 } else {441 fmt.Fprintf(h, "CXX ID ERROR=%q\n", err)442 }443 }444 if len(p.FFiles) > 0 {445 fcExe := b.fcExe()446 fmt.Fprintf(h, "FC=%q %q\n", fcExe, fflags)447 if fcID, _, err := b.gccToolID(fcExe[0], "f95"); err == nil {448 fmt.Fprintf(h, "FC ID=%q\n", fcID)449 } else {450 fmt.Fprintf(h, "FC ID ERROR=%q\n", err)451 }452 }453 // TODO(rsc): Should we include the SWIG version?454 }455}456457// allowedVersion reports whether the version v is an allowed version of go458// (one that we can compile).459// v is known to be of the form "1.23".460func allowedVersion(v string) bool {461 // Special case: no requirement.462 if v == "" {463 return true464 }465 return gover.Compare(gover.Local(), v) >= 0466}467468func (b *Builder) computeNonGoOverlay(a *Action, p *load.Package, sh *Shell, objdir string, nonGoFileLists [][]string) error {469OverlayLoop:470 for _, fs := range nonGoFileLists {471 for _, f := range fs {472 if fsys.Replaced(mkAbs(p.Dir, f)) {473 a.nonGoOverlay = make(map[string]string)474 break OverlayLoop475 }476 }477 }478 if a.nonGoOverlay != nil {479 for _, fs := range nonGoFileLists {480 for i := range fs {481 from := mkAbs(p.Dir, fs[i])482 dst := objdir + filepath.Base(fs[i])483 if err := sh.CopyFile(dst, fsys.Actual(from), 0666, false); err != nil {484 return err485 }486 a.nonGoOverlay[from] = dst487 }488 }489 }490491 return nil492}493494func (b *Builder) runCover(ctx context.Context, a *Action) error {495 p := a.Package496 sh := b.Shell(a)497498 // Determine the covmeta file name.499 var covMetaFileName string500 if a.Package.Internal.Cover.GenMeta {501 covMetaFileName = a.Objdir + covcmd.MetaFileForPackage(a.Package.ImportPath)502 }503504 if err := sh.Mkdir(a.Objdir); err != nil {505 return err506 }507508 a.actionID = b.coverActionID(a, covMetaFileName)509 if pr, err := b.loadCachedCoverOutputs(a); err == nil {510 a.Provider = pr511 return nil512 }513514 gofiles := slices.Clone(a.Package.GoFiles)515 cgofiles := slices.Clone(a.Package.CgoFiles)516517 outfiles := []string{}518 infiles := []string{}519 for i, file := range str.StringList(gofiles, cgofiles) {520 if base.IsTestFile(file) {521 continue // Not covering this file.522 }523524 var sourceFile string525 var coverFile string526 if base, found := strings.CutSuffix(file, ".cgo1.go"); found {527 // cgo files have absolute paths528 base = filepath.Base(base)529 sourceFile = file530 coverFile = a.Objdir + base + ".cgo1.go"531 } else {532 sourceFile = filepath.Join(p.Dir, file)533 coverFile = a.Objdir + file534 }535 coverFile = strings.TrimSuffix(coverFile, ".go") + ".cover.go"536 infiles = append(infiles, sourceFile)537 outfiles = append(outfiles, coverFile)538 if i < len(gofiles) {539 gofiles[i] = coverFile540 } else {541 cgofiles[i-len(gofiles)] = coverFile542 }543 }544545 var coverCfg string546 if len(infiles) != 0 {547 // Coverage instrumentation creates new top level548 // variables in the target package for things like549 // meta-data containers, counter vars, etc. To avoid550 // collisions with user variables, suffix the var name551 // with 12 hex digits from the SHA-256 hash of the552 // import path. Choice of 12 digits is historical/arbitrary,553 // we just need enough of the hash to avoid accidents,554 // as opposed to precluding determined attempts by555 // users to break things.556 sum := sha256.Sum256([]byte(a.Package.ImportPath))557 coverVar := fmt.Sprintf("goCover_%x_", sum[:6])558 mode := a.Package.Internal.Cover.Mode559 if mode == "" {560 panic("covermode should be set at this point")561 }562 coverCfg = a.Objdir + "coveragecfg"563 if newoutfiles, err := b.cover(a, infiles, outfiles, coverVar, mode, covMetaFileName, coverCfg); err != nil {564 return err565 } else {566 outfiles = newoutfiles567 gofiles = append([]string{newoutfiles[0]}, gofiles...)568 }569 }570571 pr := &coverProvider{covMetaFileName, coverCfg, gofiles, cgofiles}572 a.Provider = pr573574 if !cfg.BuildN {575 if err := b.cacheCoverOutputs(a, pr); err != nil {576 return err577 }578 }579580 return nil581}582583// build is the action for building a single package.584// Note that any new influence on this logic must be reported in b.buildActionID above as well.585func (b *Builder) build(ctx context.Context, a *Action) (err error) {586 p := a.Package587 sh := b.Shell(a)588589 bit := func(x uint32, b bool) uint32 {590 if b {591 return x592 }593 return 0594 }595596 const (597 needBuild uint32 = 1 << iota598 needVet599 needCompiledGoFiles600 )601602 cachedBuild := false603 need := bit(needBuild, !b.IsCmdList && a.needBuild || b.NeedExport) |604 bit(needVet, a.needVet) |605 bit(needCompiledGoFiles, b.NeedCompiledGoFiles)606607 if b.useCache(a, b.buildActionID(a), p.Target, need&needBuild != 0) {608 // We found the main output in the cache.609 // If we don't need any other outputs, we can stop.610 // Otherwise, we need to write files to a.Objdir (needVet).611 // Remember that we might have them in cache612 // and check again after we create a.Objdir.613 cachedBuild = true614 a.output = []byte{} // start saving output in case we miss any cache results615 need &^= needBuild616 if b.NeedExport {617 p.Export = a.built618 p.BuildID = a.buildID619 }620 if need&needCompiledGoFiles != 0 {621 if err := b.loadCachedCompiledGoFiles(a); err == nil {622 need &^= needCompiledGoFiles623 }624 }625 }626627 // Source files might be cached, even if the full action is not628 // (e.g., go list -compiled -find).629 if !cachedBuild && need&needCompiledGoFiles != 0 {630 if err := b.loadCachedCompiledGoFiles(a); err == nil {631 need &^= needCompiledGoFiles632 }633 }634635 if need == 0 {636 return nil637 }638 defer b.flushOutput(a)639640 defer func() {641 if err != nil && b.IsCmdList && b.NeedError && p.Error == nil {642 p.Error = &load.PackageError{Err: err}643 }644 }()645646 if p.Error != nil {647 // Don't try to build anything for packages with errors. There may be a648 // problem with the inputs that makes the package unsafe to build.649 return p.Error650 }651652 if p.Module != nil && !allowedVersion(p.Module.GoVersion) {653 return errors.New("module requires Go " + p.Module.GoVersion + " or later")654 }655656 if err := b.checkDirectives(a); err != nil {657 return err658 }659660 if err := sh.Mkdir(a.Objdir); err != nil {661 return err662 }663664 // Load cached vet config, but only if that's all we have left665 // (need == needVet, not testing just the one bit).666 // If we are going to do a full build anyway,667 // we're going to regenerate the files in the build action anyway.668 if need == needVet {669 if err := b.loadCachedVet(a, a.Deps); err == nil {670 need &^= needVet671 }672 }673674 var coverPr *coverProvider675 var runCgoPr *runCgoProvider676 for _, dep := range a.Deps {677 switch pr := dep.Provider.(type) {678 case *coverProvider:679 coverPr = pr680 case *runCgoProvider:681 runCgoPr = pr682 }683 }684685 if need == 0 {686 return687 }688 defer b.flushOutput(a)689690 if cfg.BuildN {691 // In -n mode, print a banner between packages.692 // The banner is five lines so that when changes to693 // different sections of the bootstrap script have to694 // be merged, the banners give patch something695 // to use to find its context.696 sh.Printf("\n#\n# %s\n#\n\n", p.ImportPath)697 }698699 if cfg.BuildV {700 sh.Printf("%s\n", p.ImportPath)701 }702703 objdir := a.Objdir704705 if err := AllowInstall(a); err != nil {706 return err707 }708709 // make target directory710 dir, _ := filepath.Split(a.Target)711 if dir != "" {712 if err := sh.Mkdir(dir); err != nil {713 return err714 }715 }716717 gofiles := str.StringList(p.GoFiles)718 cfiles := str.StringList(p.CFiles)719 sfiles := str.StringList(p.SFiles)720 var objects, cgoObjects []string721722 // If we're doing coverage, preprocess the .go files and put them in the work directory723 if p.Internal.Cover.Mode != "" {724 gofiles = coverPr.goSources725 }726727 if p.UsesCgo() || p.UsesSwig() {728 if runCgoPr == nil {729 base.Fatalf("internal error: could not find runCgoProvider")730 }731732 // In a package using cgo, cgo compiles the C, C++ and assembly files with gcc.733 // There is one exception: runtime/cgo's job is to bridge the734 // cgo and non-cgo worlds, so it necessarily has files in both.735 // In that case gcc only gets the gcc_* files.736 cfiles = nil737 if p.Standard && p.ImportPath == "runtime/cgo" {738 // filter to the non-gcc files.739 i := 0740 for _, f := range sfiles {741 if !strings.HasPrefix(f, "gcc_") {742 sfiles[i] = f743 i++744 }745 }746 sfiles = sfiles[:i]747 } else {748 sfiles = nil749 }750 outGo, outObj, err := b.processCgoOutputs(a, runCgoPr, base.Tool("cgo"), objdir)751752 if err != nil {753 return err754 }755 if cfg.BuildToolchainName == "gccgo" {756 cgoObjects = append(cgoObjects, a.Objdir+"_cgo_flags")757 }758 cgoObjects = append(cgoObjects, outObj...)759 gofiles = append(gofiles, outGo...)760 }761762 var srcfiles []string // .go and non-.go763 srcfiles = append(srcfiles, gofiles...)764 srcfiles = append(srcfiles, sfiles...)765 srcfiles = append(srcfiles, cfiles...)766 b.cacheSrcFiles(a, srcfiles)767768 // Sanity check only, since Package.load already checked as well.769 if len(gofiles) == 0 {770 return &load.NoGoError{Package: p}771 }772773 // Prepare Go vet config if needed.774 if need&needVet != 0 {775 buildVetConfig(a, srcfiles, a.Deps)776 need &^= needVet777 }778 if need&needCompiledGoFiles != 0 {779 if err := b.loadCachedCompiledGoFiles(a); err != nil {780 return fmt.Errorf("loading compiled Go files from cache: %w", err)781 }782 need &^= needCompiledGoFiles783 }784 if need == 0 {785 // Nothing left to do.786 return nil787 }788789 // Collect symbol ABI requirements from assembly.790 symabis, err := BuildToolchain.symabis(b, a, sfiles)791 if err != nil {792 return err793 }794795 // Prepare Go import config.796 // We start it off with a comment so it can't be empty, so icfg.Bytes() below is never nil.797 // It should never be empty anyway, but there have been bugs in the past that resulted798 // in empty configs, which then unfortunately turn into "no config passed to compiler",799 // and the compiler falls back to looking in pkg itself, which mostly works,800 // except when it doesn't.801 var icfg bytes.Buffer802 fmt.Fprintf(&icfg, "# import config\n")803 for i, raw := range p.Internal.RawImports {804 final := p.Imports[i]805 if final != raw {806 fmt.Fprintf(&icfg, "importmap %s=%s\n", raw, final)807 }808 }809 for _, a1 := range a.Deps {810 p1 := a1.Package811 if p1 == nil || p1.ImportPath == "" || a1.built == "" {812 continue813 }814 fmt.Fprintf(&icfg, "packagefile %s=%s\n", p1.ImportPath, a1.built)815 }816817 // Prepare Go embed config if needed.818 // Unlike the import config, it's okay for the embed config to be empty.819 var embedcfg []byte820 if len(p.Internal.Embed) > 0 {821 var embed struct {822 Patterns map[string][]string823 Files map[string]string824 }825 embed.Patterns = p.Internal.Embed826 embed.Files = make(map[string]string)827 for _, file := range p.EmbedFiles {828 embed.Files[file] = fsys.Actual(filepath.Join(p.Dir, file))829 }830 js, err := json.MarshalIndent(&embed, "", "\t")831 if err != nil {832 return fmt.Errorf("marshal embedcfg: %v", err)833 }834 embedcfg = js835 }836837 // Find PGO profile if needed.838 var pgoProfile string839 for _, a1 := range a.Deps {840 if a1.Mode != "preprocess PGO profile" {841 continue842 }843 if pgoProfile != "" {844 return fmt.Errorf("action contains multiple PGO profile dependencies")845 }846 pgoProfile = a1.built847 }848849 var coverageConfig string850 if coverPr != nil {851 coverageConfig = coverPr.coverageConfig852 }853854 if p.Internal.BuildInfo != nil && cfg.ModulesEnabled {855 prog := modload.ModInfoProg(p.Internal.BuildInfo.String(), cfg.BuildToolchainName == "gccgo")856 if len(prog) > 0 {857 if err := sh.writeFile(objdir+"_gomod_.go", prog); err != nil {858 return err859 }860 gofiles = append(gofiles, objdir+"_gomod_.go")861 }862 }863864 // Compile Go.865 objpkg := objdir + "_pkg_.a"866 ofile, out, err := BuildToolchain.gc(b, a, objpkg, icfg.Bytes(), embedcfg, symabis, len(sfiles) > 0, pgoProfile, coverageConfig, gofiles)867 if len(out) > 0 && (p.UsesCgo() || p.UsesSwig()) && !cfg.BuildX {868 // Fix up output referring to cgo-generated code to be more readable.869 // Replace *[100]_Ctype_foo with *[100]C.foo.870 // If we're using -x, assume we're debugging and want the full dump, so disable the rewrite.871 out = cgoTypeSigRe.ReplaceAll(out, []byte("C."))872 }873 if err := sh.reportCmd("", "", out, err); err != nil {874 return err875 }876 if ofile != objpkg {877 objects = append(objects, ofile)878 }879880 // Copy .h files named for goos or goarch or goos_goarch881 // to names using GOOS and GOARCH.882 // For example, defs_linux_amd64.h becomes defs_GOOS_GOARCH.h.883 _goos_goarch := "_" + cfg.Goos + "_" + cfg.Goarch884 _goos := "_" + cfg.Goos885 _goarch := "_" + cfg.Goarch886 for _, file := range p.HFiles {887 name, ext := fileExtSplit(file)888 switch {889 case strings.HasSuffix(name, _goos_goarch):890 targ := file[:len(name)-len(_goos_goarch)] + "_GOOS_GOARCH." + ext891 if err := sh.CopyFile(objdir+targ, filepath.Join(p.Dir, file), 0666, true); err != nil {892 return err893 }894 case strings.HasSuffix(name, _goarch):895 targ := file[:len(name)-len(_goarch)] + "_GOARCH." + ext896 if err := sh.CopyFile(objdir+targ, filepath.Join(p.Dir, file), 0666, true); err != nil {897 return err898 }899 case strings.HasSuffix(name, _goos):900 targ := file[:len(name)-len(_goos)] + "_GOOS." + ext901 if err := sh.CopyFile(objdir+targ, filepath.Join(p.Dir, file), 0666, true); err != nil {902 return err903 }904 }905 }906907 if err := b.computeNonGoOverlay(a, p, sh, objdir, [][]string{cfiles}); err != nil {908 return err909 }910911 // Compile C files in a package being built with gccgo. We disallow912 // C files when compiling with gc unless swig or cgo is used.913 for _, file := range cfiles {914 out := file[:len(file)-len(".c")] + ".o"915 if err := BuildToolchain.cc(b, a, objdir+out, file); err != nil {916 return err917 }918 objects = append(objects, out)919 }920921 // Assemble .s files.922 if len(sfiles) > 0 {923 ofiles, err := BuildToolchain.asm(b, a, sfiles)924 if err != nil {925 return err926 }927 objects = append(objects, ofiles...)928 }929930 // For gccgo on ELF systems, we write the build ID as an assembler file.931 // This lets us set the SHF_EXCLUDE flag.932 // This is read by readGccgoArchive in cmd/internal/buildid/buildid.go.933 if a.buildID != "" && cfg.BuildToolchainName == "gccgo" {934 switch cfg.Goos {935 case "aix", "android", "dragonfly", "freebsd", "illumos", "linux", "netbsd", "openbsd", "solaris":936 asmfile, err := b.gccgoBuildIDFile(a)937 if err != nil {938 return err939 }940 ofiles, err := BuildToolchain.asm(b, a, []string{asmfile})941 if err != nil {942 return err943 }944 objects = append(objects, ofiles...)945 }946 }947948 // NOTE(rsc): On Windows, it is critically important that the949 // gcc-compiled objects (cgoObjects) be listed after the ordinary950 // objects in the archive. I do not know why this is.951 // https://golang.org/issue/2601952 objects = append(objects, cgoObjects...)953954 // Add system object files.955 for _, syso := range p.SysoFiles {956 objects = append(objects, filepath.Join(p.Dir, syso))957 }958959 // Pack into archive in objdir directory.960 // If the Go compiler wrote an archive, we only need to add the961 // object files for non-Go sources to the archive.962 // If the Go compiler wrote an archive and the package is entirely963 // Go sources, there is no pack to execute at all.964 if len(objects) > 0 {965 if err := BuildToolchain.pack(b, a, objpkg, objects); err != nil {966 return err967 }968 }969970 if err := b.updateBuildID(a, objpkg); err != nil {971 return err972 }973974 a.built = objpkg975 return nil976}977978var cgoTypeSigRe = lazyregexp.New(`\b_C2?(type|func|var|macro)_\B`)979980func (b *Builder) checkDirectives(a *Action) error {981 var msg []byte982 p := a.Package983 var seen map[string]token.Position984 for _, d := range p.Internal.Build.Directives {985 if strings.HasPrefix(d.Text, "//go:debug") {986 key, _, err := load.ParseGoDebug(d.Text)987 if err != nil && err != load.ErrNotGoDebug {988 msg = fmt.Appendf(msg, "%s: invalid //go:debug: %v\n", d.Pos, err)989 continue990 }991 if pos, ok := seen[key]; ok {992 msg = fmt.Appendf(msg, "%s: repeated //go:debug for %v\n\t%s: previous //go:debug\n", d.Pos, key, pos)993 continue994 }995 if seen == nil {996 seen = make(map[string]token.Position)997 }998 seen[key] = d.Pos999 }1000 }1001 if len(msg) > 0 {1002 // We pass a non-nil error to reportCmd to trigger the failure reporting1003 // path, but the content of the error doesn't matter because msg is1004 // non-empty.1005 err := errors.New("invalid directive")1006 return b.Shell(a).reportCmd("", "", msg, err)1007 }1008 return nil1009}10101011func (b *Builder) cacheObjdirFile(a *Action, c cache.Cache, name string) error {1012 f, err := os.Open(a.Objdir + name)1013 if err != nil {1014 return err1015 }1016 defer f.Close()1017 _, _, err = c.Put(cache.Subkey(a.actionID, name), f)1018 return err1019}10201021func (b *Builder) findCachedObjdirFile(a *Action, c cache.Cache, name string) (string, error) {1022 file, _, err := cache.GetFile(c, cache.Subkey(a.actionID, name))1023 if err != nil {1024 return "", fmt.Errorf("loading cached file %s: %w", name, err)1025 }1026 return file, nil1027}10281029func (b *Builder) loadCachedObjdirFile(a *Action, c cache.Cache, name string) error {1030 cached, err := b.findCachedObjdirFile(a, c, name)1031 if err != nil {1032 return err1033 }1034 return b.Shell(a).CopyFile(a.Objdir+name, cached, 0666, true)1035}10361037func (b *Builder) cacheSrcFiles(a *Action, srcfiles []string) {1038 c := cache.Default()1039 var buf bytes.Buffer1040 for _, file := range srcfiles {1041 if !strings.HasPrefix(file, a.Objdir) {1042 // not generated1043 buf.WriteString("./")1044 buf.WriteString(file)1045 buf.WriteString("\n")1046 continue1047 }1048 name := file[len(a.Objdir):]1049 buf.WriteString(name)1050 buf.WriteString("\n")1051 if err := b.cacheObjdirFile(a, c, name); err != nil {1052 return1053 }1054 }1055 cache.PutBytes(c, cache.Subkey(a.actionID, "srcfiles"), buf.Bytes())1056}10571058// coverProviderCached is the structure we'll use to represent a coverProvider1059// in the action cache. It has the same fields as a coverProvider but they'll contain1060// different contents: we replace the acutal objdir name with $OBJDIR so that1061// the cached provider doesn't depend on the objdir used.1062type coverProviderCached struct {1063 CovMetaFile string1064 CoverageConfig string1065 GoSources, CgoSources []string1066}10671068func (b *Builder) cacheCoverOutputs(a *Action, pr *coverProvider) error {1069 c := cache.Default()10701071 cacheSrcFileName := func(a *Action, file string) string {1072 if name, ok := strings.CutPrefix(file, a.Objdir); ok {1073 return name1074 }1075 return "./" + file1076 }10771078 b.cacheSrcFiles(a, str.StringList(pr.goSources, pr.cgoSources))1079 var cached coverProviderCached1080 if pr.covMetaFileName != "" {1081 cached.CovMetaFile = strings.TrimPrefix(pr.covMetaFileName, a.Objdir)1082 if err := b.cacheObjdirFile(a, c, cached.CovMetaFile); err != nil {1083 return err1084 }1085 }1086 if pr.coverageConfig != "" {1087 cached.CoverageConfig = strings.TrimPrefix(pr.coverageConfig, a.Objdir)1088 if err := b.cacheObjdirFile(a, c, cached.CoverageConfig); err != nil {1089 return err1090 }1091 }10921093 for _, fn := range pr.goSources {1094 cached.GoSources = append(cached.GoSources, cacheSrcFileName(a, fn))1095 }1096 for _, fn := range pr.cgoSources {1097 cached.CgoSources = append(cached.CgoSources, cacheSrcFileName(a, fn))1098 }1099 js, err := json.Marshal(cached)1100 if err != nil {1101 return err1102 }1103 cache.PutBytes(c, cache.Subkey(a.actionID, "coverprovider"), js)11041105 return nil1106}11071108func (b *Builder) loadCachedCoverOutputs(a *Action) (*coverProvider, error) {1109 c := cache.Default()11101111 if _, err := b.loadCachedSrcFiles(a); err != nil {1112 return nil, err1113 }11141115 var cached coverProviderCached1116 if js, _, err := cache.GetBytes(c, cache.Subkey(a.actionID, "coverprovider")); err != nil {1117 return nil, err1118 } else if err := json.Unmarshal(js, &cached); err != nil {1119 return nil, err1120 }11211122 covMetaFile := cached.CovMetaFile1123 if covMetaFile != "" {1124 if err := b.loadCachedObjdirFile(a, c, covMetaFile); err != nil {1125 return nil, err1126 }1127 covMetaFile = a.Objdir + covMetaFile1128 }1129 coverageConfig := cached.CoverageConfig1130 if coverageConfig != "" {1131 if err := b.loadCachedObjdirFile(a, c, coverageConfig); err != nil {1132 return nil, err1133 }1134 coverageConfig = a.Objdir + coverageConfig1135 }11361137 var goSources, cgoSources []string1138 for _, file := range cached.GoSources {1139 name, ok := strings.CutPrefix(file, "./")1140 if !ok {1141 name = a.Objdir + name1142 }1143 goSources = append(goSources, name)1144 }1145 for _, file := range cached.CgoSources {1146 name, ok := strings.CutPrefix(file, "./")1147 if !ok {1148 name = a.Objdir + name1149 }1150 cgoSources = append(cgoSources, name)1151 }11521153 pr := &coverProvider{1154 covMetaFileName: covMetaFile,1155 coverageConfig: coverageConfig,1156 goSources: goSources,1157 cgoSources: cgoSources,1158 }11591160 return pr, nil1161}11621163func (b *Builder) coverActionID(a *Action, covMetaFileName string) cache.ActionID {1164 p := a.Package1165 h := cache.NewHash("cover " + p.ImportPath)1166 fmt.Fprintf(h, "cover %q\n", b.toolID("cover"))1167 b.addPackageOrigin(h, p)11681169 // Input files for cover.1170 fmt.Fprintf(h, "setup %s %v\n", p.Internal.Cover.Mode, p.Internal.Cover.GenMeta)1171 fmt.Fprintf(h, "config ")1172 if err := json.NewEncoder(h).Encode(coverConfig(p, filepath.Base(covMetaFileName), "")); err != nil {1173 base.Fatal(err)1174 }1175 for _, file := range str.StringList(p.GoFiles, p.CgoFiles) {1176 fmt.Fprintf(h, "file %s %s\n", file, b.fileHash(filepath.Join(p.Dir, file)))1177 }11781179 return h.Sum()1180}11811182// cgoCompileActionID returns the action ID for the action to compile1183// a file with the C compiler for cgo.1184func (b *Builder) cgoCompileActionID(a *Action, f string, flags []string) cache.ActionID {1185 h := cache.NewHash("cgo compile file")1186 p := a.Package11871188 fmt.Fprintf(h, "goos %s goarch %s\n", cfg.Goos, cfg.Goarch)11891190 fmt.Fprintf(h, "file %s %s\n", filepath.Base(f), b.fileHash(f))1191 fmt.Fprintf(h, "flags %q\n", replaceAll(flags, a.Objdir, "$OBJDIR/"))11921193 // Package origin, trimpath and Goroot affect the -ffile-prefix-map1194 // flag added during C compilation, which changes the DWARF paths1195 // in the resulting .o file.1196 b.addPackageOrigin(h, p)1197 if cfg.BuildTrimpath {1198 fmt.Fprintln(h, "trimpath")1199 }12001201 // This has too much information.1202 // We only need to choose CC/CXX/FC based on which kind of compilation this1203 // action is doing. Perhaps we could add a getCCIDFunc to cgoCompileActor?1204 // It also includes the flags which are be unnecessary because we know which1205 // flags are passed.1206 b.addCToolchainIDs(h, p)12071208 return h.Sum()1209}12101211// cgoRunActionID returns the ActionID for the action that runs1212// the cgo command.1213func (b *Builder) cgoRunActionID(a *Action) cache.ActionID {1214 p := a.Package1215 h := cache.NewHash("cgo " + p.ImportPath)12161217 fmt.Fprintf(h, "goos %s goarch %s\n", cfg.Goos, cfg.Goarch)12181219 fmt.Fprintf(h, "cgo %q\n", b.toolID("cgo"))12201221 // cgo mixes the import path into its generated symbol names1222 // (_cgo_<hash>_Cfunc_*), so the generated files are only valid1223 // for the same import path. The hash name argument of NewHash1224 // is debug-only, so the import path must also be written here.1225 fmt.Fprintf(h, "import %q\n", p.ImportPath)12261227 // Add p.Dir, which is needed because cgo embeds absolute1228 // source paths in //line directives of its generated files.1229 // TODO(matloob): Can we change that?1230 fmt.Fprintf(h, "dir %s\n", p.Dir)1231 b.addCToolchainIDs(h, p) // incorporates c/c++/fflags12321233 // msan/asan cause -fsanitize to be passed1234 fmt.Fprintf(h, "msan %v asan %v\n", cfg.BuildMSan, cfg.BuildASan)1235 // exportheader signifies whether _cgo_install.h will be produced1236 fmt.Fprintf(h, "exportheader %v\n", cfg.BuildBuildmode == "c-archive" || cfg.BuildBuildmode == "c-shared")1237 // gccgopkgpath affects cgo's produced symbol names1238 if cfg.BuildToolchainName == "gccgo" {1239 fmt.Fprintf(h, "gccgopkgpath %s\n", gccgoPkgpath(p))1240 }12411242 // Whether there are Obj-C/C++/Fortran files will affect the flags produced.1243 fmt.Fprintf(h, "mfiles %v cxxfiles %v ffiles %v\n", len(p.MFiles) > 0, len(p.CXXFiles)+len(p.SwigCXXFiles) > 0, len(p.FFiles) > 0)12441245 // Cgo and swig input files. These are the direct inputs to the cgo tool.1246 if p.Internal.Cover.Mode != "" {1247 // Use cover action id: cover will generate the new cgo files.1248 for _, dep := range a.Deps {1249 if dep.Mode == "cover" {1250 fmt.Fprintf(h, "cover %x\n", dep.actionID)1251 break1252 }1253 }1254 } else {1255 for _, file := range p.CgoFiles {1256 fmt.Fprintf(h, "file %s %s\n", file, b.fileHash(filepath.Join(p.Dir, file)))1257 }1258 }1259 for _, file := range str.StringList(p.SwigFiles, p.SwigCXXFiles) {1260 fmt.Fprintf(h, "file %s %s\n", file, b.fileHash(filepath.Join(p.Dir, file)))1261 }12621263 return h.Sum()1264}12651266// runCgoProviderCached is the JSON-serializable form of a runCgoProvider1267// stored in the action cache. All files are relative to objdir.1268type runCgoProviderCached struct {1269 CFLAGS, CXXFLAGS, FFLAGS, LDFLAGS []string1270 NotCompatibleForInternalLinking bool1271 GoFiles []string // list for the provider1272 Files []string // files to restore to objdir1273}12741275func replaceAll(strs []string, from, to string) []string {1276 var replaced []string1277 for _, s := range strs {1278 replaced = append(replaced, strings.ReplaceAll(s, from, to))1279 }1280 return replaced1281}12821283func (b *Builder) cacheRunCgoOutputs(a *Action, pr *runCgoProvider) error {1284 c := cache.Default()12851286 // TODO(matloob): map on a seq would be nice1287 trimObjdirPrefix := func(files []string) []string {1288 var trimmed []string1289 for _, f := range files {1290 trimmed = append(trimmed, strings.TrimPrefix(f, a.Objdir))1291 }1292 return trimmed1293 }12941295 cgo2Files := func(cgo1Files []string) []string {1296 var cgo2 []string1297 for _, f := range cgo1Files {1298 if base, ok := strings.CutSuffix(f, ".cgo1.go"); ok {1299 cgo2 = append(cgo2, base+".cgo2.c")1300 }1301 }1302 return cgo21303 }13041305 _, outC, outCXX := b.swigOutputs(a.Package, a.Objdir)1306 files := str.StringList(1307 []string{"_cgo_export.c", "_cgo_export.h", "_cgo_main.c"},1308 trimObjdirPrefix(pr.goFiles),1309 trimObjdirPrefix(cgo2Files(pr.goFiles)),1310 trimObjdirPrefix(outC),1311 trimObjdirPrefix(outCXX))1312 for _, name := range []string{"_cgo_install.h", "_cgo_defun.c", "_cgo_flags"} {1313 if _, err := os.Stat(a.Objdir + name); err == nil {1314 files = append(files, name)1315 }1316 }1317 for _, file := range files {1318 if err := b.cacheObjdirFile(a, c, file); err != nil {1319 return err1320 }1321 }13221323 cached := runCgoProviderCached{1324 CFLAGS: replaceAll(pr.CFLAGS, a.Objdir, "$OBJDIR/"),1325 CXXFLAGS: replaceAll(pr.CXXFLAGS, a.Objdir, "$OBJDIR/"),1326 FFLAGS: replaceAll(pr.FFLAGS, a.Objdir, "$OBJDIR/"),1327 LDFLAGS: replaceAll(pr.LDFLAGS, a.Objdir, "$OBJDIR/"),1328 NotCompatibleForInternalLinking: pr.notCompatibleForInternalLinking,1329 GoFiles: trimObjdirPrefix(pr.goFiles),1330 Files: files,1331 }1332 data, err := json.Marshal(cached)1333 if err != nil {1334 return err1335 }1336 cache.PutBytes(c, cache.Subkey(a.actionID, "cgorunprovider"), data)1337 return nil1338}13391340func (b *Builder) loadCachedRunCgoOutputs(a *Action) (*runCgoProvider, error) {1341 c := cache.Default()13421343 var cached runCgoProviderCached1344 js, _, err := cache.GetBytes(c, cache.Subkey(a.actionID, "cgorunprovider"))1345 if err != nil {1346 return nil, err1347 } else if err := json.Unmarshal(js, &cached); err != nil {1348 return nil, err1349 }13501351 for _, name := range cached.Files {1352 if err := b.loadCachedObjdirFile(a, c, name); err != nil {1353 return nil, err1354 }1355 }13561357 var goFilesObjdir []string1358 for _, f := range cached.GoFiles {1359 goFilesObjdir = append(goFilesObjdir, a.Objdir+f)1360 }13611362 pr := &runCgoProvider{1363 CFLAGS: replaceAll(cached.CFLAGS, "$OBJDIR/", a.Objdir),1364 CXXFLAGS: replaceAll(cached.CXXFLAGS, "$OBJDIR/", a.Objdir),1365 FFLAGS: replaceAll(cached.FFLAGS, "$OBJDIR/", a.Objdir),1366 LDFLAGS: replaceAll(cached.LDFLAGS, "$OBJDIR/", a.Objdir),1367 notCompatibleForInternalLinking: cached.NotCompatibleForInternalLinking,1368 goFiles: goFilesObjdir,1369 }13701371 return pr, nil1372}13731374func (b *Builder) loadCachedSrcFiles(a *Action) ([]string, error) {1375 c := cache.Default()1376 list, _, err := cache.GetBytes(c, cache.Subkey(a.actionID, "srcfiles"))1377 if err != nil {1378 return nil, fmt.Errorf("reading srcfiles list: %w", err)1379 }1380 var srcfiles []string1381 for name := range strings.SplitSeq(string(list), "\n") {1382 if name == "" { // end of list1383 continue1384 }1385 if strings.HasPrefix(name, "./") {1386 srcfiles = append(srcfiles, name[2:])1387 continue1388 }1389 if err := b.loadCachedObjdirFile(a, c, name); err != nil {1390 return nil, err1391 }1392 srcfiles = append(srcfiles, a.Objdir+name)1393 }1394 return srcfiles, nil1395}13961397func (b *Builder) loadCachedVet(a *Action, vetDeps []*Action) error {1398 srcfiles, err := b.loadCachedSrcFiles(a)1399 if err != nil {1400 return err1401 }1402 buildVetConfig(a, srcfiles, vetDeps)1403 return nil1404}14051406func (b *Builder) loadCachedCompiledGoFiles(a *Action) error {1407 c := cache.Default()1408 list, _, err := cache.GetBytes(c, cache.Subkey(a.actionID, "srcfiles"))1409 if err != nil {1410 return fmt.Errorf("reading srcfiles list: %w", err)1411 }1412 var gofiles []string1413 for name := range strings.SplitSeq(string(list), "\n") {1414 if name == "" { // end of list1415 continue1416 } else if !strings.HasSuffix(name, ".go") {1417 continue1418 }1419 if strings.HasPrefix(name, "./") {1420 gofiles = append(gofiles, name[len("./"):])1421 continue1422 }1423 file, err := b.findCachedObjdirFile(a, c, name)1424 if err != nil {1425 return fmt.Errorf("finding %s: %w", name, err)1426 }1427 gofiles = append(gofiles, file)1428 }1429 a.Package.CompiledGoFiles = gofiles1430 return nil1431}14321433// vetConfig is the configuration passed to vet describing a single package.1434type vetConfig struct {1435 ID string // package ID (example: "fmt [fmt.test]")1436 Compiler string // compiler name (gc, gccgo)1437 Dir string // directory containing package1438 ImportPath string // canonical import path ("package path")1439 GoFiles []string // absolute paths to package source files1440 NonGoFiles []string // absolute paths to package non-Go files1441 IgnoredFiles []string // absolute paths to ignored source files14421443 Module *analysis.Module // module information, if any1444 ImportMap map[string]string // map import path in source code to package path1445 PackageFile map[string]string // map package path to .a file with export data1446 Standard map[string]bool // map package path to whether it's in the standard library1447 PackageVetx map[string]string // map package path to vetx data from earlier vet run1448 VetxOnly bool // only compute vetx data; don't report detected problems1449 VetxOutput string // write vetx data to this output file1450 Stdout string // write stdout (JSON, unified diff) to this output file1451 GoVersion string // Go version for package1452 FixArchive string // write fixed files to this zip archive, if non-empty14531454 SucceedOnTypecheckFailure bool // awful hack; see #18395 and below1455}14561457// analysisModuleFromModulePublic converts a modinfo.ModulePublic to a analysis.Module.1458func analysisModuleFromModulePublic(m *modinfo.ModulePublic) *analysis.Module {1459 if m == nil {1460 return nil1461 }1462 vm := &analysis.Module{1463 Path: m.Path,1464 Version: m.Version,1465 Replace: analysisModuleFromModulePublic(m.Replace),1466 Time: m.Time,1467 Main: m.Main,1468 Indirect: m.Indirect,1469 Dir: m.Dir,1470 GoMod: m.GoMod,1471 GoVersion: m.GoVersion,1472 }1473 if m.Error != nil {1474 vm.Error = &analysis.ModuleError{Err: m.Error.Err}1475 }1476 return vm1477}14781479func buildVetConfig(a *Action, srcfiles []string, vetDeps []*Action) {1480 // Classify files based on .go extension.1481 // srcfiles does not include raw cgo files.1482 var gofiles, nongofiles []string1483 for _, name := range srcfiles {1484 if strings.HasSuffix(name, ".go") {1485 gofiles = append(gofiles, name)1486 } else {1487 nongofiles = append(nongofiles, name)1488 }1489 }14901491 ignored := str.StringList(a.Package.IgnoredGoFiles, a.Package.IgnoredOtherFiles)14921493 // Pass list of absolute paths to vet,1494 // so that vet's error messages will use absolute paths,1495 // so that we can reformat them relative to the directory1496 // in which the go command is invoked.1497 vcfg := &vetConfig{1498 ID: a.Package.ImportPath,1499 Compiler: cfg.BuildToolchainName,1500 Dir: a.Package.Dir,1501 GoFiles: actualFiles(mkAbsFiles(a.Package.Dir, gofiles)),1502 NonGoFiles: actualFiles(mkAbsFiles(a.Package.Dir, nongofiles)),1503 IgnoredFiles: actualFiles(mkAbsFiles(a.Package.Dir, ignored)),1504 ImportPath: a.Package.ImportPath,1505 ImportMap: make(map[string]string),1506 PackageFile: make(map[string]string),1507 Standard: make(map[string]bool),1508 }1509 vcfg.GoVersion = "go" + gover.Local()1510 if a.Package.Module != nil {1511 v := a.Package.Module.GoVersion1512 if v == "" {1513 v = gover.DefaultGoModVersion1514 }1515 vcfg.GoVersion = "go" + v1516 vcfg.Module = analysisModuleFromModulePublic(a.Package.Module)1517 }1518 a.vetCfg = vcfg1519 for i, raw := range a.Package.Internal.RawImports {1520 final := a.Package.Imports[i]1521 vcfg.ImportMap[raw] = final1522 }15231524 // Compute the list of mapped imports in the vet config1525 // so that we can add any missing mappings below.1526 vcfgMapped := make(map[string]bool)1527 for _, p := range vcfg.ImportMap {1528 vcfgMapped[p] = true1529 }15301531 for _, a1 := range vetDeps {1532 p1 := a1.Package1533 if p1 == nil || p1.ImportPath == "" || p1 == a.Package {1534 continue1535 }1536 // Add import mapping if needed1537 // (for imports like "runtime/cgo" that appear only in generated code).1538 if !vcfgMapped[p1.ImportPath] {1539 vcfg.ImportMap[p1.ImportPath] = p1.ImportPath1540 }1541 if a1.built != "" {1542 vcfg.PackageFile[p1.ImportPath] = a1.built1543 }1544 if p1.Standard {1545 vcfg.Standard[p1.ImportPath] = true1546 }1547 }1548}15491550// VetTool is the path to the effective vet or fix tool binary.1551// The user may specify a non-default value using -{vet,fix}tool.1552// The caller is expected to set it (if needed) before executing any vet actions.1553var VetTool string15541555// VetFlags are the default flags to pass to vet.1556// The caller is expected to set them before executing any vet actions.1557var VetFlags []string15581559// VetHandleStdout determines how the stdout output of each vet tool1560// invocation should be handled. The default behavior is to copy it to1561// the go command's stdout, atomically.1562var VetHandleStdout = copyToStdout15631564// VetExplicit records whether the vet flags (which may include1565// -{vet,fix}tool) were set explicitly on the command line.1566var VetExplicit bool15671568func (b *Builder) vet(ctx context.Context, a *Action) error {1569 // a.Deps[0] is the build of the package being vetted.15701571 a.Failed = nil // vet of dependency may have failed but we can still succeed15721573 if a.Deps[0].Failed != nil {1574 // The build of the package has failed. Skip vet check.1575 // Vet could return export data for non-typecheck errors,1576 // but we ignore it because the package cannot be compiled.1577 return nil1578 }15791580 vcfg := a.Deps[0].vetCfg1581 if vcfg == nil {1582 // Vet config should only be missing if the build failed.1583 return fmt.Errorf("vet config not found")1584 }15851586 sh := b.Shell(a)15871588 // We use "vet" terminology even when building action graphs for go fix.1589 vcfg.VetxOnly = a.VetxOnly1590 vcfg.VetxOutput = a.Objdir + "vet.out"1591 vcfg.Stdout = a.Objdir + "vet.stdout"1592 if a.needFix {1593 vcfg.FixArchive = a.Objdir + "vet.fix.zip"1594 }1595 vcfg.PackageVetx = make(map[string]string)15961597 h := cache.NewHash("vet " + a.Package.ImportPath)1598 fmt.Fprintf(h, "vet %q\n", b.toolID("vet"))15991600 vetFlags := VetFlags16011602 // In GOROOT, we enable all the vet tests during 'go test',1603 // not just the high-confidence subset. This gets us extra1604 // checking for the standard library (at some compliance cost)1605 // and helps us gain experience about how well the checks1606 // work, to help decide which should be turned on by default.1607 // The command-line still wins.1608 //1609 // Note that this flag change applies even when running vet as1610 // a dependency of vetting a package outside std.1611 // (Otherwise we'd have to introduce a whole separate1612 // space of "vet fmt as a dependency of a std top-level vet"1613 // versus "vet fmt as a dependency of a non-std top-level vet".)1614 // This is OK as long as the packages that are farther down the1615 // dependency tree turn on *more* analysis, as here.1616 // (The unsafeptr check does not write any facts for use by1617 // later vet runs, nor does unreachable.)1618 //1619 // When changing the default analyzer suite, please update1620 // x/tools/go/analysis/unitchecker/vet_std_test.go too so that1621 // it functions as a consistent early-warning system for1622 // changes to analyzers (as opposed to changes in the target1623 // packages, which is the purpose of this logic).1624 if a.Package.Goroot && !VetExplicit && VetTool == base.Tool("vet") {1625 // Turn off -unsafeptr checks.1626 // There's too much unsafe.Pointer code1627 // that vet doesn't like in low-level packages1628 // like runtime, sync, and reflect.1629 // Note that $GOROOT/src/buildall.bash1630 // does the same1631 // and should be updated if these flags are1632 // changed here.1633 vetFlags = []string{"-unsafeptr=false"}16341635 // Also turn off -unreachable checks during go test.1636 // During testing it is very common to make changes1637 // like hard-coded forced returns or panics that make1638 // code unreachable. It's unreasonable to insist on files1639 // not having any unreachable code during "go test".1640 // (buildall.bash still has -unreachable enabled1641 // for the overall whole-tree scan.)1642 if cfg.CmdName == "test" {1643 vetFlags = append(vetFlags, "-unreachable=false")1644 }1645 }16461647 // Note: We could decide that vet should compute export data for1648 // all analyses, in which case we don't need to include the flags here.1649 // But that would mean that if an analysis causes problems like1650 // unexpected crashes there would be no way to turn it off.1651 // It seems better to let the flags disable export analysis too.1652 fmt.Fprintf(h, "vetflags %q\n", vetFlags)16531654 fmt.Fprintf(h, "pkg %q\n", a.Deps[0].actionID)1655 for _, a1 := range a.Deps {1656 if a1.Mode == "vet" && a1.built != "" {1657 fmt.Fprintf(h, "vetout %q %s\n", a1.Package.ImportPath, b.fileHash(a1.built))1658 vcfg.PackageVetx[a1.Package.ImportPath] = a1.built1659 }1660 }1661 var (1662 id = cache.ActionID(h.Sum()) // for .vetx file1663 stdoutKey = cache.Subkey(id, "stdout") // for .stdout file1664 fixArchiveKey = cache.Subkey(id, "fix.zip") // for .fix.zip file1665 )16661667 // Check the cache; -a forces a rebuild.1668 if !cfg.BuildA {1669 c := cache.Default()16701671 // There may be multiple artifacts in the cache.1672 // We need to retrieve them all, or none:1673 // the effect must be transactional.1674 var (1675 vetxFile string // name of cached .vetx file1676 fixArchive string // name of cached .fix.zip file1677 stdout io.Reader = bytes.NewReader(nil) // cached stdout stream1678 )16791680 // Obtain location of cached .vetx file.1681 vetxFile, _, err := cache.GetFile(c, id)1682 if err != nil {1683 goto cachemiss1684 }16851686 // Obtain location of cached .fix.zip file (if needed).1687 if a.needFix {1688 file, _, err := cache.GetFile(c, fixArchiveKey)1689 if err != nil {1690 goto cachemiss1691 }1692 fixArchive = file1693 }16941695 // Copy cached .stdout file to stdout.1696 if file, _, err := cache.GetFile(c, stdoutKey); err == nil {1697 f, err := os.Open(file)1698 if err != nil {1699 goto cachemiss1700 }1701 defer f.Close() // ignore error (can't fail)1702 stdout = f1703 }17041705 // Cache hit: commit transaction.1706 a.built = vetxFile1707 a.FixArchive = fixArchive1708 if err := VetHandleStdout(stdout); err != nil {1709 return err // internal error (don't fall through to cachemiss)1710 }17111712 return nil1713 }1714cachemiss:17151716 js, err := json.MarshalIndent(vcfg, "", "\t")1717 if err != nil {1718 return fmt.Errorf("internal error marshaling vet config: %v", err)1719 }1720 js = append(js, '\n')1721 if err := sh.writeFile(a.Objdir+"vet.cfg", js); err != nil {1722 return err1723 }17241725 // TODO(rsc): Why do we pass $GCCGO to go vet?1726 env := b.cCompilerEnv()1727 if cfg.BuildToolchainName == "gccgo" {1728 env = append(env, "GCCGO="+BuildToolchain.compiler())1729 }17301731 p := a.Package1732 tool := VetTool1733 if tool == "" {1734 panic("VetTool unset")1735 }17361737 if err := sh.run(p.Dir, p.ImportPath, env, cfg.BuildToolexec, tool, vetFlags, a.Objdir+"vet.cfg"); err != nil {1738 return err1739 }17401741 // Vet tool succeeded, possibly with facts, fixes, or JSON stdout.1742 // Save all in cache.17431744 // Save facts.1745 if f, err := os.Open(vcfg.VetxOutput); err == nil {1746 defer f.Close() // ignore error1747 a.built = vcfg.VetxOutput1748 cache.Default().Put(id, f) // ignore error1749 }17501751 // Save fix archive (if any).1752 if a.needFix {1753 if f, err := os.Open(vcfg.FixArchive); err == nil {1754 defer f.Close() // ignore error1755 a.FixArchive = vcfg.FixArchive1756 cache.Default().Put(fixArchiveKey, f) // ignore error1757 }1758 }17591760 // Save stdout.1761 if f, err := os.Open(vcfg.Stdout); err == nil {1762 defer f.Close() // ignore error1763 if err := VetHandleStdout(f); err != nil {1764 return err1765 }1766 f.Seek(0, io.SeekStart) // ignore error1767 cache.Default().Put(stdoutKey, f) // ignore error1768 }17691770 return nil1771}17721773var stdoutMu sync.Mutex // serializes concurrent writes (of e.g. JSON values) to stdout17741775// copyToStdout copies the stream to stdout while holding the lock.1776func copyToStdout(r io.Reader) error {1777 stdoutMu.Lock()1778 defer stdoutMu.Unlock()1779 if _, err := io.Copy(os.Stdout, r); err != nil {1780 return fmt.Errorf("copying vet tool stdout: %w", err)1781 }1782 return nil1783}17841785// linkActionID computes the action ID for a link action.1786func (b *Builder) linkActionID(a *Action) cache.ActionID {1787 p := a.Package1788 h := cache.NewHash("link " + p.ImportPath)17891790 // Toolchain-independent configuration.1791 fmt.Fprintf(h, "link\n")1792 // Hash the resolved buildmode (ldBuildmode), not cfg.BuildBuildmode,1793 // so that -buildmode=default produces the same build ID as the1794 // buildmode it resolves to. See go.dev/issue/63559.1795 fmt.Fprintf(h, "buildmode %s goos %s goarch %s\n", ldBuildmode, cfg.Goos, cfg.Goarch)1796 fmt.Fprintf(h, "import %q\n", p.ImportPath)1797 fmt.Fprintf(h, "omitdebug %v standard %v local %v prefix %q\n", p.Internal.OmitDebug, p.Standard, p.Internal.Local, p.Internal.LocalPrefix)1798 fmt.Fprintf(h, "defaultgodebug %q\n", p.DefaultGODEBUG)1799 if cfg.BuildTrimpath {1800 fmt.Fprintln(h, "trimpath")1801 }18021803 // Toolchain-dependent configuration, shared with b.linkSharedActionID.1804 b.printLinkerConfig(h, p)18051806 // Input files.1807 for _, a1 := range a.Deps {1808 p1 := a1.Package1809 if p1 != nil {1810 if a1.built != "" || a1.buildID != "" {1811 buildID := a1.buildID1812 if buildID == "" {1813 buildID = b.buildID(a1.built)1814 }1815 fmt.Fprintf(h, "packagefile %s=%s\n", p1.ImportPath, contentID(buildID))1816 }1817 // Because we put package main's full action ID into the binary's build ID,1818 // we must also put the full action ID into the binary's action ID hash.1819 if p1.Name == "main" {1820 fmt.Fprintf(h, "packagemain %s\n", a1.buildID)1821 }1822 if p1.Shlib != "" {1823 fmt.Fprintf(h, "packageshlib %s=%s\n", p1.ImportPath, contentID(b.buildID(p1.Shlib)))1824 }1825 }1826 }18271828 return h.Sum()1829}18301831// printLinkerConfig prints the linker config into the hash h,1832// as part of the computation of a linker-related action ID.1833func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) {1834 switch cfg.BuildToolchainName {1835 default:1836 base.Fatalf("linkActionID: unknown toolchain %q", cfg.BuildToolchainName)18371838 case "gc":1839 fmt.Fprintf(h, "link %s %q %s\n", b.toolID("link"), forcedLdflags, ldBuildmode)1840 if p != nil {1841 fmt.Fprintf(h, "linkflags %q\n", p.Internal.Ldflags)1842 }18431844 // GOARM, GOMIPS, etc.1845 key, val, _ := cfg.GetArchEnv()1846 fmt.Fprintf(h, "%s=%s\n", key, val)18471848 if cfg.CleanGOEXPERIMENT != "" {1849 fmt.Fprintf(h, "GOEXPERIMENT=%q\n", cfg.CleanGOEXPERIMENT)1850 }18511852 // The linker writes source file paths that refer to GOROOT,1853 // but only if -trimpath is not specified (see [gctoolchain.ld] in gc.go).1854 gorootFinal := cfg.GOROOT1855 if cfg.BuildTrimpath {1856 gorootFinal = ""1857 }1858 fmt.Fprintf(h, "GOROOT=%s\n", gorootFinal)18591860 // GO_EXTLINK_ENABLED controls whether the external linker is used.1861 fmt.Fprintf(h, "GO_EXTLINK_ENABLED=%s\n", cfg.Getenv("GO_EXTLINK_ENABLED"))18621863 // TODO(rsc): Do cgo settings and flags need to be included?1864 // Or external linker settings and flags?18651866 case "gccgo":1867 id, _, err := b.gccgoToolID(BuildToolchain.linker(), "go")1868 if err != nil {1869 base.Fatalf("%v", err)1870 }1871 fmt.Fprintf(h, "link %s %s\n", id, ldBuildmode)1872 // TODO(iant): Should probably include cgo flags here.1873 }1874}18751876// link is the action for linking a single command.1877// Note that any new influence on this logic must be reported in b.linkActionID above as well.1878func (b *Builder) link(ctx context.Context, a *Action) (err error) {1879 if b.useCache(a, b.linkActionID(a), a.Package.Target, !b.IsCmdList) || b.IsCmdList {1880 return nil1881 }1882 defer b.flushOutput(a)18831884 sh := b.Shell(a)1885 if err := sh.Mkdir(a.Objdir); err != nil {1886 return err1887 }18881889 importcfg := a.Objdir + "importcfg.link"1890 if err := b.writeLinkImportcfg(a, importcfg); err != nil {1891 return err1892 }18931894 if err := AllowInstall(a); err != nil {1895 return err1896 }18971898 // make target directory1899 dir, _ := filepath.Split(a.Target)1900 if dir != "" {1901 if err := sh.Mkdir(dir); err != nil {1902 return err1903 }1904 }19051906 if err := BuildToolchain.ld(b, a, a.Target, importcfg, a.Deps[0].built); err != nil {1907 return err1908 }19091910 // Update the binary with the final build ID.1911 if err := b.updateBuildID(a, a.Target); err != nil {1912 return err1913 }19141915 a.built = a.Target1916 return nil1917}19181919func (b *Builder) writeLinkImportcfg(a *Action, file string) error {1920 // Prepare Go import cfg.1921 var icfg bytes.Buffer1922 for _, a1 := range a.Deps {1923 p1 := a1.Package1924 if p1 == nil {1925 continue1926 }1927 fmt.Fprintf(&icfg, "packagefile %s=%s\n", p1.ImportPath, a1.built)1928 if p1.Shlib != "" {1929 fmt.Fprintf(&icfg, "packageshlib %s=%s\n", p1.ImportPath, p1.Shlib)1930 }1931 }1932 info := ""1933 if a.Package.Internal.BuildInfo != nil {1934 info = a.Package.Internal.BuildInfo.String()1935 }1936 fmt.Fprintf(&icfg, "modinfo %q\n", modload.ModInfoData(info))1937 return b.Shell(a).writeFile(file, icfg.Bytes())1938}19391940// PkgconfigCmd returns a pkg-config binary name1941// defaultPkgConfig is defined in zdefaultcc.go, written by cmd/dist.1942func (b *Builder) PkgconfigCmd() string {1943 return envList("PKG_CONFIG", cfg.DefaultPkgConfig)[0]1944}19451946// splitPkgConfigOutput parses the pkg-config output into a slice of flags.1947// This implements the shell quoting semantics described in1948// https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02,1949// except that it does not support parameter or arithmetic expansion or command1950// substitution and hard-codes the <blank> delimiters instead of reading them1951// from LC_LOCALE.1952func splitPkgConfigOutput(out []byte) ([]string, error) {1953 if len(out) == 0 {1954 return nil, nil1955 }1956 var flags []string1957 flag := make([]byte, 0, len(out))1958 didQuote := false // was the current flag parsed from a quoted string?1959 escaped := false // did we just read `\` in a non-single-quoted context?1960 quote := byte(0) // what is the quote character around the current string?19611962 for _, c := range out {1963 if escaped {1964 if quote == '"' {1965 // “The <backslash> shall retain its special meaning as an escape1966 // character … only when followed by one of the following characters1967 // when considered special:”1968 switch c {1969 case '$', '`', '"', '\\', '\n':1970 // Handle the escaped character normally.1971 default:1972 // Not an escape character after all.1973 flag = append(flag, '\\', c)1974 escaped = false1975 continue1976 }1977 }19781979 if c == '\n' {1980 // “If a <newline> follows the <backslash>, the shell shall interpret1981 // this as line continuation.”1982 } else {1983 flag = append(flag, c)1984 }1985 escaped = false1986 continue1987 }19881989 if quote != 0 && c == quote {1990 quote = 01991 continue1992 }1993 switch quote {1994 case '\'':1995 // “preserve the literal value of each character”1996 flag = append(flag, c)1997 continue1998 case '"':1999 // “preserve the literal value of all characters within the double-quotes,2000 // with the exception of …”
Findings
✓ No findings reported for this file.