| // Copyright 2011 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Package load loads packages. |
| package load |
| |
| import ( |
| "bytes" |
| "context" |
| "crypto/sha256" |
| "encoding/json" |
| "errors" |
| "fmt" |
| "go/build" |
| "go/scanner" |
| "go/token" |
| "internal/platform" |
| "io/fs" |
| "os" |
| pathpkg "path" |
| "path/filepath" |
| "runtime" |
| "runtime/debug" |
| "slices" |
| "sort" |
| "strconv" |
| "strings" |
| "time" |
| "unicode" |
| "unicode/utf8" |
| |
| "cmd/go/internal/base" |
| "cmd/go/internal/cfg" |
| "cmd/go/internal/fsys" |
| "cmd/go/internal/gover" |
| "cmd/go/internal/imports" |
| "cmd/go/internal/modfetch" |
| "cmd/go/internal/modindex" |
| "cmd/go/internal/modinfo" |
| "cmd/go/internal/modload" |
| "cmd/go/internal/par" |
| "cmd/go/internal/search" |
| "cmd/go/internal/str" |
| "cmd/go/internal/trace" |
| "cmd/go/internal/vcs" |
| "cmd/internal/pkgpattern" |
| |
| "golang.org/x/mod/modfile" |
| "golang.org/x/mod/module" |
| ) |
| |
| // A Package describes a single package found in a directory. |
| type Package struct { |
| PackagePublic // visible in 'go list' |
| Internal PackageInternal // for use inside go command only |
| } |
| |
| type PackagePublic struct { |
| // Note: These fields are part of the go command's public API. |
| // See list.go. It is okay to add fields, but not to change or |
| // remove existing ones. Keep in sync with ../list/list.go |
| Dir string `json:",omitempty"` // directory containing package sources |
| ImportPath string `json:",omitempty"` // import path of package in dir |
| ImportComment string `json:",omitempty"` // path in import comment on package statement |
| Name string `json:",omitempty"` // package name |
| Doc string `json:",omitempty"` // package documentation string |
| Target string `json:",omitempty"` // installed target for this package (may be executable) |
| Shlib string `json:",omitempty"` // the shared library that contains this package (only set when -linkshared) |
| Root string `json:",omitempty"` // Go root, Go path dir, or module root dir containing this package |
| ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory |
| ForTest string `json:",omitempty"` // package is only for use in named test |
| Export string `json:",omitempty"` // file containing export data (set by go list -export) |
| BuildID string `json:",omitempty"` // build ID of the compiled package (set by go list -export) |
| Module *modinfo.ModulePublic `json:",omitempty"` // info about package's module, if any |
| Match []string `json:",omitempty"` // command-line patterns matching this package |
| Goroot bool `json:",omitempty"` // is this package found in the Go root? |
| Standard bool `json:",omitempty"` // is this package part of the standard Go library? |
| DepOnly bool `json:",omitempty"` // package is only as a dependency, not explicitly listed |
| BinaryOnly bool `json:",omitempty"` // package cannot be recompiled |
| Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies? |
| |
| DefaultGODEBUG string `json:",omitempty"` // default GODEBUG setting (only for Name=="main") |
| |
| // Stale and StaleReason remain here *only* for the list command. |
| // They are only initialized in preparation for list execution. |
| // The regular build determines staleness on the fly during action execution. |
| Stale bool `json:",omitempty"` // would 'go install' do anything for this package? |
| StaleReason string `json:",omitempty"` // why is Stale true? |
| |
| // Source files |
| // If you add to this list you MUST add to p.AllFiles (below) too. |
| // Otherwise file name security lists will not apply to any new additions. |
| GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) |
| CgoFiles []string `json:",omitempty"` // .go source files that import "C" |
| CompiledGoFiles []string `json:",omitempty"` // .go output from running cgo on CgoFiles |
| IgnoredGoFiles []string `json:",omitempty"` // .go source files ignored due to build constraints |
| InvalidGoFiles []string `json:",omitempty"` // .go source files with detected problems (parse error, wrong package name, and so on) |
| IgnoredOtherFiles []string `json:",omitempty"` // non-.go source files ignored due to build constraints |
| CFiles []string `json:",omitempty"` // .c source files |
| CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files |
| MFiles []string `json:",omitempty"` // .m source files |
| HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files |
| FFiles []string `json:",omitempty"` // .f, .F, .for and .f90 Fortran source files |
| SFiles []string `json:",omitempty"` // .s source files |
| SwigFiles []string `json:",omitempty"` // .swig files |
| SwigCXXFiles []string `json:",omitempty"` // .swigcxx files |
| SysoFiles []string `json:",omitempty"` // .syso system object files added to package |
| |
| // Embedded files |
| EmbedPatterns []string `json:",omitempty"` // //go:embed patterns |
| EmbedFiles []string `json:",omitempty"` // files matched by EmbedPatterns |
| |
| // Cgo directives |
| CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler |
| CgoCPPFLAGS []string `json:",omitempty"` // cgo: flags for C preprocessor |
| CgoCXXFLAGS []string `json:",omitempty"` // cgo: flags for C++ compiler |
| CgoFFLAGS []string `json:",omitempty"` // cgo: flags for Fortran compiler |
| CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker |
| CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names |
| |
| // Dependency information |
| Imports []string `json:",omitempty"` // import paths used by this package |
| ImportMap map[string]string `json:",omitempty"` // map from source import to ImportPath (identity entries omitted) |
| Deps []string `json:",omitempty"` // all (recursively) imported dependencies |
| |
| // Error information |
| // Incomplete is above, packed into the other bools |
| Error *PackageError `json:",omitempty"` // error loading this package (not dependencies) |
| DepsErrors []*PackageError `json:",omitempty"` // errors loading dependencies, collected by go list before output |
| |
| // Test information |
| // If you add to this list you MUST add to p.AllFiles (below) too. |
| // Otherwise file name security lists will not apply to any new additions. |
| TestGoFiles []string `json:",omitempty"` // _test.go files in package |
| TestImports []string `json:",omitempty"` // imports from TestGoFiles |
| TestEmbedPatterns []string `json:",omitempty"` // //go:embed patterns |
| TestEmbedFiles []string `json:",omitempty"` // files matched by TestEmbedPatterns |
| XTestGoFiles []string `json:",omitempty"` // _test.go files outside package |
| XTestImports []string `json:",omitempty"` // imports from XTestGoFiles |
| XTestEmbedPatterns []string `json:",omitempty"` // //go:embed patterns |
| XTestEmbedFiles []string `json:",omitempty"` // files matched by XTestEmbedPatterns |
| } |
| |
| // AllFiles returns the names of all the files considered for the package. |
| // This is used for sanity and security checks, so we include all files, |
| // even IgnoredGoFiles, because some subcommands consider them. |
| // The go/build package filtered others out (like foo_wrongGOARCH.s) |
| // and that's OK. |
| func (p *Package) AllFiles() []string { |
| files := str.StringList( |
| p.GoFiles, |
| p.CgoFiles, |
| // no p.CompiledGoFiles, because they are from GoFiles or generated by us |
| p.IgnoredGoFiles, |
| // no p.InvalidGoFiles, because they are from GoFiles |
| p.IgnoredOtherFiles, |
| p.CFiles, |
| p.CXXFiles, |
| p.MFiles, |
| p.HFiles, |
| p.FFiles, |
| p.SFiles, |
| p.SwigFiles, |
| p.SwigCXXFiles, |
| p.SysoFiles, |
| p.TestGoFiles, |
| p.XTestGoFiles, |
| ) |
| |
| // EmbedFiles may overlap with the other files. |
| // Dedup, but delay building the map as long as possible. |
| // Only files in the current directory (no slash in name) |
| // need to be checked against the files variable above. |
| var have map[string]bool |
| for _, file := range p.EmbedFiles { |
| if !strings.Contains(file, "/") { |
| if have == nil { |
| have = make(map[string]bool) |
| for _, file := range files { |
| have[file] = true |
| } |
| } |
| if have[file] { |
| continue |
| } |
| } |
| files = append(files, file) |
| } |
| return files |
| } |
| |
| // Desc returns the package "description", for use in b.showOutput. |
| func (p *Package) Desc() string { |
| if p.ForTest != "" { |
| return p.ImportPath + " [" + p.ForTest + ".test]" |
| } |
| if p.Internal.ForMain != "" { |
| return p.ImportPath + " [" + p.Internal.ForMain + "]" |
| } |
| return p.ImportPath |
| } |
| |
| // IsTestOnly reports whether p is a test-only package. |
| // |
| // A “test-only” package is one that: |
| // - is a test-only variant of an ordinary package, or |
| // - is a synthesized "main" package for a test binary, or |
| // - contains only _test.go files. |
| func (p *Package) IsTestOnly() bool { |
| return p.ForTest != "" || |
| p.Internal.TestmainGo != nil || |
| len(p.TestGoFiles)+len(p.XTestGoFiles) > 0 && len(p.GoFiles)+len(p.CgoFiles) == 0 |
| } |
| |
| type PackageInternal struct { |
| // Unexported fields are not part of the public API. |
| Build *build.Package |
| Imports []*Package // this package's direct imports |
| CompiledImports []string // additional Imports necessary when using CompiledGoFiles (all from standard library); 1:1 with the end of PackagePublic.Imports |
| RawImports []string // this package's original imports as they appear in the text of the program; 1:1 with the end of PackagePublic.Imports |
| ForceLibrary bool // this package is a library (even if named "main") |
| CmdlineFiles bool // package built from files listed on command line |
| CmdlinePkg bool // package listed on command line |
| CmdlinePkgLiteral bool // package listed as literal on command line (not via wildcard) |
| Local bool // imported via local path (./ or ../) |
| LocalPrefix string // interpret ./ and ../ imports relative to this prefix |
| ExeName string // desired name for temporary executable |
| FuzzInstrument bool // package should be instrumented for fuzzing |
| Cover CoverSetup // coverage mode and other setup info of -cover is being applied to this package |
| CoverVars map[string]*CoverVar // variables created by coverage analysis |
| OmitDebug bool // tell linker not to write debug information |
| GobinSubdir bool // install target would be subdir of GOBIN |
| BuildInfo *debug.BuildInfo // add this info to package main |
| TestmainGo *[]byte // content for _testmain.go |
| Embed map[string][]string // //go:embed comment mapping |
| OrigImportPath string // original import path before adding '_test' suffix |
| PGOProfile string // path to PGO profile |
| ForMain string // the main package if this package is built specifically for it |
| |
| Asmflags []string // -asmflags for this package |
| Gcflags []string // -gcflags for this package |
| Ldflags []string // -ldflags for this package |
| Gccgoflags []string // -gccgoflags for this package |
| } |
| |
| // A NoGoError indicates that no Go files for the package were applicable to the |
| // build for that package. |
| // |
| // That may be because there were no files whatsoever, or because all files were |
| // excluded, or because all non-excluded files were test sources. |
| type NoGoError struct { |
| Package *Package |
| } |
| |
| func (e *NoGoError) Error() string { |
| if len(e.Package.IgnoredGoFiles) > 0 { |
| // Go files exist, but they were ignored due to build constraints. |
| return "build constraints exclude all Go files in " + e.Package.Dir |
| } |
| if len(e.Package.TestGoFiles)+len(e.Package.XTestGoFiles) > 0 { |
| // Test Go files exist, but we're not interested in them. |
| // The double-negative is unfortunate but we want e.Package.Dir |
| // to appear at the end of error message. |
| return "no non-test Go files in " + e.Package.Dir |
| } |
| return "no Go files in " + e.Package.Dir |
| } |
| |
| // setLoadPackageDataError presents an error found when loading package data |
| // as a *PackageError. It has special cases for some common errors to improve |
| // messages shown to users and reduce redundancy. |
| // |
| // setLoadPackageDataError returns true if it's safe to load information about |
| // imported packages, for example, if there was a parse error loading imports |
| // in one file, but other files are okay. |
| func (p *Package) setLoadPackageDataError(err error, path string, stk *ImportStack, importPos []token.Position) { |
| matchErr, isMatchErr := err.(*search.MatchError) |
| if isMatchErr && matchErr.Match.Pattern() == path { |
| if matchErr.Match.IsLiteral() { |
| // The error has a pattern has a pattern similar to the import path. |
| // It may be slightly different (./foo matching example.com/foo), |
| // but close enough to seem redundant. |
| // Unwrap the error so we don't show the pattern. |
| err = matchErr.Err |
| } |
| } |
| |
| // Replace (possibly wrapped) *build.NoGoError with *load.NoGoError. |
| // The latter is more specific about the cause. |
| var nogoErr *build.NoGoError |
| if errors.As(err, &nogoErr) { |
| if p.Dir == "" && nogoErr.Dir != "" { |
| p.Dir = nogoErr.Dir |
| } |
| err = &NoGoError{Package: p} |
| } |
| |
| // Take only the first error from a scanner.ErrorList. PackageError only |
| // has room for one position, so we report the first error with a position |
| // instead of all of the errors without a position. |
| var pos string |
| var isScanErr bool |
| if scanErr, ok := err.(scanner.ErrorList); ok && len(scanErr) > 0 { |
| isScanErr = true // For stack push/pop below. |
| |
| scanPos := scanErr[0].Pos |
| scanPos.Filename = base.ShortPath(scanPos.Filename) |
| pos = scanPos.String() |
| err = errors.New(scanErr[0].Msg) |
| } |
| |
| // Report the error on the importing package if the problem is with the import declaration |
| // for example, if the package doesn't exist or if the import path is malformed. |
| // On the other hand, don't include a position if the problem is with the imported package, |
| // for example there are no Go files (NoGoError), or there's a problem in the imported |
| // package's source files themselves (scanner errors). |
| // |
| // TODO(matloob): Perhaps make each of those the errors in the first group |
| // (including modload.ImportMissingError, ImportMissingSumError, and the |
| // corresponding "cannot find package %q in any of" GOPATH-mode error |
| // produced in build.(*Context).Import; modload.AmbiguousImportError, |
| // and modload.PackageNotInModuleError; and the malformed module path errors |
| // produced in golang.org/x/mod/module.CheckMod) implement an interface |
| // to make it easier to check for them? That would save us from having to |
| // move the modload errors into this package to avoid a package import cycle, |
| // and from having to export an error type for the errors produced in build. |
| if !isMatchErr && (nogoErr != nil || isScanErr) { |
| stk.Push(path) |
| defer stk.Pop() |
| } |
| |
| p.Error = &PackageError{ |
| ImportStack: stk.Copy(), |
| Pos: pos, |
| Err: err, |
| } |
| p.Incomplete = true |
| |
| if path != stk.Top() { |
| p.Error.setPos(importPos) |
| } |
| } |
| |
| // Resolve returns the resolved version of imports, |
| // which should be p.TestImports or p.XTestImports, NOT p.Imports. |
| // The imports in p.TestImports and p.XTestImports are not recursively |
| // loaded during the initial load of p, so they list the imports found in |
| // the source file, but most processing should be over the vendor-resolved |
| // import paths. We do this resolution lazily both to avoid file system work |
| // and because the eventual real load of the test imports (during 'go test') |
| // can produce better error messages if it starts with the original paths. |
| // The initial load of p loads all the non-test imports and rewrites |
| // the vendored paths, so nothing should ever call p.vendored(p.Imports). |
| func (p *Package) Resolve(imports []string) []string { |
| if len(imports) > 0 && len(p.Imports) > 0 && &imports[0] == &p.Imports[0] { |
| panic("internal error: p.Resolve(p.Imports) called") |
| } |
| seen := make(map[string]bool) |
| var all []string |
| for _, path := range imports { |
| path = ResolveImportPath(p, path) |
| if !seen[path] { |
| seen[path] = true |
| all = append(all, path) |
| } |
| } |
| sort.Strings(all) |
| return all |
| } |
| |
| // CoverVar holds the name of the generated coverage variables targeting the named file. |
| type CoverVar struct { |
| File string // local file name |
| Var string // name of count struct |
| } |
| |
| // CoverSetup holds parameters related to coverage setup for a given package (covermode, etc). |
| type CoverSetup struct { |
| Mode string // coverage mode for this package |
| Cfg string // path to config file to pass to "go tool cover" |
| GenMeta bool // ask cover tool to emit a static meta data if set |
| } |
| |
| func (p *Package) copyBuild(opts PackageOpts, pp *build.Package) { |
| p.Internal.Build = pp |
| |
| if pp.PkgTargetRoot != "" && cfg.BuildPkgdir != "" { |
| old := pp.PkgTargetRoot |
| pp.PkgRoot = cfg.BuildPkgdir |
| pp.PkgTargetRoot = cfg.BuildPkgdir |
| if pp.PkgObj != "" { |
| pp.PkgObj = filepath.Join(cfg.BuildPkgdir, strings.TrimPrefix(pp.PkgObj, old)) |
| } |
| } |
| |
| p.Dir = pp.Dir |
| p.ImportPath = pp.ImportPath |
| p.ImportComment = pp.ImportComment |
| p.Name = pp.Name |
| p.Doc = pp.Doc |
| p.Root = pp.Root |
| p.ConflictDir = pp.ConflictDir |
| p.BinaryOnly = pp.BinaryOnly |
| |
| // TODO? Target |
| p.Goroot = pp.Goroot |
| p.Standard = p.Goroot && p.ImportPath != "" && search.IsStandardImportPath(p.ImportPath) |
| p.GoFiles = pp.GoFiles |
| p.CgoFiles = pp.CgoFiles |
| p.IgnoredGoFiles = pp.IgnoredGoFiles |
| p.InvalidGoFiles = pp.InvalidGoFiles |
| p.IgnoredOtherFiles = pp.IgnoredOtherFiles |
| p.CFiles = pp.CFiles |
| p.CXXFiles = pp.CXXFiles |
| p.MFiles = pp.MFiles |
| p.HFiles = pp.HFiles |
| p.FFiles = pp.FFiles |
| p.SFiles = pp.SFiles |
| p.SwigFiles = pp.SwigFiles |
| p.SwigCXXFiles = pp.SwigCXXFiles |
| p.SysoFiles = pp.SysoFiles |
| if cfg.BuildMSan { |
| // There's no way for .syso files to be built both with and without |
| // support for memory sanitizer. Assume they are built without, |
| // and drop them. |
| p.SysoFiles = nil |
| } |
| p.CgoCFLAGS = pp.CgoCFLAGS |
| p.CgoCPPFLAGS = pp.CgoCPPFLAGS |
| p.CgoCXXFLAGS = pp.CgoCXXFLAGS |
| p.CgoFFLAGS = pp.CgoFFLAGS |
| p.CgoLDFLAGS = pp.CgoLDFLAGS |
| p.CgoPkgConfig = pp.CgoPkgConfig |
| // We modify p.Imports in place, so make copy now. |
| p.Imports = make([]string, len(pp.Imports)) |
| copy(p.Imports, pp.Imports) |
| p.Internal.RawImports = pp.Imports |
| p.TestGoFiles = pp.TestGoFiles |
| p.TestImports = pp.TestImports |
| p.XTestGoFiles = pp.XTestGoFiles |
| p.XTestImports = pp.XTestImports |
| if opts.IgnoreImports { |
| p.Imports = nil |
| p.Internal.RawImports = nil |
| p.TestImports = nil |
| p.XTestImports = nil |
| } |
| p.EmbedPatterns = pp.EmbedPatterns |
| p.TestEmbedPatterns = pp.TestEmbedPatterns |
| p.XTestEmbedPatterns = pp.XTestEmbedPatterns |
| p.Internal.OrigImportPath = pp.ImportPath |
| } |
| |
| // A PackageError describes an error loading information about a package. |
| type PackageError struct { |
| ImportStack []string // shortest path from package named on command line to this one |
| Pos string // position of error |
| Err error // the error itself |
| IsImportCycle bool // the error is an import cycle |
| Hard bool // whether the error is soft or hard; soft errors are ignored in some places |
| alwaysPrintStack bool // whether to always print the ImportStack |
| } |
| |
| func (p *PackageError) Error() string { |
| // TODO(#43696): decide when to print the stack or the position based on |
| // the error type and whether the package is in the main module. |
| // Document the rationale. |
| if p.Pos != "" && (len(p.ImportStack) == 0 || !p.alwaysPrintStack) { |
| // Omit import stack. The full path to the file where the error |
| // is the most important thing. |
| return p.Pos + ": " + p.Err.Error() |
| } |
| |
| // If the error is an ImportPathError, and the last path on the stack appears |
| // in the error message, omit that path from the stack to avoid repetition. |
| // If an ImportPathError wraps another ImportPathError that matches the |
| // last path on the stack, we don't omit the path. An error like |
| // "package A imports B: error loading C caused by B" would not be clearer |
| // if "imports B" were omitted. |
| if len(p.ImportStack) == 0 { |
| return p.Err.Error() |
| } |
| var optpos string |
| if p.Pos != "" { |
| optpos = "\n\t" + p.Pos |
| } |
| return "package " + strings.Join(p.ImportStack, "\n\timports ") + optpos + ": " + p.Err.Error() |
| } |
| |
| func (p *PackageError) Unwrap() error { return p.Err } |
| |
| // PackageError implements MarshalJSON so that Err is marshaled as a string |
| // and non-essential fields are omitted. |
| func (p *PackageError) MarshalJSON() ([]byte, error) { |
| perr := struct { |
| ImportStack []string |
| Pos string |
| Err string |
| }{p.ImportStack, p.Pos, p.Err.Error()} |
| return json.Marshal(perr) |
| } |
| |
| func (p *PackageError) setPos(posList []token.Position) { |
| if len(posList) == 0 { |
| return |
| } |
| pos := posList[0] |
| pos.Filename = base.ShortPath(pos.Filename) |
| p.Pos = pos.String() |
| } |
| |
| // ImportPathError is a type of error that prevents a package from being loaded |
| // for a given import path. When such a package is loaded, a *Package is |
| // returned with Err wrapping an ImportPathError: the error is attached to |
| // the imported package, not the importing package. |
| // |
| // The string returned by ImportPath must appear in the string returned by |
| // Error. Errors that wrap ImportPathError (such as PackageError) may omit |
| // the import path. |
| type ImportPathError interface { |
| error |
| ImportPath() string |
| } |
| |
| var ( |
| _ ImportPathError = (*importError)(nil) |
| _ ImportPathError = (*mainPackageError)(nil) |
| _ ImportPathError = (*modload.ImportMissingError)(nil) |
| _ ImportPathError = (*modload.ImportMissingSumError)(nil) |
| _ ImportPathError = (*modload.DirectImportFromImplicitDependencyError)(nil) |
| ) |
| |
| type importError struct { |
| importPath string |
| err error // created with fmt.Errorf |
| } |
| |
| func ImportErrorf(path, format string, args ...any) ImportPathError { |
| err := &importError{importPath: path, err: fmt.Errorf(format, args...)} |
| if errStr := err.Error(); !strings.Contains(errStr, path) { |
| panic(fmt.Sprintf("path %q not in error %q", path, errStr)) |
| } |
| return err |
| } |
| |
| func (e *importError) Error() string { |
| return e.err.Error() |
| } |
| |
| func (e *importError) Unwrap() error { |
| // Don't return e.err directly, since we're only wrapping an error if %w |
| // was passed to ImportErrorf. |
| return errors.Unwrap(e.err) |
| } |
| |
| func (e *importError) ImportPath() string { |
| return e.importPath |
| } |
| |
| // An ImportStack is a stack of import paths, possibly with the suffix " (test)" appended. |
| // The import path of a test package is the import path of the corresponding |
| // non-test package with the suffix "_test" added. |
| type ImportStack []string |
| |
| func (s *ImportStack) Push(p string) { |
| *s = append(*s, p) |
| } |
| |
| func (s *ImportStack) Pop() { |
| *s = (*s)[0 : len(*s)-1] |
| } |
| |
| func (s *ImportStack) Copy() []string { |
| return append([]string{}, *s...) |
| } |
| |
| func (s *ImportStack) Top() string { |
| if len(*s) == 0 { |
| return "" |
| } |
| return (*s)[len(*s)-1] |
| } |
| |
| // shorterThan reports whether sp is shorter than t. |
| // We use this to record the shortest import sequence |
| // that leads to a particular package. |
| func (sp *ImportStack) shorterThan(t []string) bool { |
| s := *sp |
| if len(s) != len(t) { |
| return len(s) < len(t) |
| } |
| // If they are the same length, settle ties using string ordering. |
| for i := range s { |
| if s[i] != t[i] { |
| return s[i] < t[i] |
| } |
| } |
| return false // they are equal |
| } |
| |
| // packageCache is a lookup cache for LoadImport, |
| // so that if we look up a package multiple times |
| // we return the same pointer each time. |
| var packageCache = map[string]*Package{} |
| |
| // ClearPackageCache clears the in-memory package cache and the preload caches. |
| // It is only for use by GOPATH-based "go get". |
| // TODO(jayconrod): When GOPATH-based "go get" is removed, delete this function. |
| func ClearPackageCache() { |
| clear(packageCache) |
| resolvedImportCache.Clear() |
| packageDataCache.Clear() |
| } |
| |
| // ClearPackageCachePartial clears packages with the given import paths from the |
| // in-memory package cache and the preload caches. It is only for use by |
| // GOPATH-based "go get". |
| // TODO(jayconrod): When GOPATH-based "go get" is removed, delete this function. |
| func ClearPackageCachePartial(args []string) { |
| shouldDelete := make(map[string]bool) |
| for _, arg := range args { |
| shouldDelete[arg] = true |
| if p := packageCache[arg]; p != nil { |
| delete(packageCache, arg) |
| } |
| } |
| resolvedImportCache.DeleteIf(func(key importSpec) bool { |
| return shouldDelete[key.path] |
| }) |
| packageDataCache.DeleteIf(func(key string) bool { |
| return shouldDelete[key] |
| }) |
| } |
| |
| // ReloadPackageNoFlags is like LoadImport but makes sure |
| // not to use the package cache. |
| // It is only for use by GOPATH-based "go get". |
| // TODO(rsc): When GOPATH-based "go get" is removed, delete this function. |
| func ReloadPackageNoFlags(arg string, stk *ImportStack) *Package { |
| p := packageCache[arg] |
| if p != nil { |
| delete(packageCache, arg) |
| resolvedImportCache.DeleteIf(func(key importSpec) bool { |
| return key.path == p.ImportPath |
| }) |
| packageDataCache.Delete(p.ImportPath) |
| } |
| return LoadPackage(context.TODO(), PackageOpts{}, arg, base.Cwd(), stk, nil, 0) |
| } |
| |
| // dirToImportPath returns the pseudo-import path we use for a package |
| // outside the Go path. It begins with _/ and then contains the full path |
| // to the directory. If the package lives in c:\home\gopher\my\pkg then |
| // the pseudo-import path is _/c_/home/gopher/my/pkg. |
| // Using a pseudo-import path like this makes the ./ imports no longer |
| // a special case, so that all the code to deal with ordinary imports works |
| // automatically. |
| func dirToImportPath(dir string) string { |
| return pathpkg.Join("_", strings.Map(makeImportValid, filepath.ToSlash(dir))) |
| } |
| |
| func makeImportValid(r rune) rune { |
| // Should match Go spec, compilers, and ../../go/parser/parser.go:/isValidImport. |
| const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD" |
| if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) { |
| return '_' |
| } |
| return r |
| } |
| |
| // Mode flags for loadImport and download (in get.go). |
| const ( |
| // ResolveImport means that loadImport should do import path expansion. |
| // That is, ResolveImport means that the import path came from |
| // a source file and has not been expanded yet to account for |
| // vendoring or possible module adjustment. |
| // Every import path should be loaded initially with ResolveImport, |
| // and then the expanded version (for example with the /vendor/ in it) |
| // gets recorded as the canonical import path. At that point, future loads |
| // of that package must not pass ResolveImport, because |
| // disallowVendor will reject direct use of paths containing /vendor/. |
| ResolveImport = 1 << iota |
| |
| // ResolveModule is for download (part of "go get") and indicates |
| // that the module adjustment should be done, but not vendor adjustment. |
| ResolveModule |
| |
| // GetTestDeps is for download (part of "go get") and indicates |
| // that test dependencies should be fetched too. |
| GetTestDeps |
| |
| // The remainder are internal modes for calls to loadImport. |
| |
| // cmdlinePkg is for a package mentioned on the command line. |
| cmdlinePkg |
| |
| // cmdlinePkgLiteral is for a package mentioned on the command line |
| // without using any wildcards or meta-patterns. |
| cmdlinePkgLiteral |
| ) |
| |
| // LoadImport scans the directory named by path, which must be an import path, |
| // but possibly a local import path (an absolute file system path or one beginning |
| // with ./ or ../). A local relative path is interpreted relative to srcDir. |
| // It returns a *Package describing the package found in that directory. |
| // LoadImport does not set tool flags and should only be used by |
| // this package, as part of a bigger load operation, and by GOPATH-based "go get". |
| // TODO(rsc): When GOPATH-based "go get" is removed, unexport this function. |
| // The returned PackageError, if any, describes why parent is not allowed |
| // to import the named package, with the error referring to importPos. |
| // The PackageError can only be non-nil when parent is not nil. |
| func LoadImport(ctx context.Context, opts PackageOpts, path, srcDir string, parent *Package, stk *ImportStack, importPos []token.Position, mode int) (*Package, *PackageError) { |
| return loadImport(ctx, opts, nil, path, srcDir, parent, stk, importPos, mode) |
| } |
| |
| // LoadPackage does Load import, but without a parent package load contezt |
| func LoadPackage(ctx context.Context, opts PackageOpts, path, srcDir string, stk *ImportStack, importPos []token.Position, mode int) *Package { |
| p, err := loadImport(ctx, opts, nil, path, srcDir, nil, stk, importPos, mode) |
| if err != nil { |
| base.Fatalf("internal error: loadImport of %q with nil parent returned an error", path) |
| } |
| return p |
| } |
| |
| func loadImport(ctx context.Context, opts PackageOpts, pre *preload, path, srcDir string, parent *Package, stk *ImportStack, importPos []token.Position, mode int) (*Package, *PackageError) { |
| ctx, span := trace.StartSpan(ctx, "modload.loadImport "+path) |
| defer span.Done() |
| |
| if path == "" { |
| panic("LoadImport called with empty package path") |
| } |
| |
| var parentPath, parentRoot string |
| parentIsStd := false |
| if parent != nil { |
| parentPath = parent.ImportPath |
| parentRoot = parent.Root |
| parentIsStd = parent.Standard |
| } |
| bp, loaded, err := loadPackageData(ctx, path, parentPath, srcDir, parentRoot, parentIsStd, mode) |
| if loaded && pre != nil && !opts.IgnoreImports { |
| pre.preloadImports(ctx, opts, bp.Imports, bp) |
| } |
| if bp == nil { |
| p := &Package{ |
| PackagePublic: PackagePublic{ |
| ImportPath: path, |
| Incomplete: true, |
| }, |
| } |
| if importErr, ok := err.(ImportPathError); !ok || importErr.ImportPath() != path { |
| // Only add path to the error's import stack if it's not already present |
| // in the error. |
| // |
| // TODO(bcmills): setLoadPackageDataError itself has a similar Push / Pop |
| // sequence that empirically doesn't trigger for these errors, guarded by |
| // a somewhat complex condition. Figure out how to generalize that |
| // condition and eliminate the explicit calls here. |
| stk.Push(path) |
| defer stk.Pop() |
| } |
| p.setLoadPackageDataError(err, path, stk, nil) |
| return p, nil |
| } |
| |
| setCmdline := func(p *Package) { |
| if mode&cmdlinePkg != 0 { |
| p.Internal.CmdlinePkg = true |
| } |
| if mode&cmdlinePkgLiteral != 0 { |
| p.Internal.CmdlinePkgLiteral = true |
| } |
| } |
| |
| importPath := bp.ImportPath |
| p := packageCache[importPath] |
| if p != nil { |
| stk.Push(path) |
| p = reusePackage(p, stk) |
| stk.Pop() |
| setCmdline(p) |
| } else { |
| p = new(Package) |
| p.Internal.Local = build.IsLocalImport(path) |
| p.ImportPath = importPath |
| packageCache[importPath] = p |
| |
| setCmdline(p) |
| |
| // Load package. |
| // loadPackageData may return bp != nil even if an error occurs, |
| // in order to return partial information. |
| p.load(ctx, opts, path, stk, importPos, bp, err) |
| |
| if !cfg.ModulesEnabled && path != cleanImport(path) { |
| p.Error = &PackageError{ |
| ImportStack: stk.Copy(), |
| Err: ImportErrorf(path, "non-canonical import path %q: should be %q", path, pathpkg.Clean(path)), |
| } |
| p.Incomplete = true |
| p.Error.setPos(importPos) |
| } |
| } |
| |
| // Checked on every import because the rules depend on the code doing the importing. |
| if perr := disallowInternal(ctx, srcDir, parent, parentPath, p, stk); perr != nil { |
| perr.setPos(importPos) |
| return p, perr |
| } |
| if mode&ResolveImport != 0 { |
| if perr := disallowVendor(srcDir, path, parentPath, p, stk); perr != nil { |
| perr.setPos(importPos) |
| return p, perr |
| } |
| } |
| |
| if p.Name == "main" && parent != nil && parent.Dir != p.Dir { |
| perr := &PackageError{ |
| ImportStack: stk.Copy(), |
| Err: ImportErrorf(path, "import %q is a program, not an importable package", path), |
| } |
| perr.setPos(importPos) |
| return p, perr |
| } |
| |
| if p.Internal.Local && parent != nil && !parent.Internal.Local { |
| var err error |
| if path == "." { |
| err = ImportErrorf(path, "%s: cannot import current directory", path) |
| } else { |
| err = ImportErrorf(path, "local import %q in non-local package", path) |
| } |
| perr := &PackageError{ |
| ImportStack: stk.Copy(), |
| Err: err, |
| } |
| perr.setPos(importPos) |
| return p, perr |
| } |
| |
| return p, nil |
| } |
| |
| // loadPackageData loads information needed to construct a *Package. The result |
| // is cached, and later calls to loadPackageData for the same package will return |
| // the same data. |
| // |
| // loadPackageData returns a non-nil package even if err is non-nil unless |
| // the package path is malformed (for example, the path contains "mod/" or "@"). |
| // |
| // loadPackageData returns a boolean, loaded, which is true if this is the |
| // first time the package was loaded. Callers may preload imports in this case. |
| func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoot string, parentIsStd bool, mode int) (bp *build.Package, loaded bool, err error) { |
| ctx, span := trace.StartSpan(ctx, "load.loadPackageData "+path) |
| defer span.Done() |
| |
| if path == "" { |
| panic("loadPackageData called with empty package path") |
| } |
| |
| if strings.HasPrefix(path, "mod/") { |
| // Paths beginning with "mod/" might accidentally |
| // look in the module cache directory tree in $GOPATH/pkg/mod/. |
| // This prefix is owned by the Go core for possible use in the |
| // standard library (since it does not begin with a domain name), |
| // so it's OK to disallow entirely. |
| return nil, false, fmt.Errorf("disallowed import path %q", path) |
| } |
| |
| if strings.Contains(path, "@") { |
| return nil, false, errors.New("can only use path@version syntax with 'go get' and 'go install' in module-aware mode") |
| } |
| |
| // Determine canonical package path and directory. |
| // For a local import the identifier is the pseudo-import path |
| // we create from the full directory to the package. |
| // Otherwise it is the usual import path. |
| // For vendored imports, it is the expanded form. |
| // |
| // Note that when modules are enabled, local import paths are normally |
| // canonicalized by modload.LoadPackages before now. However, if there's an |
| // error resolving a local path, it will be returned untransformed |
| // so that 'go list -e' reports something useful. |
| importKey := importSpec{ |
| path: path, |
| parentPath: parentPath, |
| parentDir: parentDir, |
| parentRoot: parentRoot, |
| parentIsStd: parentIsStd, |
| mode: mode, |
| } |
| r := resolvedImportCache.Do(importKey, func() resolvedImport { |
| var r resolvedImport |
| if cfg.ModulesEnabled { |
| r.dir, r.path, r.err = modload.Lookup(parentPath, parentIsStd, path) |
| } else if build.IsLocalImport(path) { |
| r.dir = filepath.Join(parentDir, path) |
| r.path = dirToImportPath(r.dir) |
| } else if mode&ResolveImport != 0 { |
| // We do our own path resolution, because we want to |
| // find out the key to use in packageCache without the |
| // overhead of repeated calls to buildContext.Import. |
| // The code is also needed in a few other places anyway. |
| r.path = resolveImportPath(path, parentPath, parentDir, parentRoot, parentIsStd) |
| } else if mode&ResolveModule != 0 { |
| r.path = moduleImportPath(path, parentPath, parentDir, parentRoot) |
| } |
| if r.path == "" { |
| r.path = path |
| } |
| return r |
| }) |
| // Invariant: r.path is set to the resolved import path. If the path cannot |
| // be resolved, r.path is set to path, the source import path. |
| // r.path is never empty. |
| |
| // Load the package from its directory. If we already found the package's |
| // directory when resolving its import path, use that. |
| p, err := packageDataCache.Do(r.path, func() (*build.Package, error) { |
| loaded = true |
| var data struct { |
| p *build.Package |
| err error |
| } |
| if r.dir != "" { |
| var buildMode build.ImportMode |
| buildContext := cfg.BuildContext |
| if !cfg.ModulesEnabled { |
| buildMode = build.ImportComment |
| } else { |
| buildContext.GOPATH = "" // Clear GOPATH so packages are imported as pure module packages |
| } |
| modroot := modload.PackageModRoot(ctx, r.path) |
| if modroot == "" && str.HasPathPrefix(r.dir, cfg.GOROOTsrc) { |
| modroot = cfg.GOROOTsrc |
| gorootSrcCmd := filepath.Join(cfg.GOROOTsrc, "cmd") |
| if str.HasPathPrefix(r.dir, gorootSrcCmd) { |
| modroot = gorootSrcCmd |
| } |
| } |
| if modroot != "" { |
| if rp, err := modindex.GetPackage(modroot, r.dir); err == nil { |
| data.p, data.err = rp.Import(cfg.BuildContext, buildMode) |
| goto Happy |
| } else if !errors.Is(err, modindex.ErrNotIndexed) { |
| base.Fatal(err) |
| } |
| } |
| data.p, data.err = buildContext.ImportDir(r.dir, buildMode) |
| Happy: |
| if cfg.ModulesEnabled { |
| // Override data.p.Root, since ImportDir sets it to $GOPATH, if |
| // the module is inside $GOPATH/src. |
| if info := modload.PackageModuleInfo(ctx, path); info != nil { |
| data.p.Root = info.Dir |
| } |
| } |
| if r.err != nil { |
| if data.err != nil { |
| // ImportDir gave us one error, and the module loader gave us another. |
| // We arbitrarily choose to keep the error from ImportDir because |
| // that's what our tests already expect, and it seems to provide a bit |
| // more detail in most cases. |
| } else if errors.Is(r.err, imports.ErrNoGo) { |
| // ImportDir said there were files in the package, but the module |
| // loader said there weren't. Which one is right? |
| // Without this special-case hack, the TestScript/test_vet case fails |
| // on the vetfail/p1 package (added in CL 83955). |
| // Apparently, imports.ShouldBuild biases toward rejecting files |
| // with invalid build constraints, whereas ImportDir biases toward |
| // accepting them. |
| // |
| // TODO(#41410: Figure out how this actually ought to work and fix |
| // this mess). |
| } else { |
| data.err = r.err |
| } |
| } |
| } else if r.err != nil { |
| data.p = new(build.Package) |
| data.err = r.err |
| } else if cfg.ModulesEnabled && path != "unsafe" { |
| data.p = new(build.Package) |
| data.err = fmt.Errorf("unknown import path %q: internal error: module loader did not resolve import", r.path) |
| } else { |
| buildMode := build.ImportComment |
| if mode&ResolveImport == 0 || r.path != path { |
| // Not vendoring, or we already found the vendored path. |
| buildMode |= build.IgnoreVendor |
| } |
| data.p, data.err = cfg.BuildContext.Import(r.path, parentDir, buildMode) |
| } |
| data.p.ImportPath = r.path |
| |
| // Set data.p.BinDir in cases where go/build.Context.Import |
| // may give us a path we don't want. |
| if !data.p.Goroot { |
| if cfg.GOBIN != "" { |
| data.p.BinDir = cfg.GOBIN |
| } else if cfg.ModulesEnabled { |
| data.p.BinDir = modload.BinDir() |
| } |
| } |
| |
| if !cfg.ModulesEnabled && data.err == nil && |
| data.p.ImportComment != "" && data.p.ImportComment != path && |
| !strings.Contains(path, "/vendor/") && !strings.HasPrefix(path, "vendor/") { |
| data.err = fmt.Errorf("code in directory %s expects import %q", data.p.Dir, data.p.ImportComment) |
| } |
| return data.p, data.err |
| }) |
| |
| return p, loaded, err |
| } |
| |
| // importSpec describes an import declaration in source code. It is used as a |
| // cache key for resolvedImportCache. |
| type importSpec struct { |
| path string |
| parentPath, parentDir, parentRoot string |
| parentIsStd bool |
| mode int |
| } |
| |
| // resolvedImport holds a canonical identifier for a package. It may also contain |
| // a path to the package's directory and an error if one occurred. resolvedImport |
| // is the value type in resolvedImportCache. |
| type resolvedImport struct { |
| path, dir string |
| err error |
| } |
| |
| // resolvedImportCache maps import strings to canonical package names. |
| var resolvedImportCache par.Cache[importSpec, resolvedImport] |
| |
| // packageDataCache maps canonical package names (string) to package metadata. |
| var packageDataCache par.ErrCache[string, *build.Package] |
| |
| // preloadWorkerCount is the number of concurrent goroutines that can load |
| // packages. Experimentally, there are diminishing returns with more than |
| // 4 workers. This was measured on the following machines. |
| // |
| // * MacBookPro with a 4-core Intel Core i7 CPU |
| // * Linux workstation with 6-core Intel Xeon CPU |
| // * Linux workstation with 24-core Intel Xeon CPU |
| // |
| // It is very likely (though not confirmed) that this workload is limited |
| // by memory bandwidth. We don't have a good way to determine the number of |
| // workers that would saturate the bus though, so runtime.GOMAXPROCS |
| // seems like a reasonable default. |
| var preloadWorkerCount = runtime.GOMAXPROCS(0) |
| |
| // preload holds state for managing concurrent preloading of package data. |
| // |
| // A preload should be created with newPreload before loading a large |
| // package graph. flush must be called when package loading is complete |
| // to ensure preload goroutines are no longer active. This is necessary |
| // because of global mutable state that cannot safely be read and written |
| // concurrently. In particular, packageDataCache may be cleared by "go get" |
| // in GOPATH mode, and modload.loaded (accessed via modload.Lookup) may be |
| // modified by modload.LoadPackages. |
| type preload struct { |
| cancel chan struct{} |
| sema chan struct{} |
| } |
| |
| // newPreload creates a new preloader. flush must be called later to avoid |
| // accessing global state while it is being modified. |
| func newPreload() *preload { |
| pre := &preload{ |
| cancel: make(chan struct{}), |
| sema: make(chan struct{}, preloadWorkerCount), |
| } |
| return pre |
| } |
| |
| // preloadMatches loads data for package paths matched by patterns. |
| // When preloadMatches returns, some packages may not be loaded yet, but |
| // loadPackageData and loadImport are always safe to call. |
| func (pre *preload) preloadMatches(ctx context.Context, opts PackageOpts, matches []*search.Match) { |
| for _, m := range matches { |
| for _, pkg := range m.Pkgs { |
| select { |
| case <-pre.cancel: |
| return |
| case pre.sema <- struct{}{}: |
| go func(pkg string) { |
| mode := 0 // don't use vendoring or module import resolution |
| bp, loaded, err := loadPackageData(ctx, pkg, "", base.Cwd(), "", false, mode) |
| <-pre.sema |
| if bp != nil && loaded && err == nil && !opts.IgnoreImports { |
| pre.preloadImports(ctx, opts, bp.Imports, bp) |
| } |
| }(pkg) |
| } |
| } |
| } |
| } |
| |
| // preloadImports queues a list of imports for preloading. |
| // When preloadImports returns, some packages may not be loaded yet, |
| // but loadPackageData and loadImport are always safe to call. |
| func (pre *preload) preloadImports(ctx context.Context, opts PackageOpts, imports []string, parent *build.Package) { |
| parentIsStd := parent.Goroot && parent.ImportPath != "" && search.IsStandardImportPath(parent.ImportPath) |
| for _, path := range imports { |
| if path == "C" || path == "unsafe" { |
| continue |
| } |
| select { |
| case <-pre.cancel: |
| return |
| case pre.sema <- struct{}{}: |
| go func(path string) { |
| bp, loaded, err := loadPackageData(ctx, path, parent.ImportPath, parent.Dir, parent.Root, parentIsStd, ResolveImport) |
| <-pre.sema |
| if bp != nil && loaded && err == nil && !opts.IgnoreImports { |
| pre.preloadImports(ctx, opts, bp.Imports, bp) |
| } |
| }(path) |
| } |
| } |
| } |
| |
| // flush stops pending preload operations. flush blocks until preload calls to |
| // loadPackageData have completed. The preloader will not make any new calls |
| // to loadPackageData. |
| func (pre *preload) flush() { |
| // flush is usually deferred. |
| // Don't hang program waiting for workers on panic. |
| if v := recover(); v != nil { |
| panic(v) |
| } |
| |
| close(pre.cancel) |
| for i := 0; i < preloadWorkerCount; i++ { |
| pre.sema <- struct{}{} |
| } |
| } |
| |
| func cleanImport(path string) string { |
| orig := path |
| path = pathpkg.Clean(path) |
| if strings.HasPrefix(orig, "./") && path != ".." && !strings.HasPrefix(path, "../") { |
| path = "./" + path |
| } |
| return path |
| } |
| |
| var isDirCache par.Cache[string, bool] |
| |
| func isDir(path string) bool { |
| return isDirCache.Do(path, func() bool { |
| fi, err := fsys.Stat(path) |
| return err == nil && fi.IsDir() |
| }) |
| } |
| |
| // ResolveImportPath returns the true meaning of path when it appears in parent. |
| // There are two different resolutions applied. |
| // First, there is Go 1.5 vendoring (golang.org/s/go15vendor). |
| // If vendor expansion doesn't trigger, then the path is also subject to |
| // Go 1.11 module legacy conversion (golang.org/issue/25069). |
| func ResolveImportPath(parent *Package, path string) (found string) { |
| var parentPath, parentDir, parentRoot string |
| parentIsStd := false |
| if parent != nil { |
| parentPath = parent.ImportPath |
| parentDir = parent.Dir |
| parentRoot = parent.Root |
| parentIsStd = parent.Standard |
| } |
| return resolveImportPath(path, parentPath, parentDir, parentRoot, parentIsStd) |
| } |
| |
| func resolveImportPath(path, parentPath, parentDir, parentRoot string, parentIsStd bool) (found string) { |
| if cfg.ModulesEnabled { |
| if _, p, e := modload.Lookup(parentPath, parentIsStd, path); e == nil { |
| return p |
| } |
| return path |
| } |
| found = vendoredImportPath(path, parentPath, parentDir, parentRoot) |
| if found != path { |
| return found |
| } |
| return moduleImportPath(path, parentPath, parentDir, parentRoot) |
| } |
| |
| // dirAndRoot returns the source directory and workspace root |
| // for the package p, guaranteeing that root is a path prefix of dir. |
| func dirAndRoot(path string, dir, root string) (string, string) { |
| origDir, origRoot := dir, root |
| dir = filepath.Clean(dir) |
| root = filepath.Join(root, "src") |
| if !str.HasFilePathPrefix(dir, root) || path != "command-line-arguments" && filepath.Join(root, path) != dir { |
| // Look for symlinks before reporting error. |
| dir = expandPath(dir) |
| root = expandPath(root) |
| } |
| |
| if !str.HasFilePathPrefix(dir, root) || len(dir) <= len(root) || dir[len(root)] != filepath.Separator || path != "command-line-arguments" && !build.IsLocalImport(path) && filepath.Join(root, path) != dir { |
| debug.PrintStack() |
| base.Fatalf("unexpected directory layout:\n"+ |
| " import path: %s\n"+ |
| " root: %s\n"+ |
| " dir: %s\n"+ |
| " expand root: %s\n"+ |
| " expand dir: %s\n"+ |
| " separator: %s", |
| path, |
| filepath.Join(origRoot, "src"), |
| filepath.Clean(origDir), |
| origRoot, |
| origDir, |
| string(filepath.Separator)) |
| } |
| |
| return dir, root |
| } |
| |
| // vendoredImportPath returns the vendor-expansion of path when it appears in parent. |
| // If parent is x/y/z, then path might expand to x/y/z/vendor/path, x/y/vendor/path, |
| // x/vendor/path, vendor/path, or else stay path if none of those exist. |
| // vendoredImportPath returns the expanded path or, if no expansion is found, the original. |
| func vendoredImportPath(path, parentPath, parentDir, parentRoot string) (found string) { |
| if parentRoot == "" { |
| return path |
| } |
| |
| dir, root := dirAndRoot(parentPath, parentDir, parentRoot) |
| |
| vpath := "vendor/" + path |
| for i := len(dir); i >= len(root); i-- { |
| if i < len(dir) && dir[i] != filepath.Separator { |
| continue |
| } |
| // Note: checking for the vendor directory before checking |
| // for the vendor/path directory helps us hit the |
| // isDir cache more often. It also helps us prepare a more useful |
| // list of places we looked, to report when an import is not found. |
| if !isDir(filepath.Join(dir[:i], "vendor")) { |
| continue |
| } |
| targ := filepath.Join(dir[:i], vpath) |
| if isDir(targ) && hasGoFiles(targ) { |
| importPath := parentPath |
| if importPath == "command-line-arguments" { |
| // If parent.ImportPath is 'command-line-arguments'. |
| // set to relative directory to root (also chopped root directory) |
| importPath = dir[len(root)+1:] |
| } |
| // We started with parent's dir c:\gopath\src\foo\bar\baz\quux\xyzzy. |
| // We know the import path for parent's dir. |
| // We chopped off some number of path elements and |
| // added vendor\path to produce c:\gopath\src\foo\bar\baz\vendor\path. |
| // Now we want to know the import path for that directory. |
| // Construct it by chopping the same number of path elements |
| // (actually the same number of bytes) from parent's import path |
| // and then append /vendor/path. |
| chopped := len(dir) - i |
| if chopped == len(importPath)+1 { |
| // We walked up from c:\gopath\src\foo\bar |
| // and found c:\gopath\src\vendor\path. |
| // We chopped \foo\bar (length 8) but the import path is "foo/bar" (length 7). |
| // Use "vendor/path" without any prefix. |
| return vpath |
| } |
| return importPath[:len(importPath)-chopped] + "/" + vpath |
| } |
| } |
| return path |
| } |
| |
| var ( |
| modulePrefix = []byte("\nmodule ") |
| goModPathCache par.Cache[string, string] |
| ) |
| |
| // goModPath returns the module path in the go.mod in dir, if any. |
| func goModPath(dir string) (path string) { |
| return goModPathCache.Do(dir, func() string { |
| data, err := os.ReadFile(filepath.Join(dir, "go.mod")) |
| if err != nil { |
| return "" |
| } |
| var i int |
| if bytes.HasPrefix(data, modulePrefix[1:]) { |
| i = 0 |
| } else { |
| i = bytes.Index(data, modulePrefix) |
| if i < 0 { |
| return "" |
| } |
| i++ |
| } |
| line := data[i:] |
| |
| // Cut line at \n, drop trailing \r if present. |
| if j := bytes.IndexByte(line, '\n'); j >= 0 { |
| line = line[:j] |
| } |
| if line[len(line)-1] == '\r' { |
| line = line[:len(line)-1] |
| } |
| line = line[len("module "):] |
| |
| // If quoted, unquote. |
| path = strings.TrimSpace(string(line)) |
| if path != "" && path[0] == '"' { |
| s, err := strconv.Unquote(path) |
| if err != nil { |
| return "" |
| } |
| path = s |
| } |
| return path |
| }) |
| } |
| |
| // findVersionElement returns the slice indices of the final version element /vN in path. |
| // If there is no such element, it returns -1, -1. |
| func findVersionElement(path string) (i, j int) { |
| j = len(path) |
| for i = len(path) - 1; i >= 0; i-- { |
| if path[i] == '/' { |
| if isVersionElement(path[i+1 : j]) { |
| return i, j |
| } |
| j = i |
| } |
| } |
| return -1, -1 |
| } |
| |
| // isVersionElement reports whether s is a well-formed path version element: |
| // v2, v3, v10, etc, but not v0, v05, v1. |
| func isVersionElement(s string) bool { |
| if len(s) < 2 || s[0] != 'v' || s[1] == '0' || s[1] == '1' && len(s) == 2 { |
| return false |
| } |
| for i := 1; i < len(s); i++ { |
| if s[i] < '0' || '9' < s[i] { |
| return false |
| } |
| } |
| return true |
| } |
| |
| // moduleImportPath translates import paths found in go modules |
| // back down to paths that can be resolved in ordinary builds. |
| // |
| // Define “new” code as code with a go.mod file in the same directory |
| // or a parent directory. If an import in new code says x/y/v2/z but |
| // x/y/v2/z does not exist and x/y/go.mod says “module x/y/v2”, |
| // then go build will read the import as x/y/z instead. |
| // See golang.org/issue/25069. |
| func moduleImportPath(path, parentPath, parentDir, parentRoot string) (found string) { |
| if parentRoot == "" { |
| return path |
| } |
| |
| // If there are no vN elements in path, leave it alone. |
| // (The code below would do the same, but only after |
| // some other file system accesses that we can avoid |
| // here by returning early.) |
| if i, _ := findVersionElement(path); i < 0 { |
| return path |
| } |
| |
| dir, root := dirAndRoot(parentPath, parentDir, parentRoot) |
| |
| // Consider dir and parents, up to and including root. |
| for i := len(dir); i >= len(root); i-- { |
| if i < len(dir) && dir[i] != filepath.Separator { |
| continue |
| } |
| if goModPath(dir[:i]) != "" { |
| goto HaveGoMod |
| } |
| } |
| // This code is not in a tree with a go.mod, |
| // so apply no changes to the path. |
| return path |
| |
| HaveGoMod: |
| // This import is in a tree with a go.mod. |
| // Allow it to refer to code in GOPATH/src/x/y/z as x/y/v2/z |
| // if GOPATH/src/x/y/go.mod says module "x/y/v2", |
| |
| // If x/y/v2/z exists, use it unmodified. |
| if bp, _ := cfg.BuildContext.Import(path, "", build.IgnoreVendor); bp.Dir != "" { |
| return path |
| } |
| |
| // Otherwise look for a go.mod supplying a version element. |
| // Some version-like elements may appear in paths but not |
| // be module versions; we skip over those to look for module |
| // versions. For example the module m/v2 might have a |
| // package m/v2/api/v1/foo. |
| limit := len(path) |
| for limit > 0 { |
| i, j := findVersionElement(path[:limit]) |
| if i < 0 { |
| return path |
| } |
| if bp, _ := cfg.BuildContext.Import(path[:i], "", build.IgnoreVendor); bp.Dir != "" { |
| if mpath := goModPath(bp.Dir); mpath != "" { |
| // Found a valid go.mod file, so we're stopping the search. |
| // If the path is m/v2/p and we found m/go.mod that says |
| // "module m/v2", then we return "m/p". |
| if mpath == path[:j] { |
| return path[:i] + path[j:] |
| } |
| // Otherwise just return the original path. |
| // We didn't find anything worth rewriting, |
| // and the go.mod indicates that we should |
| // not consider parent directories. |
| return path |
| } |
| } |
| limit = i |
| } |
| return path |
| } |
| |
| // hasGoFiles reports whether dir contains any files with names ending in .go. |
| // For a vendor check we must exclude directories that contain no .go files. |
| // Otherwise it is not possible to vendor just a/b/c and still import the |
| // non-vendored a/b. See golang.org/issue/13832. |
| func hasGoFiles(dir string) bool { |
| files, _ := os.ReadDir(dir) |
| for _, f := range files { |
| if !f.IsDir() && strings.HasSuffix(f.Name(), ".go") { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // reusePackage reuses package p to satisfy the import at the top |
| // of the import stack stk. If this use causes an import loop, |
| // reusePackage updates p's error information to record the loop. |
| func reusePackage(p *Package, stk *ImportStack) *Package { |
| // We use p.Internal.Imports==nil to detect a package that |
| // is in the midst of its own loadPackage call |
| // (all the recursion below happens before p.Internal.Imports gets set). |
| if p.Internal.Imports == nil { |
| if p.Error == nil { |
| p.Error = &PackageError{ |
| ImportStack: stk.Copy(), |
| Err: errors.New("import cycle not allowed"), |
| IsImportCycle: true, |
| } |
| } else if !p.Error.IsImportCycle { |
| // If the error is already set, but it does not indicate that |
| // we are in an import cycle, set IsImportCycle so that we don't |
| // end up stuck in a loop down the road. |
| p.Error.IsImportCycle = true |
| } |
| p.Incomplete = true |
| } |
| // Don't rewrite the import stack in the error if we have an import cycle. |
| // If we do, we'll lose the path that describes the cycle. |
| if p.Error != nil && !p.Error.IsImportCycle && stk.shorterThan(p.Error.ImportStack) { |
| p.Error.ImportStack = stk.Copy() |
| } |
| return p |
| } |
| |
| // disallowInternal checks that srcDir (containing package importerPath, if non-empty) |
| // is allowed to import p. |
| // If the import is allowed, disallowInternal returns the original package p. |
| // If not, it returns a new package containing just an appropriate error. |
| func disallowInternal(ctx context.Context, srcDir string, importer *Package, importerPath string, p *Package, stk *ImportStack) *PackageError { |
| // golang.org/s/go14internal: |
| // An import of a path containing the element “internal” |
| // is disallowed if the importing code is outside the tree |
| // rooted at the parent of the “internal” directory. |
| |
| // There was an error loading the package; stop here. |
| if p.Error != nil { |
| return nil |
| } |
| |
| // The generated 'testmain' package is allowed to access testing/internal/..., |
| // as if it were generated into the testing directory tree |
| // (it's actually in a temporary directory outside any Go tree). |
| // This cleans up a former kludge in passing functionality to the testing package. |
| if str.HasPathPrefix(p.ImportPath, "testing/internal") && importerPath == "testmain" { |
| return nil |
| } |
| |
| // We can't check standard packages with gccgo. |
| if cfg.BuildContext.Compiler == "gccgo" && p.Standard { |
| return nil |
| } |
| |
| // The sort package depends on internal/reflectlite, but during bootstrap |
| // the path rewriting causes the normal internal checks to fail. |
| // Instead, just ignore the internal rules during bootstrap. |
| if p.Standard && strings.HasPrefix(importerPath, "bootstrap/") { |
| return nil |
| } |
| |
| // importerPath is empty: we started |
| // with a name given on the command line, not an |
| // import. Anything listed on the command line is fine. |
| if importerPath == "" { |
| return nil |
| } |
| |
| // Check for "internal" element: three cases depending on begin of string and/or end of string. |
| i, ok := findInternal(p.ImportPath) |
| if !ok { |
| return nil |
| } |
| |
| // Internal is present. |
| // Map import path back to directory corresponding to parent of internal. |
| if i > 0 { |
| i-- // rewind over slash in ".../internal" |
| } |
| |
| if p.Module == nil { |
| parent := p.Dir[:i+len(p.Dir)-len(p.ImportPath)] |
| |
| if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) { |
| return nil |
| } |
| |
| // Look for symlinks before reporting error. |
| srcDir = expandPath(srcDir) |
| parent = expandPath(parent) |
| if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) { |
| return nil |
| } |
| } else { |
| // p is in a module, so make it available based on the importer's import path instead |
| // of the file path (https://golang.org/issue/23970). |
| if importer.Internal.CmdlineFiles { |
| // The importer is a list of command-line files. |
| // Pretend that the import path is the import path of the |
| // directory containing them. |
| // If the directory is outside the main modules, this will resolve to ".", |
| // which is not a prefix of any valid module. |
| importerPath, _ = modload.MainModules.DirImportPath(ctx, importer.Dir) |
| } |
| parentOfInternal := p.ImportPath[:i] |
| if str.HasPathPrefix(importerPath, parentOfInternal) { |
| return nil |
| } |
| } |
| |
| // Internal is present, and srcDir is outside parent's tree. Not allowed. |
| perr := &PackageError{ |
| alwaysPrintStack: true, |
| ImportStack: stk.Copy(), |
| Err: ImportErrorf(p.ImportPath, "use of internal package "+p.ImportPath+" not allowed"), |
| } |
| return perr |
| } |
| |
| // findInternal looks for the final "internal" path element in the given import path. |
| // If there isn't one, findInternal returns ok=false. |
| // Otherwise, findInternal returns ok=true and the index of the "internal". |
| func findInternal(path string) (index int, ok bool) { |
| // Three cases, depending on internal at start/end of string or not. |
| // The order matters: we must return the index of the final element, |
| // because the final one produces the most restrictive requirement |
| // on the importer. |
| switch { |
| case strings.HasSuffix(path, "/internal"): |
| return len(path) - len("internal"), true |
| case strings.Contains(path, "/internal/"): |
| return strings.LastIndex(path, "/internal/") + 1, true |
| case path == "internal", strings.HasPrefix(path, "internal/"): |
| return 0, true |
| } |
| return 0, false |
| } |
| |
| // disallowVendor checks that srcDir is allowed to import p as path. |
| // If the import is allowed, disallowVendor returns the original package p. |
| // If not, it returns a PackageError. |
| func disallowVendor(srcDir string, path string, importerPath string, p *Package, stk *ImportStack) *PackageError { |
| // If the importerPath is empty, we started |
| // with a name given on the command line, not an |
| // import. Anything listed on the command line is fine. |
| if importerPath == "" { |
| return nil |
| } |
| |
| if perr := disallowVendorVisibility(srcDir, p, importerPath, stk); perr != nil { |
| return perr |
| } |
| |
| // Paths like x/vendor/y must be imported as y, never as x/vendor/y. |
| if i, ok := FindVendor(path); ok { |
| perr := &PackageError{ |
| ImportStack: stk.Copy(), |
| Err: ImportErrorf(path, "%s must be imported as %s", path, path[i+len("vendor/"):]), |
| } |
| return perr |
| } |
| |
| return nil |
| } |
| |
| // disallowVendorVisibility checks that srcDir is allowed to import p. |
| // The rules are the same as for /internal/ except that a path ending in /vendor |
| // is not subject to the rules, only subdirectories of vendor. |
| // This allows people to have packages and commands named vendor, |
| // for maximal compatibility with existing source trees. |
| func disallowVendorVisibility(srcDir string, p *Package, importerPath string, stk *ImportStack) *PackageError { |
| // The stack does not include p.ImportPath. |
| // If there's nothing on the stack, we started |
| // with a name given on the command line, not an |
| // import. Anything listed on the command line is fine. |
| if importerPath == "" { |
| return nil |
| } |
| |
| // Check for "vendor" element. |
| i, ok := FindVendor(p.ImportPath) |
| if !ok { |
| return nil |
| } |
| |
| // Vendor is present. |
| // Map import path back to directory corresponding to parent of vendor. |
| if i > 0 { |
| i-- // rewind over slash in ".../vendor" |
| } |
| truncateTo := i + len(p.Dir) - len(p.ImportPath) |
| if truncateTo < 0 || len(p.Dir) < truncateTo { |
| return nil |
| } |
| parent := p.Dir[:truncateTo] |
| if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) { |
| return nil |
| } |
| |
| // Look for symlinks before reporting error. |
| srcDir = expandPath(srcDir) |
| parent = expandPath(parent) |
| if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) { |
| return nil |
| } |
| |
| // Vendor is present, and srcDir is outside parent's tree. Not allowed. |
| |
| perr := &PackageError{ |
| ImportStack: stk.Copy(), |
| Err: errors.New("use of vendored package not allowed"), |
| } |
| return perr |
| } |
| |
| // FindVendor looks for the last non-terminating "vendor" path element in the given import path. |
| // If there isn't one, FindVendor returns ok=false. |
| // Otherwise, FindVendor returns ok=true and the index of the "vendor". |
| // |
| // Note that terminating "vendor" elements don't count: "x/vendor" is its own package, |
| // not the vendored copy of an import "" (the empty import path). |
| // This will allow people to have packages or commands named vendor. |
| // This may help reduce breakage, or it may just be confusing. We'll see. |
| func FindVendor(path string) (index int, ok bool) { |
| // Two cases, depending on internal at start of string or not. |
| // The order matters: we must return the index of the final element, |
| // because the final one is where the effective import path starts. |
| switch { |
| case strings.Contains(path, "/vendor/"): |
| return strings.LastIndex(path, "/vendor/") + 1, true |
| case strings.HasPrefix(path, "vendor/"): |
| return 0, true |
| } |
| return 0, false |
| } |
| |
| type TargetDir int |
| |
| const ( |
| ToTool TargetDir = iota // to GOROOT/pkg/tool (default for cmd/*) |
| ToBin // to bin dir inside package root (default for non-cmd/*) |
| StalePath // an old import path; fail to build |
| ) |
| |
| // InstallTargetDir reports the target directory for installing the command p. |
| func InstallTargetDir(p *Package) TargetDir { |
| if strings.HasPrefix(p.ImportPath, "code.google.com/p/go.tools/cmd/") { |
| return StalePath |
| } |
| if p.Goroot && strings.HasPrefix(p.ImportPath, "cmd/") && p.Name == "main" { |
| switch p.ImportPath { |
| case "cmd/go", "cmd/gofmt": |
| return ToBin |
| } |
| return ToTool |
| } |
| return ToBin |
| } |
| |
| var cgoExclude = map[string]bool{ |
| "runtime/cgo": true, |
| } |
| |
| var cgoSyscallExclude = map[string]bool{ |
| "runtime/cgo": true, |
| "runtime/race": true, |
| "runtime/msan": true, |
| "runtime/asan": true, |
| } |
| |
| var foldPath = make(map[string]string) |
| |
| // exeFromImportPath returns an executable name |
| // for a package using the import path. |
| // |
| // The executable name is the last element of the import path. |
| // In module-aware mode, an additional rule is used on import paths |
| // consisting of two or more path elements. If the last element is |
| // a vN path element specifying the major version, then the |
| // second last element of the import path is used instead. |
| func (p *Package) exeFromImportPath() string { |
| _, elem := pathpkg.Split(p.ImportPath) |
| if cfg.ModulesEnabled { |
| // If this is example.com/mycmd/v2, it's more useful to |
| // install it as mycmd than as v2. See golang.org/issue/24667. |
| if elem != p.ImportPath && isVersionElement(elem) { |
| _, elem = pathpkg.Split(pathpkg.Dir(p.ImportPath)) |
| } |
| } |
| return elem |
| } |
| |
| // exeFromFiles returns an executable name for a package |
| // using the first element in GoFiles or CgoFiles collections without the prefix. |
| // |
| // Returns empty string in case of empty collection. |
| func (p *Package) exeFromFiles() string { |
| var src string |
| if len(p.GoFiles) > 0 { |
| src = p.GoFiles[0] |
| } else if len(p.CgoFiles) > 0 { |
| src = p.CgoFiles[0] |
| } else { |
| return "" |
| } |
| _, elem := filepath.Split(src) |
| return elem[:len(elem)-len(".go")] |
| } |
| |
| // DefaultExecName returns the default executable name for a package |
| func (p *Package) DefaultExecName() string { |
| if p.Internal.CmdlineFiles { |
| return p.exeFromFiles() |
| } |
| return p.exeFromImportPath() |
| } |
| |
| // load populates p using information from bp, err, which should |
| // be the result of calling build.Context.Import. |
| // stk contains the import stack, not including path itself. |
| func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk *ImportStack, importPos []token.Position, bp *build.Package, err error) { |
| p.copyBuild(opts, bp) |
| |
| // The localPrefix is the path we interpret ./ imports relative to, |
| // if we support them at all (not in module mode!). |
| // Synthesized main packages sometimes override this. |
| if p.Internal.Local && !cfg.ModulesEnabled { |
| p.Internal.LocalPrefix = dirToImportPath(p.Dir) |
| } |
| |
| // setError sets p.Error if it hasn't already been set. We may proceed |
| // after encountering some errors so that 'go list -e' has more complete |
| // output. If there's more than one error, we should report the first. |
| setError := func(err error) { |
| if p.Error == nil { |
| p.Error = &PackageError{ |
| ImportStack: stk.Copy(), |
| Err: err, |
| } |
| p.Incomplete = true |
| |
| // Add the importer's position information if the import position exists, and |
| // the current package being examined is the importer. |
| // If we have not yet accepted package p onto the import stack, |
| // then the cause of the error is not within p itself: the error |
| // must be either in an explicit command-line argument, |
| // or on the importer side (indicated by a non-empty importPos). |
| if path != stk.Top() && len(importPos) > 0 { |
| p.Error.setPos(importPos) |
| } |
| } |
| } |
| |
| if err != nil { |
| p.Incomplete = true |
| p.setLoadPackageDataError(err, path, stk, importPos) |
| } |
| |
| useBindir := p.Name == "main" |
| if !p.Standard { |
| switch cfg.BuildBuildmode { |
| case "c-archive", "c-shared", "plugin": |
| useBindir = false |
| } |
| } |
| |
| if useBindir { |
| // Report an error when the old code.google.com/p/go.tools paths are used. |
| if InstallTargetDir(p) == StalePath { |
| // TODO(matloob): remove this branch, and StalePath itself. code.google.com/p/go is so |
| // old, even this code checking for it is stale now! |
| newPath := strings.Replace(p.ImportPath, "code.google.com/p/go.", "golang.org/x/", 1) |
| e := ImportErrorf(p.ImportPath, "the %v command has moved; use %v instead.", p.ImportPath, newPath) |
| setError(e) |
| return |
| } |
| elem := p.DefaultExecName() + cfg.ExeSuffix |
| full := filepath.Join(cfg.BuildContext.GOOS+"_"+cfg.BuildContext.GOARCH, elem) |
| if cfg.BuildContext.GOOS != runtime.GOOS || cfg.BuildContext.GOARCH != runtime.GOARCH { |
| // Install cross-compiled binaries to subdirectories of bin. |
| elem = full |
| } |
| if p.Internal.Build.BinDir == "" && cfg.ModulesEnabled { |
| p.Internal.Build.BinDir = modload.BinDir() |
| } |
| if p.Internal.Build.BinDir != "" { |
| // Install to GOBIN or bin of GOPATH entry. |
| p.Target = filepath.Join(p.Internal.Build.BinDir, elem) |
| if !p.Goroot && strings.Contains(elem, string(filepath.Separator)) && cfg.GOBIN != "" { |
| // Do not create $GOBIN/goos_goarch/elem. |
| p.Target = "" |
| p.Internal.GobinSubdir = true |
| } |
| } |
| if InstallTargetDir(p) == ToTool { |
| // This is for 'go tool'. |
| // Override all the usual logic and force it into the tool directory. |
| if cfg.BuildToolchainName == "gccgo" { |
| p.Target = filepath.Join(build.ToolDir, elem) |
| } else { |
| p.Target = filepath.Join(cfg.GOROOTpkg, "tool", full) |
| } |
| } |
| } else if p.Internal.Local { |
| // Local import turned into absolute path. |
| // No permanent install target. |
| p.Target = "" |
| } else if p.Standard && cfg.BuildContext.Compiler == "gccgo" { |
| // gccgo has a preinstalled standard library that cmd/go cannot rebuild. |
| p.Target = "" |
| } else { |
| p.Target = p.Internal.Build.PkgObj |
| if cfg.BuildBuildmode == "shared" && p.Internal.Build.PkgTargetRoot != "" { |
| // TODO(matloob): This shouldn't be necessary, but the cmd/cgo/internal/testshared |
| // test fails without Target set for this condition. Figure out why and |
| // fix it. |
| p.Target = filepath.Join(p.Internal.Build.PkgTargetRoot, p.ImportPath+".a") |
| } |
| if cfg.BuildLinkshared && p.Internal.Build.PkgTargetRoot != "" { |
| // TODO(bcmills): The reliance on PkgTargetRoot implies that -linkshared does |
| // not work for any package that lacks a PkgTargetRoot — such as a non-main |
| // package in module mode. We should probably fix that. |
| targetPrefix := filepath.Join(p.Internal.Build.PkgTargetRoot, p.ImportPath) |
| p.Target = targetPrefix + ".a" |
| shlibnamefile := targetPrefix + ".shlibname" |
| shlib, err := os.ReadFile(shlibnamefile) |
| if err != nil && !os.IsNotExist(err) { |
| base.Fatalf("reading shlibname: %v", err) |
| } |
| if err == nil { |
| libname := strings.TrimSpace(string(shlib)) |
| if cfg.BuildContext.Compiler == "gccgo" { |
| p.Shlib = filepath.Join(p.Internal.Build.PkgTargetRoot, "shlibs", libname) |
| } else { |
| p.Shlib = filepath.Join(p.Internal.Build.PkgTargetRoot, libname) |
| } |
| } |
| } |
| } |
| |
| // Build augmented import list to add implicit dependencies. |
| // Be careful not to add imports twice, just to avoid confusion. |
| importPaths := p.Imports |
| addImport := func(path string, forCompiler bool) { |
| for _, p := range importPaths { |
| if path == p { |
| return |
| } |
| } |
| importPaths = append(importPaths, path) |
| if forCompiler { |
| p.Internal.CompiledImports = append(p.Internal.CompiledImports, path) |
| } |
| } |
| |
| if !opts.IgnoreImports { |
| // Cgo translation adds imports of "unsafe", "runtime/cgo" and "syscall", |
| // except for certain packages, to avoid circular dependencies. |
| if p.UsesCgo() { |
| addImport("unsafe", true) |
| } |
| if p.UsesCgo() && (!p.Standard || !cgoExclude[p.ImportPath]) && cfg.BuildContext.Compiler != "gccgo" { |
| addImport("runtime/cgo", true) |
| } |
| if p.UsesCgo() && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) { |
| addImport("syscall", true) |
| } |
| |
| // SWIG adds imports of some standard packages. |
| if p.UsesSwig() { |
| addImport("unsafe", true) |
| if cfg.BuildContext.Compiler != "gccgo" { |
| addImport("runtime/cgo", true) |
| } |
| addImport("syscall", true) |
| addImport("sync", true) |
| |
| // TODO: The .swig and .swigcxx files can use |
| // %go_import directives to import other packages. |
| } |
| |
| // The linker loads implicit dependencies. |
| if p.Name == "main" && !p.Internal.ForceLibrary { |
| ldDeps, err := LinkerDeps(p) |
| if err != nil { |
| setError(err) |
| return |
| } |
| for _, dep := range ldDeps { |
| addImport(dep, false) |
| } |
| } |
| } |
| |
| // Check for case-insensitive collisions of import paths. |
| // If modifying, consider changing checkPathCollisions() in |
| // src/cmd/go/internal/modcmd/vendor.go |
| fold := str.ToFold(p.ImportPath) |
| if other := foldPath[fold]; other == "" { |
| foldPath[fold] = p.ImportPath |
| } else if other != p.ImportPath { |
| setError(ImportErrorf(p.ImportPath, "case-insensitive import collision: %q and %q", p.ImportPath, other)) |
| return |
| } |
| |
| if !SafeArg(p.ImportPath) { |
| setError(ImportErrorf(p.ImportPath, "invalid import path %q", p.ImportPath)) |
| return |
| } |
| |
| // Errors after this point are caused by this package, not the importing |
| // package. Pushing the path here prevents us from reporting the error |
| // with the position of the import declaration. |
| stk.Push(path) |
| defer stk.Pop() |
| |
| pkgPath := p.ImportPath |
| if p.Internal.CmdlineFiles { |
| pkgPath = "command-line-arguments" |
| } |
| if cfg.ModulesEnabled { |
| p.Module = modload.PackageModuleInfo(ctx, pkgPath) |
| } |
| p.DefaultGODEBUG = defaultGODEBUG(p, nil, nil, nil) |
| |
| if !opts.SuppressEmbedFiles { |
| p.EmbedFiles, p.Internal.Embed, err = resolveEmbed(p.Dir, p.EmbedPatterns) |
| if err != nil { |
| p.Incomplete = true |
| setError(err) |
| embedErr := err.(*EmbedError) |
| p.Error.setPos(p.Internal.Build.EmbedPatternPos[embedErr.Pattern]) |
| } |
| } |
| |
| // Check for case-insensitive collision of input files. |
| // To avoid problems on case-insensitive files, we reject any package |
| // where two different input files have equal names under a case-insensitive |
| // comparison. |
| inputs := p.AllFiles() |
| f1, f2 := str.FoldDup(inputs) |
| if f1 != "" { |
| setError(fmt.Errorf("case-insensitive file name collision: %q and %q", f1, f2)) |
| return |
| } |
| |
| // If first letter of input file is ASCII, it must be alphanumeric. |
| // This avoids files turning into flags when invoking commands, |
| // and other problems we haven't thought of yet. |
| // Also, _cgo_ files must be generated by us, not supplied. |
| // They are allowed to have //go:cgo_ldflag directives. |
| // The directory scan ignores files beginning with _, |
| // so we shouldn't see any _cgo_ files anyway, but just be safe. |
| for _, file := range inputs { |
| if !SafeArg(file) || strings.HasPrefix(file, "_cgo_") { |
| setError(fmt.Errorf("invalid input file name %q", file)) |
| return |
| } |
| } |
| if name := pathpkg.Base(p.ImportPath); !SafeArg(name) { |
| setError(fmt.Errorf("invalid input directory name %q", name)) |
| return |
| } |
| if strings.ContainsAny(p.Dir, "\r\n") { |
| setError(fmt.Errorf("invalid package directory %q", p.Dir)) |
| return |
| } |
| |
| // Build list of imported packages and full dependency list. |
| imports := make([]*Package, 0, len(p.Imports)) |
| for i, path := range importPaths { |
| if path == "C" { |
| continue |
| } |
| p1, err := LoadImport(ctx, opts, path, p.Dir, p, stk, p.Internal.Build.ImportPos[path], ResolveImport) |
| if err != nil && p.Error == nil { |
| p.Error = err |
| p.Incomplete = true |
| } |
| |
| path = p1.ImportPath |
| importPaths[i] = path |
| if i < len(p.Imports) { |
| p.Imports[i] = path |
| } |
| |
| imports = append(imports, p1) |
| if p1.Incomplete { |
| p.Incomplete = true |
| } |
| } |
| p.Internal.Imports = imports |
| if p.Error == nil && p.Name == "main" && !p.Internal.ForceLibrary && !p.Incomplete && !opts.SuppressBuildInfo { |
| // TODO(bcmills): loading VCS metadata can be fairly slow. |
| // Consider starting this as a background goroutine and retrieving the result |
| // asynchronously when we're actually ready to build the package, or when we |
| // actually need to evaluate whether the package's metadata is stale. |
| p.setBuildInfo(ctx, opts.AutoVCS) |
| } |
| |
| // If cgo is not enabled, ignore cgo supporting sources |
| // just as we ignore go files containing import "C". |
| if !cfg.BuildContext.CgoEnabled { |
| p.CFiles = nil |
| p.CXXFiles = nil |
| p.MFiles = nil |
| p.SwigFiles = nil |
| p.SwigCXXFiles = nil |
| // Note that SFiles are okay (they go to the Go assembler) |
| // and HFiles are okay (they might be used by the SFiles). |
| // Also Sysofiles are okay (they might not contain object |
| // code; see issue #16050). |
| } |
| |
| // The gc toolchain only permits C source files with cgo or SWIG. |
| if len(p.CFiles) > 0 && !p.UsesCgo() && !p.UsesSwig() && cfg.BuildContext.Compiler == "gc" { |
| setError(fmt.Errorf("C source files not allowed when not using cgo or SWIG: %s", strings.Join(p.CFiles, " "))) |
| return |
| } |
| |
| // C++, Objective-C, and Fortran source files are permitted only with cgo or SWIG, |
| // regardless of toolchain. |
| if len(p.CXXFiles) > 0 && !p.UsesCgo() && !p.UsesSwig() { |
| setError(fmt.Errorf("C++ source files not allowed when not using cgo or SWIG: %s", strings.Join(p.CXXFiles, " "))) |
| return |
| } |
| if len(p.MFiles) > 0 && !p.UsesCgo() && !p.UsesSwig() { |
| setError(fmt.Errorf("Objective-C source files not allowed when not using cgo or SWIG: %s", strings.Join(p.MFiles, " "))) |
| return |
| } |
| if len(p.FFiles) > 0 && !p.UsesCgo() && !p.UsesSwig() { |
| setError(fmt.Errorf("Fortran source files not allowed when not using cgo or SWIG: %s", strings.Join(p.FFiles, " "))) |
| return |
| } |
| } |
| |
| // An EmbedError indicates a problem with a go:embed directive. |
| type EmbedError struct { |
| Pattern string |
| Err error |
| } |
| |
| func (e *EmbedError) Error() string { |
| return fmt.Sprintf("pattern %s: %v", e.Pattern, e.Err) |
| } |
| |
| func (e *EmbedError) Unwrap() error { |
| return e.Err |
| } |
| |
| // ResolveEmbed resolves //go:embed patterns and returns only the file list. |
| // For use by go mod vendor to find embedded files it should copy into the |
| // vendor directory. |
| // TODO(#42504): Once go mod vendor uses load.PackagesAndErrors, just |
| // call (*Package).ResolveEmbed |
| func ResolveEmbed(dir string, patterns []string) ([]string, error) { |
| files, _, err := resolveEmbed(dir, patterns) |
| return files, err |
| } |
| |
| // resolveEmbed resolves //go:embed patterns to precise file lists. |
| // It sets files to the list of unique files matched (for go list), |
| // and it sets pmap to the more precise mapping from |
| // patterns to files. |
| func resolveEmbed(pkgdir string, patterns []string) (files []string, pmap map[string][]string, err error) { |
| var pattern string |
| defer func() { |
| if err != nil { |
| err = &EmbedError{ |
| Pattern: pattern, |
| Err: err, |
| } |
| } |
| }() |
| |
| // TODO(rsc): All these messages need position information for better error reports. |
| pmap = make(map[string][]string) |
| have := make(map[string]int) |
| dirOK := make(map[string]bool) |
| pid := 0 // pattern ID, to allow reuse of have map |
| for _, pattern = range patterns { |
| pid++ |
| |
| glob := pattern |
| all := strings.HasPrefix(pattern, "all:") |
| if all { |
| glob = pattern[len("all:"):] |
| } |
| // Check pattern is valid for //go:embed. |
| if _, err := pathpkg.Match(glob, ""); err != nil || !validEmbedPattern(glob) { |
| return nil, nil, fmt.Errorf("invalid pattern syntax") |
| } |
| |
| // Glob to find matches. |
| match, err := fsys.Glob(str.QuoteGlob(str.WithFilePathSeparator(pkgdir)) + filepath.FromSlash(glob)) |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| // Filter list of matches down to the ones that will still exist when |
| // the directory is packaged up as a module. (If p.Dir is in the module cache, |
| // only those files exist already, but if p.Dir is in the current module, |
| // then there may be other things lying around, like symbolic links or .git directories.) |
| var list []string |
| for _, file := range match { |
| // relative path to p.Dir which begins without prefix slash |
| rel := filepath.ToSlash(str.TrimFilePathPrefix(file, pkgdir)) |
| |
| what := "file" |
| info, err := fsys.Lstat(file) |
| if err != nil { |
| return nil, nil, err |
| } |
| if info.IsDir() { |
| what = "directory" |
| } |
| |
| // Check that directories along path do not begin a new module |
| // (do not contain a go.mod). |
| for dir := file; len(dir) > len(pkgdir)+1 && !dirOK[dir]; dir = filepath.Dir(dir) { |
| if _, err := fsys.Stat(filepath.Join(dir, "go.mod")); err == nil { |
| return nil, nil, fmt.Errorf("cannot embed %s %s: in different module", what, rel) |
| } |
| if dir != file { |
| if info, err := fsys.Lstat(dir); err == nil && !info.IsDir() { |
| return nil, nil, fmt.Errorf("cannot embed %s %s: in non-directory %s", what, rel, dir[len(pkgdir)+1:]) |
| } |
| } |
| dirOK[dir] = true |
| if elem := filepath.Base(dir); isBadEmbedName(elem) { |
| if dir == file { |
| return nil, nil, fmt.Errorf("cannot embed %s %s: invalid name %s", what, rel, elem) |
| } else { |
| return nil, nil, fmt.Errorf("cannot embed %s %s: in invalid directory %s", what, rel, elem) |
| } |
| } |
| } |
| |
| switch { |
| default: |
| return nil, nil, fmt.Errorf("cannot embed irregular file %s", rel) |
| |
| case info.Mode().IsRegular(): |
| if have[rel] != pid { |
| have[rel] = pid |
| list = append(list, rel) |
| } |
| |
| case info.IsDir(): |
| // Gather all files in the named directory, stopping at module boundaries |
| // and ignoring files that wouldn't be packaged into a module. |
| count := 0 |
| err := fsys.Walk(file, func(path string, info os.FileInfo, err error) error { |
| if err != nil { |
| return err |
| } |
| rel := filepath.ToSlash(str.TrimFilePathPrefix(path, pkgdir)) |
| name := info.Name() |
| if path != file && (isBadEmbedName(name) || ((name[0] == '.' || name[0] == '_') && !all)) { |
| // Ignore bad names, assuming they won't go into modules. |
| // Also avoid hidden files that user may not know about. |
| // See golang.org/issue/42328. |
| if info.IsDir() { |
| return fs.SkipDir |
| } |
| return nil |
| } |
| if info.IsDir() { |
| if _, err := fsys.Stat(filepath.Join(path, "go.mod")); err == nil { |
| return filepath.SkipDir |
| } |
| return nil |
| } |
| if !info.Mode().IsRegular() { |
| return nil |
| } |
| count++ |
| if have[rel] != pid { |
| have[rel] = pid |
| list = append(list, rel) |
| } |
| return nil |
| }) |
| if err != nil { |
| return nil, nil, err |
| } |
| if count == 0 { |
| return nil, nil, fmt.Errorf("cannot embed directory %s: contains no embeddable files", rel) |
| } |
| } |
| } |
| |
| if len(list) == 0 { |
| return nil, nil, fmt.Errorf("no matching files found") |
| } |
| sort.Strings(list) |
| pmap[pattern] = list |
| } |
| |
| for file := range have { |
| files = append(files, file) |
| } |
| sort.Strings(files) |
| return files, pmap, nil |
| } |
| |
| func validEmbedPattern(pattern string) bool { |
| return pattern != "." && fs.ValidPath(pattern) |
| } |
| |
| // isBadEmbedName reports whether name is the base name of a file that |
| // can't or won't be included in modules and therefore shouldn't be treated |
| // as existing for embedding. |
| func isBadEmbedName(name string) bool { |
| if err := module.CheckFilePath(name); err != nil { |
| return true |
| } |
| switch name { |
| // Empty string should be impossible but make it bad. |
| case "": |
| return true |
| // Version control directories won't be present in module. |
| case ".bzr", ".hg", ".git", ".svn": |
| return true |
| } |
| return false |
| } |
| |
| // vcsStatusCache maps repository directories (string) |
| // to their VCS information. |
| var vcsStatusCache par.ErrCache[string, vcs.Status] |
| |
| func appendBuildSetting(info *debug.BuildInfo, key, value string) { |
| value = strings.ReplaceAll(value, "\n", " ") // make value safe |
| info.Settings = append(info.Settings, debug.BuildSetting{Key: key, Value: value}) |
| } |
| |
| // setBuildInfo gathers build information and sets it into |
| // p.Internal.BuildInfo, which will later be formatted as a string and embedded |
| // in the binary. setBuildInfo should only be called on a main package with no |
| // errors. |
| // |
| // This information can be retrieved using debug.ReadBuildInfo. |
| // |
| // Note that the GoVersion field is not set here to avoid encoding it twice. |
| // It is stored separately in the binary, mostly for historical reasons. |
| func (p *Package) setBuildInfo(ctx context.Context, autoVCS bool) { |
| setPkgErrorf := func(format string, args ...any) { |
| if p.Error == nil { |
| p.Error = &PackageError{Err: fmt.Errorf(format, args...)} |
| p.Incomplete = true |
| } |
| } |
| |
| var debugModFromModinfo func(*modinfo.ModulePublic) *debug.Module |
| debugModFromModinfo = func(mi *modinfo.ModulePublic) *debug.Module { |
| version := mi.Version |
| if version == "" { |
| version = "(devel)" |
| } |
| dm := &debug.Module{ |
| Path: mi.Path, |
| Version: version, |
| } |
| if mi.Replace != nil { |
| dm.Replace = debugModFromModinfo(mi.Replace) |
| } else if mi.Version != "" && cfg.BuildMod != "vendor" { |
| dm.Sum = modfetch.Sum(ctx, module.Version{Path: mi.Path, Version: mi.Version}) |
| } |
| return dm |
| } |
| |
| var main debug.Module |
| if p.Module != nil { |
| main = *debugModFromModinfo(p.Module) |
| } |
| |
| visited := make(map[*Package]bool) |
| mdeps := make(map[module.Version]*debug.Module) |
| var q []*Package |
| q = append(q, p.Internal.Imports...) |
| for len(q) > 0 { |
| p1 := q[0] |
| q = q[1:] |
| if visited[p1] { |
| continue |
| } |
| visited[p1] = true |
| if p1.Module != nil { |
| m := module.Version{Path: p1.Module.Path, Version: p1.Module.Version} |
| if p1.Module.Path != main.Path && mdeps[m] == nil { |
| mdeps[m] = debugModFromModinfo(p1.Module) |
| } |
| } |
| q = append(q, p1.Internal.Imports...) |
| } |
| sortedMods := make([]module.Version, 0, len(mdeps)) |
| for mod := range mdeps { |
| sortedMods = append(sortedMods, mod) |
| } |
| gover.ModSort(sortedMods) |
| deps := make([]*debug.Module, len(sortedMods)) |
| for i, mod := range sortedMods { |
| deps[i] = mdeps[mod] |
| } |
| |
| pkgPath := p.ImportPath |
| if p.Internal.CmdlineFiles { |
| pkgPath = "command-line-arguments" |
| } |
| info := &debug.BuildInfo{ |
| Path: pkgPath, |
| Main: main, |
| Deps: deps, |
| } |
| appendSetting := func(key, value string) { |
| appendBuildSetting(info, key, value) |
| } |
| |
| // Add command-line flags relevant to the build. |
| // This is informational, not an exhaustive list. |
| // Please keep the list sorted. |
| if cfg.BuildASan { |
| appendSetting("-asan", "true") |
| } |
| if BuildAsmflags.present { |
| appendSetting("-asmflags", BuildAsmflags.String()) |
| } |
| buildmode := cfg.BuildBuildmode |
| if buildmode == "default" { |
| if p.Name == "main" { |
| buildmode = "exe" |
| } else { |
| buildmode = "archive" |
| } |
| } |
| appendSetting("-buildmode", buildmode) |
| appendSetting("-compiler", cfg.BuildContext.Compiler) |
| if gccgoflags := BuildGccgoflags.String(); gccgoflags != "" && cfg.BuildContext.Compiler == "gccgo" { |
| appendSetting("-gccgoflags", gccgoflags) |
| } |
| if gcflags := BuildGcflags.String(); gcflags != "" && cfg.BuildContext.Compiler == "gc" { |
| appendSetting("-gcflags", gcflags) |
| } |
| if ldflags := BuildLdflags.String(); ldflags != "" { |
| // https://go.dev/issue/52372: only include ldflags if -trimpath is not set, |
| // since it can include system paths through various linker flags (notably |
| // -extar, -extld, and -extldflags). |
| // |
| // TODO: since we control cmd/link, in theory we can parse ldflags to |
| // determine whether they may refer to system paths. If we do that, we can |
| // redact only those paths from the recorded -ldflags setting and still |
| // record the system-independent parts of the flags. |
| if !cfg.BuildTrimpath { |
| appendSetting("-ldflags", ldflags) |
| } |
| } |
| if cfg.BuildMSan { |
| appendSetting("-msan", "true") |
| } |
| // N.B. -pgo added later by setPGOProfilePath. |
| if cfg.BuildRace { |
| appendSetting("-race", "true") |
| } |
| if tags := cfg.BuildContext.BuildTags; len(tags) > 0 { |
| appendSetting("-tags", strings.Join(tags, ",")) |
| } |
| if cfg.BuildTrimpath { |
| appendSetting("-trimpath", "true") |
| } |
| if p.DefaultGODEBUG != "" { |
| appendSetting("DefaultGODEBUG", p.DefaultGODEBUG) |
| } |
| cgo := "0" |
| if cfg.BuildContext.CgoEnabled { |
| cgo = "1" |
| } |
| appendSetting("CGO_ENABLED", cgo) |
| // https://go.dev/issue/52372: only include CGO flags if -trimpath is not set. |
| // (If -trimpath is set, it is possible that these flags include system paths.) |
| // If cgo is involved, reproducibility is already pretty well ruined anyway, |
| // given that we aren't stamping header or library versions. |
| // |
| // TODO(bcmills): perhaps we could at least parse the flags and stamp the |
| // subset of flags that are known not to be paths? |
| if cfg.BuildContext.CgoEnabled && !cfg.BuildTrimpath { |
| for _, name := range []string{"CGO_CFLAGS", "CGO_CPPFLAGS", "CGO_CXXFLAGS", "CGO_LDFLAGS"} { |
| appendSetting(name, cfg.Getenv(name)) |
| } |
| } |
| appendSetting("GOARCH", cfg.BuildContext.GOARCH) |
| if cfg.RawGOEXPERIMENT != "" { |
| appendSetting("GOEXPERIMENT", cfg.RawGOEXPERIMENT) |
| } |
| appendSetting("GOOS", cfg.BuildContext.GOOS) |
| if key, val := cfg.GetArchEnv(); key != "" && val != "" { |
| appendSetting(key, val) |
| } |
| |
| // Add VCS status if all conditions are true: |
| // |
| // - -buildvcs is enabled. |
| // - p is a non-test contained within a main module (there may be multiple |
| // main modules in a workspace, but local replacements don't count). |
| // - Both the current directory and p's module's root directory are contained |
| // in the same local repository. |
| // - We know the VCS commands needed to get the status. |
| setVCSError := func(err error) { |
| setPkgErrorf("error obtaining VCS status: %v\n\tUse -buildvcs=false to disable VCS stamping.", err) |
| } |
| |
| var repoDir string |
| var vcsCmd *vcs.Cmd |
| var err error |
| const allowNesting = true |
| |
| wantVCS := false |
| switch cfg.BuildBuildvcs { |
| case "true": |
| wantVCS = true // Include VCS metadata even for tests if requested explicitly; see https://go.dev/issue/52648. |
| case "auto": |
| wantVCS = autoVCS && !p.IsTestOnly() |
| case "false": |
| default: |
| panic(fmt.Sprintf("unexpected value for cfg.BuildBuildvcs: %q", cfg.BuildBuildvcs)) |
| } |
| |
| if wantVCS && p.Module != nil && p.Module.Version == "" && !p.Standard { |
| if p.Module.Path == "bootstrap" && cfg.GOROOT == os.Getenv("GOROOT_BOOTSTRAP") { |
| // During bootstrapping, the bootstrap toolchain is built in module |
| // "bootstrap" (instead of "std"), with GOROOT set to GOROOT_BOOTSTRAP |
| // (so the bootstrap toolchain packages don't even appear to be in GOROOT). |
| goto omitVCS |
| } |
| repoDir, vcsCmd, err = vcs.FromDir(base.Cwd(), "", allowNesting) |
| if err != nil && !errors.Is(err, os.ErrNotExist) { |
| setVCSError(err) |
| return |
| } |
| if !str.HasFilePathPrefix(p.Module.Dir, repoDir) && |
| !str.HasFilePathPrefix(repoDir, p.Module.Dir) { |
| // The module containing the main package does not overlap with the |
| // repository containing the working directory. Don't include VCS info. |
| // If the repo contains the module or vice versa, but they are not |
| // the same directory, it's likely an error (see below). |
| goto omitVCS |
| } |
| if cfg.BuildBuildvcs == "auto" && vcsCmd != nil && vcsCmd.Cmd != "" { |
| if _, err := cfg.LookPath(vcsCmd.Cmd); err != nil { |
| // We fould a repository, but the required VCS tool is not present. |
| // "-buildvcs=auto" means that we should silently drop the VCS metadata. |
| goto omitVCS |
| } |
| } |
| } |
| if repoDir != "" && vcsCmd.Status != nil { |
| // Check that the current directory, package, and module are in the same |
| // repository. vcs.FromDir allows nested Git repositories, but nesting |
| // is not allowed for other VCS tools. The current directory may be outside |
| // p.Module.Dir when a workspace is used. |
| pkgRepoDir, _, err := vcs.FromDir(p.Dir, "", allowNesting) |
| if err != nil { |
| setVCSError(err) |
| return |
| } |
| if pkgRepoDir != repoDir { |
| if cfg.BuildBuildvcs != "auto" { |
| setVCSError(fmt.Errorf("main package is in repository %q but current directory is in repository %q", pkgRepoDir, repoDir)) |
| return |
| } |
| goto omitVCS |
| } |
| modRepoDir, _, err := vcs.FromDir(p.Module.Dir, "", allowNesting) |
| if err != nil { |
| setVCSError(err) |
| return |
| } |
| if modRepoDir != repoDir { |
| if cfg.BuildBuildvcs != "auto" { |
| setVCSError(fmt.Errorf("main module is in repository %q but current directory is in repository %q", modRepoDir, repoDir)) |
| return |
| } |
| goto omitVCS |
| } |
| |
| st, err := vcsStatusCache.Do(repoDir, func() (vcs.Status, error) { |
| return vcsCmd.Status(vcsCmd, repoDir) |
| }) |
| if err != nil { |
| setVCSError(err) |
| return |
| } |
| |
| appendSetting("vcs", vcsCmd.Cmd) |
| if st.Revision != "" { |
| appendSetting("vcs.revision", st.Revision) |
| } |
| if !st.CommitTime.IsZero() { |
| stamp := st.CommitTime.UTC().Format(time.RFC3339Nano) |
| appendSetting("vcs.time", stamp) |
| } |
| appendSetting("vcs.modified", strconv.FormatBool(st.Uncommitted)) |
| } |
| omitVCS: |
| |
| p.Internal.BuildInfo = info |
| } |
| |
| // SafeArg reports whether arg is a "safe" command-line argument, |
| // meaning that when it appears in a command-line, it probably |
| // doesn't have some special meaning other than its own name. |
| // Obviously args beginning with - are not safe (they look like flags). |
| // Less obviously, args beginning with @ are not safe (they look like |
| // GNU binutils flagfile specifiers, sometimes called "response files"). |
| // To be conservative, we reject almost any arg beginning with non-alphanumeric ASCII. |
| // We accept leading . _ and / as likely in file system paths. |
| // There is a copy of this function in cmd/compile/internal/gc/noder.go. |
| func SafeArg(name string) bool { |
| if name == "" { |
| return false |
| } |
| c := name[0] |
| return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf |
| } |
| |
| // LinkerDeps returns the list of linker-induced dependencies for main package p. |
| func LinkerDeps(p *Package) ([]string, error) { |
| // Everything links runtime. |
| deps := []string{"runtime"} |
| |
| // External linking mode forces an import of runtime/cgo. |
| if what := externalLinkingReason(p); what != "" && cfg.BuildContext.Compiler != "gccgo" { |
| if !cfg.BuildContext.CgoEnabled { |
| return nil, fmt.Errorf("%s requires external (cgo) linking, but cgo is not enabled", what) |
| } |
| deps = append(deps, "runtime/cgo") |
| } |
| // On ARM with GOARM=5, it forces an import of math, for soft floating point. |
| if cfg.Goarch == "arm" { |
| deps = append(deps, "math") |
| } |
| // Using the race detector forces an import of runtime/race. |
| if cfg.BuildRace { |
| deps = append(deps, "runtime/race") |
| } |
| // Using memory sanitizer forces an import of runtime/msan. |
| if cfg.BuildMSan { |
| deps = append(deps, "runtime/msan") |
| } |
| // Using address sanitizer forces an import of runtime/asan. |
| if cfg.BuildASan { |
| deps = append(deps, "runtime/asan") |
| } |
| // Building for coverage forces an import of runtime/coverage. |
| if cfg.BuildCover && cfg.Experiment.CoverageRedesign { |
| deps = append(deps, "runtime/coverage") |
| } |
| |
| return deps, nil |
| } |
| |
| // externalLinkingReason reports the reason external linking is required |
| // even for programs that do not use cgo, or the empty string if external |
| // linking is not required. |
| func externalLinkingReason(p *Package) (what string) { |
| // Some targets must use external linking even inside GOROOT. |
| if platform.MustLinkExternal(cfg.Goos, cfg.Goarch, false) { |
| return cfg.Goos + "/" + cfg.Goarch |
| } |
| |
| // Some build modes always require external linking. |
| switch cfg.BuildBuildmode { |
| case "c-shared", "plugin": |
| return "-buildmode=" + cfg.BuildBuildmode |
| } |
| |
| // Using -linkshared always requires external linking. |
| if cfg.BuildLinkshared { |
| return "-linkshared" |
| } |
| |
| // Decide whether we are building a PIE, |
| // bearing in mind that some systems default to PIE. |
| isPIE := false |
| if cfg.BuildBuildmode == "pie" { |
| isPIE = true |
| } else if cfg.BuildBuildmode == "default" && platform.DefaultPIE(cfg.BuildContext.GOOS, cfg.BuildContext.GOARCH, cfg.BuildRace) { |
| isPIE = true |
| } |
| // If we are building a PIE, and we are on a system |
| // that does not support PIE with internal linking mode, |
| // then we must use external linking. |
| if isPIE && !platform.InternalLinkPIESupported(cfg.BuildContext.GOOS, cfg.BuildContext.GOARCH) { |
| if cfg.BuildBuildmode == "pie" { |
| return "-buildmode=pie" |
| } |
| return "default PIE binary" |
| } |
| |
| // Using -ldflags=-linkmode=external forces external linking. |
| // If there are multiple -linkmode options, the last one wins. |
| if p != nil { |
| ldflags := BuildLdflags.For(p) |
| for i := len(ldflags) - 1; i >= 0; i-- { |
| a := ldflags[i] |
| if a == "-linkmode=external" || |
| a == "-linkmode" && i+1 < len(ldflags) && ldflags[i+1] == "external" { |
| return a |
| } else if a == "-linkmode=internal" || |
| a == "-linkmode" && i+1 < len(ldflags) && ldflags[i+1] == "internal" { |
| return "" |
| } |
| } |
| } |
| |
| return "" |
| } |
| |
| // mkAbs rewrites list, which must be paths relative to p.Dir, |
| // into a sorted list of absolute paths. It edits list in place but for |
| // convenience also returns list back to its caller. |
| func (p *Package) mkAbs(list []string) []string { |
| for i, f := range list { |
| list[i] = filepath.Join(p.Dir, f) |
| } |
| sort.Strings(list) |
| return list |
| } |
| |
| // InternalGoFiles returns the list of Go files being built for the package, |
| // using absolute paths. |
| func (p *Package) InternalGoFiles() []string { |
| return p.mkAbs(str.StringList(p.GoFiles, p.CgoFiles, p.TestGoFiles)) |
| } |
| |
| // InternalXGoFiles returns the list of Go files being built for the XTest package, |
| // using absolute paths. |
| func (p *Package) InternalXGoFiles() []string { |
| return p.mkAbs(p.XTestGoFiles) |
| } |
| |
| // InternalAllGoFiles returns the list of all Go files possibly relevant for the package, |
| // using absolute paths. "Possibly relevant" means that files are not excluded |
| // due to build tags, but files with names beginning with . or _ are still excluded. |
| func (p *Package) InternalAllGoFiles() []string { |
| return p.mkAbs(str.StringList(p.IgnoredGoFiles, p.GoFiles, p.CgoFiles, p.TestGoFiles, p.XTestGoFiles)) |
| } |
| |
| // UsesSwig reports whether the package needs to run SWIG. |
| func (p *Package) UsesSwig() bool { |
| return len(p.SwigFiles) > 0 || len(p.SwigCXXFiles) > 0 |
| } |
| |
| // UsesCgo reports whether the package needs to run cgo |
| func (p *Package) UsesCgo() bool { |
| return len(p.CgoFiles) > 0 |
| } |
| |
| // PackageList returns the list of packages in the dag rooted at roots |
| // as visited in a depth-first post-order traversal. |
| func PackageList(roots []*Package) []*Package { |
| seen := map[*Package]bool{} |
| all := []*Package{} |
| var walk func(*Package) |
| walk = func(p *Package) { |
| if seen[p] { |
| return |
| } |
| seen[p] = true |
| for _, p1 := range p.Internal.Imports { |
| walk(p1) |
| } |
| all = append(all, p) |
| } |
| for _, root := range roots { |
| walk(root) |
| } |
| return all |
| } |
| |
| // TestPackageList returns the list of packages in the dag rooted at roots |
| // as visited in a depth-first post-order traversal, including the test |
| // imports of the roots. This ignores errors in test packages. |
| func TestPackageList(ctx context.Context, opts PackageOpts, roots []*Package) []*Package { |
| seen := map[*Package]bool{} |
| all := []*Package{} |
| var walk func(*Package) |
| walk = func(p *Package) { |
| if seen[p] { |
| return |
| } |
| seen[p] = true |
| for _, p1 := range p.Internal.Imports { |
| walk(p1) |
| } |
| all = append(all, p) |
| } |
| walkTest := func(root *Package, path string) { |
| var stk ImportStack |
| p1, err := LoadImport(ctx, opts, path, root.Dir, root, &stk, root.Internal.Build.TestImportPos[path], ResolveImport) |
| if err != nil && root.Error == nil { |
| // Assign error importing the package to the importer. |
| root.Error = err |
| root.Incomplete = true |
| } |
| if p1.Error == nil { |
| walk(p1) |
| } |
| } |
| for _, root := range roots { |
| walk(root) |
| for _, path := range root.TestImports { |
| walkTest(root, path) |
| } |
| for _, path := range root.XTestImports { |
| walkTest(root, path) |
| } |
| } |
| return all |
| } |
| |
| // LoadImportWithFlags loads the package with the given import path and |
| // sets tool flags on that package. This function is useful loading implicit |
| // dependencies (like sync/atomic for coverage). |
| // TODO(jayconrod): delete this function and set flags automatically |
| // in LoadImport instead. |
| func LoadImportWithFlags(path, srcDir string, parent *Package, stk *ImportStack, importPos []token.Position, mode int) (*Package, *PackageError) { |
| p, err := LoadImport(context.TODO(), PackageOpts{}, path, srcDir, parent, stk, importPos, mode) |
| setToolFlags(p) |
| return p, err |
| } |
| |
| // LoadPackageWithFlags is the same as LoadImportWithFlags but without a parent. |
| // It's then guaranteed to not return an error |
| func LoadPackageWithFlags(path, srcDir string, stk *ImportStack, importPos []token.Position, mode int) *Package { |
| p := LoadPackage(context.TODO(), PackageOpts{}, path, srcDir, stk, importPos, mode) |
| setToolFlags(p) |
| return p |
| } |
| |
| // PackageOpts control the behavior of PackagesAndErrors and other package |
| // loading functions. |
| type PackageOpts struct { |
| // IgnoreImports controls whether we ignore explicit and implicit imports |
| // when loading packages. Implicit imports are added when supporting Cgo |
| // or SWIG and when linking main packages. |
| IgnoreImports bool |
| |
| // ModResolveTests indicates whether calls to the module loader should also |
| // resolve test dependencies of the requested packages. |
| // |
| // If ModResolveTests is true, then the module loader needs to resolve test |
| // dependencies at the same time as packages; otherwise, the test dependencies |
| // of those packages could be missing, and resolving those missing dependencies |
| // could change the selected versions of modules that provide other packages. |
| ModResolveTests bool |
| |
| // MainOnly is true if the caller only wants to load main packages. |
| // For a literal argument matching a non-main package, a stub may be returned |
| // with an error. For a non-literal argument (with "..."), non-main packages |
| // are not be matched, and their dependencies may not be loaded. A warning |
| // may be printed for non-literal arguments that match no main packages. |
| MainOnly bool |
| |
| // AutoVCS controls whether we also load version-control metadata for main packages |
| // when -buildvcs=auto (the default). |
| AutoVCS bool |
| |
| // SuppressBuildInfo is true if the caller does not need p.Stale, p.StaleReason, or p.Internal.BuildInfo |
| // to be populated on the package. |
| SuppressBuildInfo bool |
| |
| // SuppressEmbedFiles is true if the caller does not need any embed files to be populated on the |
| // package. |
| SuppressEmbedFiles bool |
| } |
| |
| // PackagesAndErrors returns the packages named by the command line arguments |
| // 'patterns'. If a named package cannot be loaded, PackagesAndErrors returns |
| // a *Package with the Error field describing the failure. If errors are found |
| // loading imported packages, the DepsErrors field is set. The Incomplete field |
| // may be set as well. |
| // |
| // To obtain a flat list of packages, use PackageList. |
| // To report errors loading packages, use ReportPackageErrors. |
| func PackagesAndErrors(ctx context.Context, opts PackageOpts, patterns []string) []*Package { |
| ctx, span := trace.StartSpan(ctx, "load.PackagesAndErrors") |
| defer span.Done() |
| |
| for _, p := range patterns { |
| // Listing is only supported with all patterns referring to either: |
| // - Files that are part of the same directory. |
| // - Explicit package paths or patterns. |
| if strings.HasSuffix(p, ".go") { |
| // We need to test whether the path is an actual Go file and not a |
| // package path or pattern ending in '.go' (see golang.org/issue/34653). |
| if fi, err := fsys.Stat(p); err == nil && !fi.IsDir() { |
| pkgs := []*Package{GoFilesPackage(ctx, opts, patterns)} |
| setPGOProfilePath(pkgs) |
| return pkgs |
| } |
| } |
| } |
| |
| var matches []*search.Match |
| if modload.Init(); cfg.ModulesEnabled { |
| modOpts := modload.PackageOpts{ |
| ResolveMissingImports: true, |
| LoadTests: opts.ModResolveTests, |
| SilencePackageErrors: true, |
| } |
| matches, _ = modload.LoadPackages(ctx, modOpts, patterns...) |
| } else { |
| noModRoots := []string{} |
| matches = search.ImportPaths(patterns, noModRoots) |
| } |
| |
| var ( |
| pkgs []*Package |
| stk ImportStack |
| seenPkg = make(map[*Package]bool) |
| ) |
| |
| pre := newPreload() |
| defer pre.flush() |
| pre.preloadMatches(ctx, opts, matches) |
| |
| for _, m := range matches { |
| for _, pkg := range m.Pkgs { |
| if pkg == "" { |
| panic(fmt.Sprintf("ImportPaths returned empty package for pattern %s", m.Pattern())) |
| } |
| mode := cmdlinePkg |
| if m.IsLiteral() { |
| // Note: do not set = m.IsLiteral unconditionally |
| // because maybe we'll see p matching both |
| // a literal and also a non-literal pattern. |
| mode |= cmdlinePkgLiteral |
| } |
| p, perr := loadImport(ctx, opts, pre, pkg, base.Cwd(), nil, &stk, nil, mode) |
| if perr != nil { |
| base.Fatalf("internal error: loadImport of %q with nil parent returned an error", pkg) |
| } |
| p.Match = append(p.Match, m.Pattern()) |
| if seenPkg[p] { |
| continue |
| } |
| seenPkg[p] = true |
| pkgs = append(pkgs, p) |
| } |
| |
| if len(m.Errs) > 0 { |
| // In addition to any packages that were actually resolved from the |
| // pattern, there was some error in resolving the pattern itself. |
| // Report it as a synthetic package. |
| p := new(Package) |
| p.ImportPath = m.Pattern() |
| // Pass an empty ImportStack and nil importPos: the error arose from a pattern, not an import. |
| var stk ImportStack |
| var importPos []token.Position |
| p.setLoadPackageDataError(m.Errs[0], m.Pattern(), &stk, importPos) |
| p.Incomplete = true |
| p.Match = append(p.Match, m.Pattern()) |
| p.Internal.CmdlinePkg = true |
| if m.IsLiteral() { |
| p.Internal.CmdlinePkgLiteral = true |
| } |
| pkgs = append(pkgs, p) |
| } |
| } |
| |
| if opts.MainOnly { |
| pkgs = mainPackagesOnly(pkgs, matches) |
| } |
| |
| // Now that CmdlinePkg is set correctly, |
| // compute the effective flags for all loaded packages |
| // (not just the ones matching the patterns but also |
| // their dependencies). |
| setToolFlags(pkgs...) |
| |
| setPGOProfilePath(pkgs) |
| |
| return pkgs |
| } |
| |
| // setPGOProfilePath sets the PGO profile path for pkgs. |
| // In -pgo=auto mode, it finds the default PGO profile. |
| func setPGOProfilePath(pkgs []*Package) { |
| updateBuildInfo := func(p *Package, file string) { |
| // Don't create BuildInfo for packages that didn't already have it. |
| if p.Internal.BuildInfo == nil { |
| return |
| } |
| |
| if cfg.BuildTrimpath { |
| appendBuildSetting(p.Internal.BuildInfo, "-pgo", filepath.Base(file)) |
| } else { |
| appendBuildSetting(p.Internal.BuildInfo, "-pgo", file) |
| } |
| // Adding -pgo breaks the sort order in BuildInfo.Settings. Restore it. |
| slices.SortFunc(p.Internal.BuildInfo.Settings, func(x, y debug.BuildSetting) int { |
| return strings.Compare(x.Key, y.Key) |
| }) |
| } |
| |
| switch cfg.BuildPGO { |
| case "off": |
| return |
| |
| case "auto": |
| // Locate PGO profiles from the main packages, and |
| // attach the profile to the main package and its |
| // dependencies. |
| // If we're building multiple main packages, they may |
| // have different profiles. We may need to split (unshare) |
| // the dependency graph so they can attach different |
| // profiles. |
| for _, p := range pkgs { |
| if p.Name != "main" { |
| continue |
| } |
| pmain := p |
| file := filepath.Join(pmain.Dir, "default.pgo") |
| if _, err := os.Stat(file); err != nil { |
| continue // no profile |
| } |
| |
| // Packages already visited. The value should replace |
| // the key, as it may be a forked copy of the original |
| // Package. |
| visited := make(map[*Package]*Package) |
| var split func(p *Package) *Package |
| split = func(p *Package) *Package { |
| if p1 := visited[p]; p1 != nil { |
| return p1 |
| } |
| |
| if len(pkgs) > 1 && p != pmain { |
| // Make a copy, then attach profile. |
| // No need to copy if there is only one root package (we can |
| // attach profile directly in-place). |
| // Also no need to copy the main package. |
| if p.Internal.PGOProfile != "" { |
| panic("setPGOProfilePath: already have profile") |
| } |
| p1 := new(Package) |
| *p1 = *p |
| // Unalias the Internal.Imports slice, which is we're going to |
| // modify. We don't copy other slices as we don't change them. |
| p1.Internal.Imports = slices.Clone(p.Internal.Imports) |
| p1.Internal.ForMain = pmain.ImportPath |
| visited[p] = p1 |
| p = p1 |
| } else { |
| visited[p] = p |
| } |
| p.Internal.PGOProfile = file |
| updateBuildInfo(p, file) |
| // Recurse to dependencies. |
| for i, pp := range p.Internal.Imports { |
| p.Internal.Imports[i] = split(pp) |
| } |
| return p |
| } |
| |
| // Replace the package and imports with the PGO version. |
| split(pmain) |
| } |
| |
| default: |
| // Profile specified from the command line. |
| // Make it absolute path, as the compiler runs on various directories. |
| file, err := filepath.Abs(cfg.BuildPGO) |
| if err != nil { |
| base.Fatalf("fail to get absolute path of PGO file %s: %v", cfg.BuildPGO, err) |
| } |
| |
| for _, p := range PackageList(pkgs) { |
| p.Internal.PGOProfile = file |
| updateBuildInfo(p, file) |
| } |
| } |
| } |
| |
| // CheckPackageErrors prints errors encountered loading pkgs and their |
| // dependencies, then exits with a non-zero status if any errors were found. |
| func CheckPackageErrors(pkgs []*Package) { |
| var anyIncomplete bool |
| for _, pkg := range pkgs { |
| if pkg.Incomplete { |
| anyIncomplete = true |
| } |
| } |
| if anyIncomplete { |
| all := PackageList(pkgs) |
| for _, p := range all { |
| if p.Error != nil { |
| base.Errorf("%v", p.Error) |
| } |
| } |
| } |
| base.ExitIfErrors() |
| |
| // Check for duplicate loads of the same package. |
| // That should be impossible, but if it does happen then |
| // we end up trying to build the same package twice, |
| // usually in parallel overwriting the same files, |
| // which doesn't work very well. |
| seen := map[string]bool{} |
| reported := map[string]bool{} |
| for _, pkg := range PackageList(pkgs) { |
| // -pgo=auto with multiple main packages can cause a package being |
| // built multiple times (with different profiles). |
| // We check that package import path + profile path is unique. |
| key := pkg.ImportPath |
| if pkg.Internal.PGOProfile != "" { |
| key += " pgo:" + pkg.Internal.PGOProfile |
| } |
| if seen[key] && !reported[key] { |
| reported[key] = true |
| base.Errorf("internal error: duplicate loads of %s", pkg.ImportPath) |
| } |
| seen[key] = true |
| } |
| base.ExitIfErrors() |
| } |
| |
| // mainPackagesOnly filters out non-main packages matched only by arguments |
| // containing "..." and returns the remaining main packages. |
| // |
| // Packages with missing, invalid, or ambiguous names may be treated as |
| // possibly-main packages. |
| // |
| // mainPackagesOnly sets a non-main package's Error field and returns it if it |
| // is named by a literal argument. |
| // |
| // mainPackagesOnly prints warnings for non-literal arguments that only match |
| // non-main packages. |
| func mainPackagesOnly(pkgs []*Package, matches []*search.Match) []*Package { |
| treatAsMain := map[string]bool{} |
| for _, m := range matches { |
| if m.IsLiteral() { |
| for _, path := range m.Pkgs { |
| treatAsMain[path] = true |
| } |
| } |
| } |
| |
| var mains []*Package |
| for _, pkg := range pkgs { |
| if pkg.Name == "main" || (pkg.Name == "" && pkg.Error != nil) { |
| treatAsMain[pkg.ImportPath] = true |
| mains = append(mains, pkg) |
| continue |
| } |
| |
| if len(pkg.InvalidGoFiles) > 0 { // TODO(#45999): && pkg.Name == "", but currently go/build sets pkg.Name arbitrarily if it is ambiguous. |
| // The package has (or may have) conflicting names, and we can't easily |
| // tell whether one of them is "main". So assume that it could be, and |
| // report an error for the package. |
| treatAsMain[pkg.ImportPath] = true |
| } |
| if treatAsMain[pkg.ImportPath] { |
| if pkg.Error == nil { |
| pkg.Error = &PackageError{Err: &mainPackageError{importPath: pkg.ImportPath}} |
| pkg.Incomplete = true |
| } |
| mains = append(mains, pkg) |
| } |
| } |
| |
| for _, m := range matches { |
| if m.IsLiteral() || len(m.Pkgs) == 0 { |
| continue |
| } |
| foundMain := false |
| for _, path := range m.Pkgs { |
| if treatAsMain[path] { |
| foundMain = true |
| break |
| } |
| } |
| if !foundMain { |
| fmt.Fprintf(os.Stderr, "go: warning: %q matched only non-main packages\n", m.Pattern()) |
| } |
| } |
| |
| return mains |
| } |
| |
| type mainPackageError struct { |
| importPath string |
| } |
| |
| func (e *mainPackageError) Error() string { |
| return fmt.Sprintf("package %s is not a main package", e.importPath) |
| } |
| |
| func (e *mainPackageError) ImportPath() string { |
| return e.importPath |
| } |
| |
| func setToolFlags(pkgs ...*Package) { |
| for _, p := range PackageList(pkgs) { |
| p.Internal.Asmflags = BuildAsmflags.For(p) |
| p.Internal.Gcflags = BuildGcflags.For(p) |
| p.Internal.Ldflags = BuildLdflags.For(p) |
| p.Internal.Gccgoflags = BuildGccgoflags.For(p) |
| } |
| } |
| |
| // GoFilesPackage creates a package for building a collection of Go files |
| // (typically named on the command line). The target is named p.a for |
| // package p or named after the first Go file for package main. |
| func GoFilesPackage(ctx context.Context, opts PackageOpts, gofiles []string) *Package { |
| modload.Init() |
| |
| for _, f := range gofiles { |
| if !strings.HasSuffix(f, ".go") { |
| pkg := new(Package) |
| pkg.Internal.Local = true |
| pkg.Internal.CmdlineFiles = true |
| pkg.Name = f |
| pkg.Error = &PackageError{ |
| Err: fmt.Errorf("named files must be .go files: %s", pkg.Name), |
| } |
| pkg.Incomplete = true |
| return pkg |
| } |
| } |
| |
| var stk ImportStack |
| ctxt := cfg.BuildContext |
| ctxt.UseAllFiles = true |
| |
| // Synthesize fake "directory" that only shows the named files, |
| // to make it look like this is a standard package or |
| // command directory. So that local imports resolve |
| // consistently, the files must all be in the same directory. |
| var dirent []fs.FileInfo |
| var dir string |
| for _, file := range gofiles { |
| fi, err := fsys.Stat(file) |
| if err != nil { |
| base.Fatalf("%s", err) |
| } |
| if fi.IsDir() { |
| base.Fatalf("%s is a directory, should be a Go file", file) |
| } |
| dir1 := filepath.Dir(file) |
| if dir == "" { |
| dir = dir1 |
| } else if dir != dir1 { |
| base.Fatalf("named files must all be in one directory; have %s and %s", dir, dir1) |
| } |
| dirent = append(dirent, fi) |
| } |
| ctxt.ReadDir = func(string) ([]fs.FileInfo, error) { return dirent, nil } |
| |
| if cfg.ModulesEnabled { |
| modload.ImportFromFiles(ctx, gofiles) |
| } |
| |
| var err error |
| if dir == "" { |
| dir = base.Cwd() |
| } |
| dir, err = filepath.Abs(dir) |
| if err != nil { |
| base.Fatalf("%s", err) |
| } |
| |
| bp, err := ctxt.ImportDir(dir, 0) |
| pkg := new(Package) |
| pkg.Internal.Local = true |
| pkg.Internal.CmdlineFiles = true |
| pkg.load(ctx, opts, "command-line-arguments", &stk, nil, bp, err) |
| if !cfg.ModulesEnabled { |
| pkg.Internal.LocalPrefix = dirToImportPath(dir) |
| } |
| pkg.ImportPath = "command-line-arguments" |
| pkg.Target = "" |
| pkg.Match = gofiles |
| |
| if pkg.Name == "main" { |
| exe := pkg.DefaultExecName() + cfg.ExeSuffix |
| |
| if cfg.GOBIN != "" { |
| pkg.Target = filepath.Join(cfg.GOBIN, exe) |
| } else if cfg.ModulesEnabled { |
| pkg.Target = filepath.Join(modload.BinDir(), exe) |
| } |
| } |
| |
| if opts.MainOnly && pkg.Name != "main" && pkg.Error == nil { |
| pkg.Error = &PackageError{Err: &mainPackageError{importPath: pkg.ImportPath}} |
| pkg.Incomplete = true |
| } |
| setToolFlags(pkg) |
| |
| return pkg |
| } |
| |
| // PackagesAndErrorsOutsideModule is like PackagesAndErrors but runs in |
| // module-aware mode and ignores the go.mod file in the current directory or any |
| // parent directory, if there is one. This is used in the implementation of 'go |
| // install pkg@version' and other commands that support similar forms. |
| // |
| // modload.ForceUseModules must be true, and modload.RootMode must be NoRoot |
| // before calling this function. |
| // |
| // PackagesAndErrorsOutsideModule imposes several constraints to avoid |
| // ambiguity. All arguments must have the same version suffix (not just a suffix |
| // that resolves to the same version). They must refer to packages in the same |
| // module, which must not be std or cmd. That module is not considered the main |
| // module, but its go.mod file (if it has one) must not contain directives that |
| // would cause it to be interpreted differently if it were the main module |
| // (replace, exclude). |
| func PackagesAndErrorsOutsideModule(ctx context.Context, opts PackageOpts, args []string) ([]*Package, error) { |
| if !modload.ForceUseModules { |
| panic("modload.ForceUseModules must be true") |
| } |
| if modload.RootMode != modload.NoRoot { |
| panic("modload.RootMode must be NoRoot") |
| } |
| |
| // Check that the arguments satisfy syntactic constraints. |
| var version string |
| var firstPath string |
| for _, arg := range args { |
| if i := strings.Index(arg, "@"); i >= 0 { |
| firstPath, version = arg[:i], arg[i+1:] |
| if version == "" { |
| return nil, fmt.Errorf("%s: version must not be empty", arg) |
| } |
| break |
| } |
| } |
| patterns := make([]string, len(args)) |
| for i, arg := range args { |
| p, found := strings.CutSuffix(arg, "@"+version) |
| if !found { |
| return nil, fmt.Errorf("%s: all arguments must refer to packages in the same module at the same version (@%s)", arg, version) |
| } |
| switch { |
| case build.IsLocalImport(p): |
| return nil, fmt.Errorf("%s: argument must be a package path, not a relative path", arg) |
| case filepath.IsAbs(p): |
| return nil, fmt.Errorf("%s: argument must be a package path, not an absolute path", arg) |
| case search.IsMetaPackage(p): |
| return nil, fmt.Errorf("%s: argument must be a package path, not a meta-package", arg) |
| case pathpkg.Clean(p) != p: |
| return nil, fmt.Errorf("%s: argument must be a clean package path", arg) |
| case !strings.Contains(p, "...") && search.IsStandardImportPath(p) && modindex.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, p): |
| return nil, fmt.Errorf("%s: argument must not be a package in the standard library", arg) |
| default: |
| patterns[i] = p |
| } |
| } |
| |
| // Query the module providing the first argument, load its go.mod file, and |
| // check that it doesn't contain directives that would cause it to be |
| // interpreted differently if it were the main module. |
| // |
| // If multiple modules match the first argument, accept the longest match |
| // (first result). It's possible this module won't provide packages named by |
| // later arguments, and other modules would. Let's not try to be too |
| // magical though. |
| allowed := modload.CheckAllowed |
| if modload.IsRevisionQuery(firstPath, version) { |
| // Don't check for retractions if a specific revision is requested. |
| allowed = nil |
| } |
| noneSelected := func(path string) (version string) { return "none" } |
| qrs, err := modload.QueryPackages(ctx, patterns[0], version, noneSelected, allowed) |
| if err != nil { |
| return nil, fmt.Errorf("%s: %w", args[0], err) |
| } |
| rootMod := qrs[0].Mod |
| deprecation, err := modload.CheckDeprecation(ctx, rootMod) |
| if err != nil { |
| return nil, fmt.Errorf("%s: %w", args[0], err) |
| } |
| if deprecation != "" { |
| fmt.Fprintf(os.Stderr, "go: module %s is deprecated: %s\n", rootMod.Path, modload.ShortMessage(deprecation, "")) |
| } |
| data, err := modfetch.GoMod(ctx, rootMod.Path, rootMod.Version) |
| if err != nil { |
| return nil, fmt.Errorf("%s: %w", args[0], err) |
| } |
| f, err := modfile.Parse("go.mod", data, nil) |
| if err != nil { |
| return nil, fmt.Errorf("%s (in %s): %w", args[0], rootMod, err) |
| } |
| directiveFmt := "%s (in %s):\n" + |
| "\tThe go.mod file for the module providing named packages contains one or\n" + |
| "\tmore %s directives. It must not contain directives that would cause\n" + |
| "\tit to be interpreted differently than if it were the main module." |
| if len(f.Replace) > 0 { |
| return nil, fmt.Errorf(directiveFmt, args[0], rootMod, "replace") |
| } |
| if len(f.Exclude) > 0 { |
| return nil, fmt.Errorf(directiveFmt, args[0], rootMod, "exclude") |
| } |
| |
| // Since we are in NoRoot mode, the build list initially contains only |
| // the dummy command-line-arguments module. Add a requirement on the |
| // module that provides the packages named on the command line. |
| if _, err := modload.EditBuildList(ctx, nil, []module.Version{rootMod}); err != nil { |
| return nil, fmt.Errorf("%s: %w", args[0], err) |
| } |
| |
| // Load packages for all arguments. |
| pkgs := PackagesAndErrors(ctx, opts, patterns) |
| |
| // Check that named packages are all provided by the same module. |
| for _, pkg := range pkgs { |
| var pkgErr error |
| if pkg.Module == nil { |
| // Packages in std, cmd, and their vendored dependencies |
| // don't have this field set. |
| pkgErr = fmt.Errorf("package %s not provided by module %s", pkg.ImportPath, rootMod) |
| } else if pkg.Module.Path != rootMod.Path || pkg.Module.Version != rootMod.Version { |
| pkgErr = fmt.Errorf("package %s provided by module %s@%s\n\tAll packages must be provided by the same module (%s).", pkg.ImportPath, pkg.Module.Path, pkg.Module.Version, rootMod) |
| } |
| if pkgErr != nil && pkg.Error == nil { |
| pkg.Error = &PackageError{Err: pkgErr} |
| pkg.Incomplete = true |
| } |
| } |
| |
| matchers := make([]func(string) bool, len(patterns)) |
| for i, p := range patterns { |
| if strings.Contains(p, "...") { |
| matchers[i] = pkgpattern.MatchPattern(p) |
| } |
| } |
| return pkgs, nil |
| } |
| |
| // EnsureImport ensures that package p imports the named package. |
| func EnsureImport(p *Package, pkg string) { |
| for _, d := range p.Internal.Imports { |
| if d.Name == pkg { |
| return |
| } |
| } |
| |
| p1, err := LoadImportWithFlags(pkg, p.Dir, p, &ImportStack{}, nil, 0) |
| if err != nil { |
| base.Fatalf("load %s: %v", pkg, err) |
| } |
| if p1.Error != nil { |
| base.Fatalf("load %s: %v", pkg, p1.Error) |
| } |
| |
| p.Internal.Imports = append(p.Internal.Imports, p1) |
| } |
| |
| // PrepareForCoverageBuild is a helper invoked for "go install |
| // -cover", "go run -cover", and "go build -cover" (but not used by |
| // "go test -cover"). It walks through the packages being built (and |
| // dependencies) and marks them for coverage instrumentation when |
| // appropriate, and possibly adding additional deps where needed. |
| func PrepareForCoverageBuild(pkgs []*Package) { |
| var match []func(*Package) bool |
| |
| matchMainModAndCommandLine := func(p *Package) bool { |
| // note that p.Standard implies p.Module == nil below. |
| return p.Internal.CmdlineFiles || p.Internal.CmdlinePkg || (p.Module != nil && p.Module.Main) |
| } |
| |
| if len(cfg.BuildCoverPkg) != 0 { |
| // If -coverpkg has been specified, then we instrument only |
| // the specific packages selected by the user-specified pattern(s). |
| match = make([]func(*Package) bool, len(cfg.BuildCoverPkg)) |
| for i := range cfg.BuildCoverPkg { |
| match[i] = MatchPackage(cfg.BuildCoverPkg[i], base.Cwd()) |
| } |
| } else { |
| // Without -coverpkg, instrument only packages in the main module |
| // (if any), as well as packages/files specifically named on the |
| // command line. |
| match = []func(*Package) bool{matchMainModAndCommandLine} |
| } |
| |
| // Visit the packages being built or installed, along with all of |
| // their dependencies, and mark them to be instrumented, taking |
| // into account the matchers we've set up in the sequence above. |
| SelectCoverPackages(PackageList(pkgs), match, "build") |
| } |
| |
| func SelectCoverPackages(roots []*Package, match []func(*Package) bool, op string) []*Package { |
| var warntag string |
| var includeMain bool |
| switch op { |
| case "build": |
| warntag = "built" |
| includeMain = true |
| case "test": |
| warntag = "tested" |
| default: |
| panic("internal error, bad mode passed to SelectCoverPackages") |
| } |
| |
| covered := []*Package{} |
| matched := make([]bool, len(match)) |
| for _, p := range roots { |
| haveMatch := false |
| for i := range match { |
| if match[i](p) { |
| matched[i] = true |
| haveMatch = true |
| } |
| } |
| if !haveMatch { |
| continue |
| } |
| |
| // There is nothing to cover in package unsafe; it comes from |
| // the compiler. |
| if p.ImportPath == "unsafe" { |
| continue |
| } |
| |
| // A package which only has test files can't be imported as a |
| // dependency, and at the moment we don't try to instrument it |
| // for coverage. There isn't any technical reason why |
| // *_test.go files couldn't be instrumented, but it probably |
| // doesn't make much sense to lump together coverage metrics |
| // (ex: percent stmts covered) of *_test.go files with |
| // non-test Go code. |
| if len(p.GoFiles)+len(p.CgoFiles) == 0 { |
| continue |
| } |
| |
| // Silently ignore attempts to run coverage on sync/atomic |
| // and/or internal/runtime/atomic when using atomic coverage |
| // mode. Atomic coverage mode uses sync/atomic, so we can't |
| // also do coverage on it. |
| if cfg.BuildCoverMode == "atomic" && p.Standard && |
| (p.ImportPath == "sync/atomic" || p.ImportPath == "internal/runtime/atomic") { |
| continue |
| } |
| |
| // If using the race detector, silently ignore attempts to run |
| // coverage on the runtime packages. It will cause the race |
| // detector to be invoked before it has been initialized. Note |
| // the use of "regonly" instead of just ignoring the package |
| // completely-- we do this due to the requirements of the |
| // package ID numbering scheme. See the comment in |
| // $GOROOT/src/internal/coverage/pkid.go dealing with |
| // hard-coding of runtime package IDs. |
| cmode := cfg.BuildCoverMode |
| if cfg.BuildRace && p.Standard && (p.ImportPath == "runtime" || strings.HasPrefix(p.ImportPath, "runtime/internal")) { |
| cmode = "regonly" |
| } |
| |
| // If -coverpkg is in effect and for some reason we don't want |
| // coverage data for the main package, make sure that we at |
| // least process it for registration hooks. |
| if includeMain && p.Name == "main" && !haveMatch { |
| haveMatch = true |
| cmode = "regonly" |
| } |
| |
| // Mark package for instrumentation. |
| p.Internal.Cover.Mode = cmode |
| covered = append(covered, p) |
| |
| // Force import of sync/atomic into package if atomic mode. |
| if cfg.BuildCoverMode == "atomic" { |
| EnsureImport(p, "sync/atomic") |
| } |
| |
| // Generate covervars if using legacy coverage design. |
| if !cfg.Experiment.CoverageRedesign { |
| var coverFiles []string |
| coverFiles = append(coverFiles, p.GoFiles...) |
| coverFiles = append(coverFiles, p.CgoFiles...) |
| p.Internal.CoverVars = DeclareCoverVars(p, coverFiles...) |
| } |
| } |
| |
| // Warn about -coverpkg arguments that are not actually used. |
| for i := range cfg.BuildCoverPkg { |
| if !matched[i] { |
| fmt.Fprintf(os.Stderr, "warning: no packages being %s depend on matches for pattern %s\n", warntag, cfg.BuildCoverPkg[i]) |
| } |
| } |
| |
| return covered |
| } |
| |
| // DeclareCoverVars attaches the required cover variables names |
| // to the files, to be used when annotating the files. This |
| // function only called when using legacy coverage test/build |
| // (e.g. GOEXPERIMENT=coverageredesign is off). |
| func DeclareCoverVars(p *Package, files ...string) map[string]*CoverVar { |
| coverVars := make(map[string]*CoverVar) |
| coverIndex := 0 |
| // We create the cover counters as new top-level variables in the package. |
| // We need to avoid collisions with user variables (GoCover_0 is unlikely but still) |
| // and more importantly with dot imports of other covered packages, |
| // so we append 12 hex digits from the SHA-256 of the import path. |
| // The point is only to avoid accidents, not to defeat users determined to |
| // break things. |
| sum := sha256.Sum256([]byte(p.ImportPath)) |
| h := fmt.Sprintf("%x", sum[:6]) |
| for _, file := range files { |
| if base.IsTestFile(file) { |
| continue |
| } |
| // For a package that is "local" (imported via ./ import or command line, outside GOPATH), |
| // we record the full path to the file name. |
| // Otherwise we record the import path, then a forward slash, then the file name. |
| // This makes profiles within GOPATH file system-independent. |
| // These names appear in the cmd/cover HTML interface. |
| var longFile string |
| if p.Internal.Local { |
| longFile = filepath.Join(p.Dir, file) |
| } else { |
| longFile = pathpkg.Join(p.ImportPath, file) |
| } |
| coverVars[file] = &CoverVar{ |
| File: longFile, |
| Var: fmt.Sprintf("GoCover_%d_%x", coverIndex, h), |
| } |
| coverIndex++ |
| } |
| return coverVars |
| } |