// 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 buildimport (pathpkg_// for linkname)// A Context specifies the supporting context for a build.typeContextstruct { GOARCH string// target architecture GOOS string// target operating system GOROOT string// Go root GOPATH string// Go paths// Dir is the caller's working directory, or the empty string to use // the current directory of the running process. In module mode, this is used // to locate the main module. // // If Dir is non-empty, directories passed to Import and ImportDir must // be absolute. Dir string CgoEnabled bool// whether cgo files are included UseAllFiles bool// use files regardless of go:build lines, file names Compiler string// compiler to assume when computing target paths// The build, tool, and release tags specify build constraints // that should be considered satisfied when processing go:build lines. // Clients creating a new context may customize BuildTags, which // defaults to empty, but it is usually an error to customize ToolTags or ReleaseTags. // ToolTags defaults to build tags appropriate to the current Go toolchain configuration. // ReleaseTags defaults to the list of Go releases the current release is compatible with. // BuildTags is not set for the Default build Context. // In addition to the BuildTags, ToolTags, and ReleaseTags, build constraints // consider the values of GOARCH and GOOS as satisfied tags. // The last element in ReleaseTags is assumed to be the current release. BuildTags []string ToolTags []string ReleaseTags []string// The install suffix specifies a suffix to use in the name of the installation // directory. By default it is empty, but custom builds that need to keep // their outputs separate can set InstallSuffix to do so. For example, when // using the race detector, the go command uses InstallSuffix = "race", so // that on a Linux/386 system, packages are written to a directory named // "linux_386_race" instead of the usual "linux_386". InstallSuffix string// By default, Import uses the operating system's file system calls // to read directories and files. To read from other sources, // callers can set the following functions. They all have default // behaviors that use the local file system, so clients need only set // the functions whose behaviors they wish to change.// JoinPath joins the sequence of path fragments into a single path. // If JoinPath is nil, Import uses filepath.Join. JoinPath func(elem ...string) string// SplitPathList splits the path list into a slice of individual paths. // If SplitPathList is nil, Import uses filepath.SplitList. SplitPathList func(list string) []string// IsAbsPath reports whether path is an absolute path. // If IsAbsPath is nil, Import uses filepath.IsAbs. IsAbsPath func(path string) bool// IsDir reports whether the path names a directory. // If IsDir is nil, Import calls os.Stat and uses the result's IsDir method. IsDir func(path string) bool// HasSubdir reports whether dir is lexically a subdirectory of // root, perhaps multiple levels below. It does not try to check // whether dir exists. // If so, HasSubdir sets rel to a slash-separated path that // can be joined to root to produce a path equivalent to dir. // If HasSubdir is nil, Import uses an implementation built on // filepath.EvalSymlinks. HasSubdir func(root, dir string) (rel string, ok bool)// ReadDir returns a slice of fs.FileInfo, sorted by Name, // describing the content of the named directory. // If ReadDir is nil, Import uses os.ReadDir. ReadDir func(dir string) ([]fs.FileInfo, error)// OpenFile opens a file (not a directory) for reading. // If OpenFile is nil, Import uses os.Open. OpenFile func(path string) (io.ReadCloser, error)}// joinPath calls ctxt.JoinPath (if not nil) or else filepath.Join.func ( *Context) ( ...string) string {if := .JoinPath; != nil {return (...) }returnfilepath.Join(...)}// splitPathList calls ctxt.SplitPathList (if not nil) or else filepath.SplitList.func ( *Context) ( string) []string {if := .SplitPathList; != nil {return () }returnfilepath.SplitList()}// isAbsPath calls ctxt.IsAbsPath (if not nil) or else filepath.IsAbs.func ( *Context) ( string) bool {if := .IsAbsPath; != nil {return () }returnfilepath.IsAbs()}// isDir calls ctxt.IsDir (if not nil) or else uses os.Stat.func ( *Context) ( string) bool {if := .IsDir; != nil {return () } , := os.Stat()return == nil && .IsDir()}// hasSubdir calls ctxt.HasSubdir (if not nil) or else uses// the local file system to answer the question.func ( *Context) (, string) ( string, bool) {if := .HasSubdir; != nil {return (, ) }// Try using paths we received.if , = hasSubdir(, ); {return }// Try expanding symlinks and comparing // expanded against unexpanded and // expanded against expanded. , := filepath.EvalSymlinks() , := filepath.EvalSymlinks()if , = hasSubdir(, ); {return }if , = hasSubdir(, ); {return }returnhasSubdir(, )}// hasSubdir reports if dir is within root by performing lexical analysis only.func hasSubdir(, string) ( string, bool) {const = string(filepath.Separator) = filepath.Clean()if !strings.HasSuffix(, ) { += } = filepath.Clean() , := strings.CutPrefix(, )if ! {return"", false }returnfilepath.ToSlash(), true}// readDir calls ctxt.ReadDir (if not nil) or else os.ReadDir.func ( *Context) ( string) ([]fs.DirEntry, error) {// TODO: add a fs.DirEntry version of Context.ReadDirif := .ReadDir; != nil { , := ()if != nil {returnnil, } := make([]fs.DirEntry, len())for , := range { [] = fs.FileInfoToDirEntry() }return , nil }returnos.ReadDir()}// openFile calls ctxt.OpenFile (if not nil) or else os.Open.func ( *Context) ( string) (io.ReadCloser, error) {if := .OpenFile; != nil {return () } , := os.Open()if != nil {returnnil, // nil interface }return , nil}// isFile determines whether path is a file by trying to open it.// It reuses openFile instead of adding another function to the// list in Context.func ( *Context) ( string) bool { , := .openFile()if != nil {returnfalse } .Close()returntrue}// gopath returns the list of Go path directories.func ( *Context) () []string {var []stringfor , := range .splitPathList(.GOPATH) {if == "" || == .GOROOT {// Empty paths are uninteresting. // If the path is the GOROOT, ignore it. // People sometimes set GOPATH=$GOROOT. // Do not get confused by this common mistake.continue }ifstrings.HasPrefix(, "~") {// Path segments starting with ~ on Unix are almost always // users who have incorrectly quoted ~ while setting GOPATH, // preventing it from expanding to $HOME. // The situation is made more confusing by the fact that // bash allows quoted ~ in $PATH (most shells do not). // Do not get confused by this, and do not try to use the path. // It does not exist, and printing errors about it confuses // those users even more, because they think "sure ~ exists!". // The go command diagnoses this situation and prints a // useful error. // On Windows, ~ is used in short names, such as c:\progra~1 // for c:\program files.continue } = append(, ) }return}// SrcDirs returns a list of package source root directories.// It draws from the current Go root and Go path but omits directories// that do not exist.func ( *Context) () []string {var []stringif .GOROOT != "" && .Compiler != "gccgo" { := .joinPath(.GOROOT, "src")if .isDir() { = append(, ) } }for , := range .gopath() { := .joinPath(, "src")if .isDir() { = append(, ) } }return}// Default is the default Context for builds.// It uses the GOARCH, GOOS, GOROOT, and GOPATH environment variables// if set, or else the compiled code's GOARCH, GOOS, and GOROOT.varDefaultContext = defaultContext()// Keep consistent with cmd/go/internal/cfg.defaultGOPATH.func defaultGOPATH() string { := "HOME"ifruntime.GOOS == "windows" { = "USERPROFILE" } elseifruntime.GOOS == "plan9" { = "home" }if := os.Getenv(); != "" { := filepath.Join(, "go")iffilepath.Clean() == filepath.Clean(runtime.GOROOT()) {// Don't set the default GOPATH to GOROOT, // as that will trigger warnings from the go tool.return"" }return }return""}// defaultToolTags should be an internal detail,// but widely used packages access it using linkname.// Notable members of the hall of shame include:// - github.com/gopherjs/gopherjs//// Do not remove or change the type signature.// See go.dev/issue/67401.////go:linkname defaultToolTagsvar defaultToolTags []string// defaultReleaseTags should be an internal detail,// but widely used packages access it using linkname.// Notable members of the hall of shame include:// - github.com/gopherjs/gopherjs//// Do not remove or change the type signature.// See go.dev/issue/67401.////go:linkname defaultReleaseTagsvar defaultReleaseTags []stringfunc defaultContext() Context {varContext .GOARCH = buildcfg.GOARCH .GOOS = buildcfg.GOOSif := runtime.GOROOT(); != "" { .GOROOT = filepath.Clean() } .GOPATH = envOr("GOPATH", defaultGOPATH()) .Compiler = runtime.Compiler .ToolTags = append(.ToolTags, buildcfg.ToolTags...)defaultToolTags = append([]string{}, .ToolTags...) // our own private copy// Each major Go release in the Go 1.x series adds a new // "go1.x" release tag. That is, the go1.x tag is present in // all releases >= Go 1.x. Code that requires Go 1.x or later // should say "go:build go1.x", and code that should only be // built before Go 1.x (perhaps it is the stub to use in that // case) should say "go:build !go1.x". // The last element in ReleaseTags is the current release.for := 1; <= goversion.Version; ++ { .ReleaseTags = append(.ReleaseTags, "go1."+strconv.Itoa()) }defaultReleaseTags = append([]string{}, .ReleaseTags...) // our own private copy := os.Getenv("CGO_ENABLED")if == "" { = defaultCGO_ENABLED }switch {case"1": .CgoEnabled = truecase"0": .CgoEnabled = falsedefault:// cgo must be explicitly enabled for cross compilation buildsifruntime.GOARCH == .GOARCH && runtime.GOOS == .GOOS { .CgoEnabled = platform.CgoSupported(.GOOS, .GOARCH)break } .CgoEnabled = false }return}func envOr(, string) string { := os.Getenv()if == "" {return }return}// An ImportMode controls the behavior of the Import method.typeImportModeuintconst (// If FindOnly is set, Import stops after locating the directory // that should contain the sources for a package. It does not // read any files in the directory.FindOnlyImportMode = 1 << iota// If AllowBinary is set, Import can be satisfied by a compiled // package object without corresponding sources. // // Deprecated: // The supported way to create a compiled-only package is to // write source code containing a //go:binary-only-package comment at // the top of the file. Such a package will be recognized // regardless of this flag setting (because it has source code) // and will have BinaryOnly set to true in the returned Package.AllowBinary// If ImportComment is set, parse import comments on package statements. // Import returns an error if it finds a comment it cannot understand // or finds conflicting comments in multiple source files. // See golang.org/s/go14customimport for more information.ImportComment// By default, Import searches vendor directories // that apply in the given source directory before searching // the GOROOT and GOPATH roots. // If an Import finds and returns a package using a vendor // directory, the resulting ImportPath is the complete path // to the package, including the path elements leading up // to and including "vendor". // For example, if Import("y", "x/subdir", 0) finds // "x/vendor/y", the returned package's ImportPath is "x/vendor/y", // not plain "y". // See golang.org/s/go15vendor for more information. // // Setting IgnoreVendor ignores vendor directories. // // In contrast to the package's ImportPath, // the returned package's Imports, TestImports, and XTestImports // are always the exact import paths from the source files: // Import makes no attempt to resolve or check those paths.IgnoreVendor)// A Package describes the Go package found in a directory.typePackagestruct { Dir string// directory containing package sources Name string// package name ImportComment string// path in import comment on package statement Doc string// documentation synopsis ImportPath string// import path of package ("" if unknown) Root string// root of Go tree where this package lives SrcRoot string// package source root directory ("" if unknown) PkgRoot string// package install root directory ("" if unknown) PkgTargetRoot string// architecture dependent install root directory ("" if unknown) BinDir string// command install directory ("" if unknown) Goroot bool// package found in Go root PkgObj string// installed .a file AllTags []string// tags that can influence file selection in this directory ConflictDir string// this directory shadows Dir in $GOPATH BinaryOnly bool// cannot be rebuilt from source (has //go:binary-only-package comment)// Source files GoFiles []string// .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) CgoFiles []string// .go source files that import "C" IgnoredGoFiles []string// .go source files ignored for this build (including ignored _test.go files) InvalidGoFiles []string// .go source files with detected problems (parse error, wrong package name, and so on) IgnoredOtherFiles []string// non-.go source files ignored for this build CFiles []string// .c source files CXXFiles []string// .cc, .cpp and .cxx source files MFiles []string// .m (Objective-C) source files HFiles []string// .h, .hh, .hpp and .hxx source files FFiles []string// .f, .F, .for and .f90 Fortran source files SFiles []string// .s source files SwigFiles []string// .swig files SwigCXXFiles []string// .swigcxx files SysoFiles []string// .syso system object files to add to archive// Cgo directives CgoCFLAGS []string// Cgo CFLAGS directives CgoCPPFLAGS []string// Cgo CPPFLAGS directives CgoCXXFLAGS []string// Cgo CXXFLAGS directives CgoFFLAGS []string// Cgo FFLAGS directives CgoLDFLAGS []string// Cgo LDFLAGS directives CgoPkgConfig []string// Cgo pkg-config directives// Test information TestGoFiles []string// _test.go files in package XTestGoFiles []string// _test.go files outside package// Go directive comments (//go:zzz...) found in source files. Directives []Directive TestDirectives []Directive XTestDirectives []Directive// Dependency information Imports []string// import paths from GoFiles, CgoFiles ImportPos map[string][]token.Position// line information for Imports TestImports []string// import paths from TestGoFiles TestImportPos map[string][]token.Position// line information for TestImports XTestImports []string// import paths from XTestGoFiles XTestImportPos map[string][]token.Position// line information for XTestImports// //go:embed patterns found in Go source files // For example, if a source file says // //go:embed a* b.c // then the list will contain those two strings as separate entries. // (See package embed for more details about //go:embed.) EmbedPatterns []string// patterns from GoFiles, CgoFiles EmbedPatternPos map[string][]token.Position// line information for EmbedPatterns TestEmbedPatterns []string// patterns from TestGoFiles TestEmbedPatternPos map[string][]token.Position// line information for TestEmbedPatterns XTestEmbedPatterns []string// patterns from XTestGoFiles XTestEmbedPatternPos map[string][]token.Position// line information for XTestEmbedPatternPos}// A Directive is a Go directive comment (//go:zzz...) found in a source file.typeDirectivestruct { Text string// full line comment including leading slashes Pos token.Position// position of comment}// IsCommand reports whether the package is considered a// command to be installed (not just a library).// Packages named "main" are treated as commands.func ( *Package) () bool {return .Name == "main"}// ImportDir is like [Import] but processes the Go package found in// the named directory.func ( *Context) ( string, ImportMode) (*Package, error) {return .Import(".", , )}// NoGoError is the error used by [Import] to describe a directory// containing no buildable Go source files. (It may still contain// test files, files hidden by build tags, and so on.)typeNoGoErrorstruct { Dir string}func ( *NoGoError) () string {return"no buildable Go source files in " + .Dir}// MultiplePackageError describes a directory containing// multiple buildable Go source files for multiple packages.typeMultiplePackageErrorstruct { Dir string// directory containing files Packages []string// package names found Files []string// corresponding files: Files[i] declares package Packages[i]}func ( *MultiplePackageError) () string {// Error string limited to two entries for compatibility.returnfmt.Sprintf("found packages %s (%s) and %s (%s) in %s", .Packages[0], .Files[0], .Packages[1], .Files[1], .Dir)}func nameExt( string) string { := strings.LastIndex(, ".")if < 0 {return"" }return [:]}var installgoroot = godebug.New("installgoroot")// Import returns details about the Go package named by the import path,// interpreting local import paths relative to the srcDir directory.// If the path is a local import path naming a package that can be imported// using a standard import path, the returned package will set p.ImportPath// to that path.//// In the directory containing the package, .go, .c, .h, and .s files are// considered part of the package except for://// - .go files in package documentation// - files starting with _ or . (likely editor temporary files)// - files with build constraints not satisfied by the context//// If an error occurs, Import returns a non-nil error and a non-nil// *[Package] containing partial information.func ( *Context) ( string, string, ImportMode) (*Package, error) { := &Package{ImportPath: , }if == "" {return , fmt.Errorf("import %q: invalid import path", ) }varstringvarstringvarerror := ""if .InstallSuffix != "" { = "_" + .InstallSuffix }switch .Compiler {case"gccgo": = "pkg/gccgo_" + .GOOS + "_" + .GOARCH + case"gc": = "pkg/" + .GOOS + "_" + .GOARCH + default:// Save error for end of function. = fmt.Errorf("import %q: unknown compiler %q", , .Compiler) } := func() {switch .Compiler {case"gccgo": , := pathpkg.Split(.ImportPath) = + "/" + + "lib" + + ".a"case"gc": = + "/" + .ImportPath + ".a" } } () := falseifIsLocalImport() { = ""// local imports have no installed pathif == "" {return , fmt.Errorf("import %q: import relative to unknown directory", ) }if !.isAbsPath() { .Dir = .joinPath(, ) }// p.Dir directory may or may not exist. Gather partial information first, check if it exists later. // Determine canonical import path, if any. // Exclude results where the import path would include /testdata/. := func( string) bool {returnstrings.Contains(, "/testdata/") || strings.HasSuffix(, "/testdata") || strings.HasPrefix(, "testdata/") || == "testdata" }if .GOROOT != "" { := .joinPath(.GOROOT, "src")if , := .hasSubdir(, .Dir); && !() { .Goroot = true .ImportPath = .Root = .GOROOT () // p.ImportPath changedgoto } } := .gopath()for , := range { := .joinPath(, "src")if , := .hasSubdir(, .Dir); && !() {// We found a potential import path for dir, // but check that using it wouldn't find something // else first.if .GOROOT != "" && .Compiler != "gccgo" {if := .joinPath(.GOROOT, "src", ); .isDir() { .ConflictDir = goto } }for , := range [:] {if := .joinPath(, "src", ); .isDir() { .ConflictDir = goto } }// sub would not name some other directory instead of this one. // Record it. .ImportPath = .Root = () // p.ImportPath changedgoto } }// It's okay that we didn't find a root containing dir. // Keep going with the information we have. } else {ifstrings.HasPrefix(, "/") {return , fmt.Errorf("import %q: cannot import absolute path", ) }if := .importGo(, , , ); == nil {goto } elseif != errNoModules {return , } := .gopath() // needed twice below; avoid computing many times// tried records the location of unsuccessful package lookupsvarstruct { []stringstring []string }// Vendor directories get first chance to satisfy import.if &IgnoreVendor == 0 && != "" { := func( string, bool) bool { , := .hasSubdir(, )if ! || !strings.HasPrefix(, "src/") || strings.Contains(, "/testdata/") {returnfalse }for { := .joinPath(, , "vendor")if .isDir() { := .joinPath(, )if .isDir() && hasGoFiles(, ) { .Dir = .ImportPath = strings.TrimPrefix(pathpkg.Join(, "vendor", ), "src/") .Goroot = .Root = () // p.ImportPath changedreturntrue } . = append(., ) } := strings.LastIndex(, "/")if < 0 {break } = [:] }returnfalse }if .Compiler != "gccgo" && .GOROOT != "" && (.GOROOT, true) {goto }for , := range {if (, false) {goto } } }// Determine directory from import path.if .GOROOT != "" {// If the package path starts with "vendor/", only search GOROOT before // GOPATH if the importer is also within GOROOT. That way, if the user has // vendored in a package that is subsequently included in the standard // distribution, they'll continue to pick up their own vendored copy. := == "" || !strings.HasPrefix(, "vendor/")if ! { _, = .hasSubdir(.GOROOT, ) }if { := .joinPath(.GOROOT, "src", )if .Compiler != "gccgo" { := .isDir() = ! && &AllowBinary != 0 && != "" && .isFile(.joinPath(.GOROOT, ))if || { .Dir = .Goroot = true .Root = .GOROOTgoto } } . = }if .Compiler == "gccgo" && goroot.IsStandardPackage(.GOROOT, .Compiler, ) {// TODO(bcmills): Setting p.Dir here is misleading, because gccgo // doesn't actually load its standard-library packages from this // directory. See if we can leave it unset. .Dir = .joinPath(.GOROOT, "src", ) .Goroot = true .Root = .GOROOTgoto } }for , := range { := .joinPath(, "src", ) := .isDir() = ! && &AllowBinary != 0 && != "" && .isFile(.joinPath(, ))if || { .Dir = .Root = goto } . = append(., ) }// If we tried GOPATH first due to a "vendor/" prefix, fall back to GOPATH. // That way, the user can still get useful results from 'go list' for // standard-vendored paths passed on the command line.if .GOROOT != "" && . == "" { := .joinPath(.GOROOT, "src", )if .Compiler != "gccgo" { := .isDir() = ! && &AllowBinary != 0 && != "" && .isFile(.joinPath(.GOROOT, ))if || { .Dir = .Goroot = true .Root = .GOROOTgoto } } . = }// package was not foundvar []string := "\t%s (vendor tree)"for , := range . { = append(, fmt.Sprintf(, )) = "\t%s" }if . != "" { = append(, fmt.Sprintf("\t%s (from $GOROOT)", .)) } else { = append(, "\t($GOROOT not set)") } = "\t%s (from $GOPATH)"for , := range . { = append(, fmt.Sprintf(, )) = "\t%s" }iflen(.) == 0 { = append(, "\t($GOPATH not set. For more details see: 'go help gopath')") }return , fmt.Errorf("cannot find package %q in any of:\n%s", , strings.Join(, "\n")) }:if .Root != "" { .SrcRoot = .joinPath(.Root, "src") .PkgRoot = .joinPath(.Root, "pkg") .BinDir = .joinPath(.Root, "bin")if != "" {// Always set PkgTargetRoot. It might be used when building in shared // mode. .PkgTargetRoot = .joinPath(.Root, )// Set the install target if applicable.if !.Goroot || (installgoroot.Value() == "all" && .ImportPath != "unsafe" && .ImportPath != "builtin") {if .Goroot {installgoroot.IncNonDefault() } .PkgObj = .joinPath(.Root, ) } } }// If it's a local import path, by the time we get here, we still haven't checked // that p.Dir directory exists. This is the right time to do that check. // We can't do it earlier, because we want to gather partial information for the // non-nil *Package returned when an error occurs. // We need to do this before we return early on FindOnly flag.ifIsLocalImport() && !.isDir(.Dir) {if .Compiler == "gccgo" && .Goroot {// gccgo has no sources for GOROOT packages.return , nil }// package was not foundreturn , fmt.Errorf("cannot find package %q in:\n\t%s", .ImportPath, .Dir) }if &FindOnly != 0 {return , }if && (&AllowBinary) != 0 {return , }if .Compiler == "gccgo" && .Goroot {// gccgo has no sources for GOROOT packages.return , nil } , := .readDir(.Dir)if != nil {return , }varerror := make(map[string]bool) := func( string, error) {if == nil { = }if ![] { .InvalidGoFiles = append(.InvalidGoFiles, ) [] = true } }var []string// files with ".S"(capital S)/.sx(capital s equivalent for case insensitive filesystems)var , string := make(map[string][]token.Position) := make(map[string][]token.Position) := make(map[string][]token.Position) := make(map[string][]token.Position) := make(map[string][]token.Position) := make(map[string][]token.Position) := make(map[string]bool) := token.NewFileSet()for , := range {if .IsDir() {continue }if .Type() == fs.ModeSymlink {if .isDir(.joinPath(.Dir, .Name())) {// Symlinks to directories are not source files.continue } } := .Name() := nameExt() , := .matchFile(.Dir, , , &.BinaryOnly, )if != nil && strings.HasSuffix(, ".go") { (, )continue }if == nil {ifstrings.HasPrefix(, "_") || strings.HasPrefix(, ".") {// not due to build constraints - don't report } elseif == ".go" { .IgnoredGoFiles = append(.IgnoredGoFiles, ) } elseiffileListForExt(, ) != nil { .IgnoredOtherFiles = append(.IgnoredOtherFiles, ) }continue }// Going to save the file. For non-Go files, can stop here.switch {case".go":// keep goingcase".S", ".sx":// special case for cgo, handled at end = append(, )continuedefault:if := fileListForExt(, ); != nil { * = append(*, ) }continue } , := .header, .nameif .parseErr != nil { (, .parseErr)// Fall through: we might still have a partial AST in info.parsed, // and we want to list files with parse errors anyway. }varstringif .parsed != nil { = .parsed.Name.Nameif == "documentation" { .IgnoredGoFiles = append(.IgnoredGoFiles, )continue } } := strings.HasSuffix(, "_test.go") := falseif && strings.HasSuffix(, "_test") && .Name != { = true = [:len()-len("_test")] }if .Name == "" { .Name = = } elseif != .Name {// TODO(#45999): The choice of p.Name is arbitrary based on file iteration // order. Instead of resolving p.Name arbitrarily, we should clear out the // existing name and mark the existing files as also invalid. (, &MultiplePackageError{Dir: .Dir,Packages: []string{.Name, },Files: []string{, }, }) }// Grab the first package comment as docs, provided it is not from a test file.if .parsed != nil && .parsed.Doc != nil && .Doc == "" && ! && ! { .Doc = doc.Synopsis(.parsed.Doc.Text()) }if &ImportComment != 0 { , := findImportComment()if != 0 { , := strconv.Unquote()if != nil { (, fmt.Errorf("%s:%d: cannot parse import comment", , )) } elseif .ImportComment == "" { .ImportComment = = } elseif .ImportComment != { (, fmt.Errorf("found import comments %q (%s) and %q (%s) in %s", .ImportComment, , , , .Dir)) } } }// Record imports and information about cgo. := falsefor , := range .imports {if .path == "C" {if { (, fmt.Errorf("use of cgo in test %s not supported", ))continue } = trueif .doc != nil {if := .saveCgo(, , .doc); != nil { (, ) } } } }var *[]stringvar , map[string][]token.Positionvar *[]Directiveswitch {case : ["cgo"] = trueif .CgoEnabled { = &.CgoFiles = = = &.Directives } else {// Ignore imports and embeds from cgo files if cgo is disabled. = &.IgnoredGoFiles }case : = &.XTestGoFiles = = = &.XTestDirectivescase : = &.TestGoFiles = = = &.TestDirectivesdefault: = &.GoFiles = = = &.Directives } * = append(*, )if != nil {for , := range .imports { [.path] = append([.path], .Position(.pos)) } }if != nil {for , := range .embeds { [.pattern] = append([.pattern], .pos) } }if != nil { * = append(*, .directives...) } }for := range { .AllTags = append(.AllTags, ) }slices.Sort(.AllTags) .EmbedPatterns, .EmbedPatternPos = cleanDecls() .TestEmbedPatterns, .TestEmbedPatternPos = cleanDecls() .XTestEmbedPatterns, .XTestEmbedPatternPos = cleanDecls() .Imports, .ImportPos = cleanDecls() .TestImports, .TestImportPos = cleanDecls() .XTestImports, .XTestImportPos = cleanDecls()// add the .S/.sx files only if we are using cgo // (which means gcc will compile them). // The standard assemblers expect .s files.iflen(.CgoFiles) > 0 { .SFiles = append(.SFiles, ...)slices.Sort(.SFiles) } else { .IgnoredOtherFiles = append(.IgnoredOtherFiles, ...)slices.Sort(.IgnoredOtherFiles) }if != nil {return , }iflen(.GoFiles)+len(.CgoFiles)+len(.TestGoFiles)+len(.XTestGoFiles) == 0 {return , &NoGoError{.Dir} }return , }func fileListForExt( *Package, string) *[]string {switch {case".c":return &.CFilescase".cc", ".cpp", ".cxx":return &.CXXFilescase".m":return &.MFilescase".h", ".hh", ".hpp", ".hxx":return &.HFilescase".f", ".F", ".for", ".f90":return &.FFilescase".s", ".S", ".sx":return &.SFilescase".swig":return &.SwigFilescase".swigcxx":return &.SwigCXXFilescase".syso":return &.SysoFiles }returnnil}func uniq( []string) []string {if == nil {returnnil } := make([]string, len())copy(, )slices.Sort() := [:0]for , := range {iflen() == 0 || [len()-1] != { = append(, ) } }return}var errNoModules = errors.New("not using modules")// importGo checks whether it can use the go command to find the directory for path.// If using the go command is not appropriate, importGo returns errNoModules.// Otherwise, importGo tries using the go command and reports whether that succeeded.// Using the go command lets build.Import and build.Context.Import find code// in Go modules. In the long term we want tools to use go/packages (currently golang.org/x/tools/go/packages),// which will also use the go command.// Invoking the go command here is not very efficient in that it computes information// about the requested package and all dependencies and then only reports about the requested package.// Then we reinvoke it for every dependency. But this is still better than not working at all.// See golang.org/issue/26504.func ( *Context) ( *Package, , string, ImportMode) error {// To invoke the go command, // we must not being doing special things like AllowBinary or IgnoreVendor, // and all the file system callbacks must be nil (we're meant to use the local file system).if &AllowBinary != 0 || &IgnoreVendor != 0 || .JoinPath != nil || .SplitPathList != nil || .IsAbsPath != nil || .IsDir != nil || .HasSubdir != nil || .ReadDir != nil || .OpenFile != nil || !equal(.ToolTags, defaultToolTags) || !equal(.ReleaseTags, defaultReleaseTags) {returnerrNoModules }// If ctxt.GOROOT is not set, we don't know which go command to invoke, // and even if we did we might return packages in GOROOT that we wouldn't otherwise find // (because we don't know to search in 'go env GOROOT' otherwise).if .GOROOT == "" {returnerrNoModules }// Predict whether module aware mode is enabled by checking the value of // GO111MODULE and looking for a go.mod file in the source directory or // one of its parents. Running 'go env GOMOD' in the source directory would // give a canonical answer, but we'd prefer not to execute another command. := os.Getenv("GO111MODULE")switch {case"off":returnerrNoModulesdefault: // "", "on", "auto", anything else// Maybe use modules. }if != "" {varstringiffilepath.IsAbs() { = } elseif .Dir != "" {returnfmt.Errorf("go/build: Dir is non-empty, so relative srcDir is not allowed: %v", ) } else {// Find the absolute source directory. hasSubdir does not handle // relative paths (and can't because the callbacks don't support this).varerror , = filepath.Abs()if != nil {returnerrNoModules } }// If the source directory is in GOROOT, then the in-process code works fine // and we should keep using it. Moreover, the 'go list' approach below doesn't // take standard-library vendoring into account and will fail.if , := .hasSubdir(filepath.Join(.GOROOT, "src"), ); {returnerrNoModules } }// For efficiency, if path is a standard library package, let the usual lookup code handle it.if := .joinPath(.GOROOT, "src", ); .isDir() {returnerrNoModules }// If GO111MODULE=auto, look to see if there is a go.mod. // Since go1.13, it doesn't matter if we're inside GOPATH.if == "auto" {var (stringerror )if .Dir == "" { , = os.Getwd()if != nil {// A nonexistent working directory can't be in a module.returnerrNoModules } } else { , = filepath.Abs(.Dir)if != nil {// If the caller passed a bogus Dir explicitly, that's materially // different from not having modules enabled.return } }for {if , := .openFile(.joinPath(, "go.mod")); == nil { := make([]byte, 100) , := .Read() .Close()if == nil || == io.EOF {// go.mod exists and is readable (is a file, not a directory).break } } := filepath.Dir()iflen() >= len() {returnerrNoModules// reached top of file system, no go.mod } = } } := filepath.Join(.GOROOT, "bin", "go") := exec.Command(, "list", "-e", "-compiler="+.Compiler, "-tags="+strings.Join(.BuildTags, ","), "-installsuffix="+.InstallSuffix, "-f={{.Dir}}\n{{.ImportPath}}\n{{.Root}}\n{{.Goroot}}\n{{if .Error}}{{.Error}}{{end}}\n", "--", )if .Dir != "" { .Dir = .Dir }var , strings.Builder .Stdout = & .Stderr = & := "0"if .CgoEnabled { = "1" } .Env = append(.Environ(),"GOOS="+.GOOS,"GOARCH="+.GOARCH,"GOROOT="+.GOROOT,"GOPATH="+.GOPATH,"CGO_ENABLED="+, )if := .Run(); != nil {returnfmt.Errorf("go/build: go list %s: %v\n%s\n", , , .String()) } := strings.SplitN(.String(), "\n", 5)iflen() != 5 {returnfmt.Errorf("go/build: importGo %s: unexpected output:\n%s\n", , .String()) } := [0] := strings.TrimSpace([4])if != "" && == "" {// If 'go list' could not locate the package (dir is empty), // return the same error that 'go list' reported.returnerrors.New() }// If 'go list' did locate the package, ignore the error. // It was probably related to loading source files, and we'll // encounter it ourselves shortly if the FindOnly flag isn't set. .Dir = .ImportPath = [1] .Root = [2] .Goroot = [3] == "true"returnnil}func equal(, []string) bool {iflen() != len() {returnfalse }for , := range {if != [] {returnfalse } }returntrue}// 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( *Context, string) bool { , := .readDir()for , := range {if !.IsDir() && strings.HasSuffix(.Name(), ".go") {returntrue } }returnfalse}func findImportComment( []byte) ( string, int) {// expect keyword package , := parseWord()ifstring() != "package" {return"", 0 }// expect package name _, = parseWord()// now ready for import comment, a // or /* */ comment // beginning and ending on the current line.forlen() > 0 && ([0] == ' ' || [0] == '\t' || [0] == '\r') { = [1:] }var []byteswitch {casebytes.HasPrefix(, slashSlash): , _, _ = bytes.Cut([2:], newline)casebytes.HasPrefix(, slashStar):varbool , _, = bytes.Cut([2:], starSlash)if ! {// malformed commentreturn"", 0 }ifbytes.Contains(, newline) {return"", 0 } } = bytes.TrimSpace()// split comment into `import`, `"pkg"` , := parseWord()ifstring() != "import" {return"", 0 } = 1 + bytes.Count([:cap()-cap()], newline)returnstrings.TrimSpace(string()), }var ( slashSlash = []byte("//") slashStar = []byte("/*") starSlash = []byte("*/") newline = []byte("\n"))// skipSpaceOrComment returns data with any leading spaces or comments removed.func skipSpaceOrComment( []byte) []byte {forlen() > 0 {switch [0] {case' ', '\t', '\r', '\n': = [1:]continuecase'/':ifbytes.HasPrefix(, slashSlash) { := bytes.Index(, newline)if < 0 {returnnil } = [+1:]continue }ifbytes.HasPrefix(, slashStar) { = [2:] := bytes.Index(, starSlash)if < 0 {returnnil } = [+2:]continue } }break }return}// parseWord skips any leading spaces or comments in data// and then parses the beginning of data as an identifier or keyword,// returning that word and what remains after the word.func parseWord( []byte) (, []byte) { = skipSpaceOrComment()// Parse past leading word characters. = for { , := utf8.DecodeRune()ifunicode.IsLetter() || '0' <= && <= '9' || == '_' { = [:]continue }break } = [:len()-len()]iflen() == 0 {returnnil, nil }return , }// MatchFile reports whether the file with the given name in the given directory// matches the context and would be included in a [Package] created by [ImportDir]// of that directory.//// MatchFile considers the name of the file and may use ctxt.OpenFile to// read some or all of the file's content.func ( *Context) (, string) ( bool, error) { , := .matchFile(, , nil, nil, nil)return != nil, }var dummyPkg Package// fileInfo records information learned about a file included in a build.type fileInfo struct { name string// full name including dir header []byte fset *token.FileSet parsed *ast.File parseErr error imports []fileImport embeds []fileEmbed directives []Directive}type fileImport struct { path string pos token.Pos doc *ast.CommentGroup}type fileEmbed struct { pattern string pos token.Position}// matchFile determines whether the file with the given name in the given directory// should be included in the package being constructed.// If the file should be included, matchFile returns a non-nil *fileInfo (and a nil error).// Non-nil errors are reserved for unexpected problems.//// If name denotes a Go program, matchFile reads until the end of the// imports and returns that section of the file in the fileInfo's header field,// even though it only considers text until the first non-comment// for go:build lines.//// If allTags is non-nil, matchFile records any encountered build tag// by setting allTags[tag] = true.func ( *Context) (, string, map[string]bool, *bool, *token.FileSet) (*fileInfo, error) {ifstrings.HasPrefix(, "_") ||strings.HasPrefix(, ".") {returnnil, nil } := strings.LastIndex(, ".")if < 0 { = len() } := [:]if != ".go" && fileListForExt(&dummyPkg, ) == nil {// skipreturnnil, nil }if !.goodOSArchFile(, ) && !.UseAllFiles {returnnil, nil } := &fileInfo{name: .joinPath(, ), fset: }if == ".syso" {// binary, no readingreturn , nil } , := .openFile(.name)if != nil {returnnil, }ifstrings.HasSuffix(, ".go") { = readGoInfo(, )ifstrings.HasSuffix(, "_test.go") { = nil// ignore //go:binary-only-package comments in _test.go files } } else { = nil// ignore //go:binary-only-package comments in non-Go sources .header, = readComments() } .Close()if != nil {return , fmt.Errorf("read %s: %v", .name, ) }// Look for go:build comments to accept or reject the file. , , := .shouldBuild(.header, )if != nil {returnnil, fmt.Errorf("%s: %v", , ) }if ! && !.UseAllFiles {returnnil, nil }if != nil && { * = true }return , nil}func cleanDecls( map[string][]token.Position) ([]string, map[string][]token.Position) { := make([]string, 0, len())for := range { = append(, ) }slices.Sort()return , }// Import is shorthand for Default.Import.func (, string, ImportMode) (*Package, error) {returnDefault.Import(, , )}// ImportDir is shorthand for Default.ImportDir.func ( string, ImportMode) (*Package, error) {returnDefault.ImportDir(, )}var ( plusBuild = []byte("+build") goBuildComment = []byte("//go:build") errMultipleGoBuild = errors.New("multiple //go:build comments"))func isGoBuildComment( []byte) bool {if !bytes.HasPrefix(, goBuildComment) {returnfalse } = bytes.TrimSpace() := [len(goBuildComment):]returnlen() == 0 || len(bytes.TrimSpace()) < len()}// Special comment denoting a binary-only package.// See https://golang.org/design/2775-binary-only-packages// for more about the design of binary-only packages.var binaryOnlyComment = []byte("//go:binary-only-package")// shouldBuild reports whether it is okay to use this file,// The rule is that in the file's leading run of // comments// and blank lines, which must be followed by a blank line// (to avoid including a Go package clause doc comment),// lines beginning with '//go:build' are taken as build directives.//// The file is accepted only if each such line lists something// matching the file. For example://// //go:build windows linux//// marks the file as applicable only on Windows and Linux.//// For each build tag it consults, shouldBuild sets allTags[tag] = true.//// shouldBuild reports whether the file should be built// and whether a //go:binary-only-package comment was found.func ( *Context) ( []byte, map[string]bool) (, bool, error) {// Identify leading run of // comments and blank lines, // which must be followed by a blank line. // Also identify any //go:build comments. , , , := parseFileHeader()if != nil {returnfalse, false, }// If //go:build line is present, it controls. // Otherwise fall back to +build processing.switch {case != nil: , := constraint.Parse(string())if != nil {returnfalse, false, fmt.Errorf("parsing //go:build line: %v", ) } = .eval(, )default: = true := forlen() > 0 { := if := bytes.IndexByte(, '\n'); >= 0 { , = [:], [+1:] } else { = [len():] } = bytes.TrimSpace()if !bytes.HasPrefix(, slashSlash) || !bytes.Contains(, plusBuild) {continue } := string()if !constraint.IsPlusBuild() {continue }if , := constraint.Parse(); == nil {if !.eval(, ) { = false } } } }return , , nil}// parseFileHeader should be an internal detail,// but widely used packages access it using linkname.// Notable members of the hall of shame include:// - github.com/bazelbuild/bazel-gazelle//// Do not remove or change the type signature.// See go.dev/issue/67401.////go:linkname parseFileHeaderfunc parseFileHeader( []byte) (, []byte, bool, error) { := 0 := := false// found non-blank, non-// line, so stopped accepting //go:build lines := false// in /* */ comment:forlen() > 0 { := if := bytes.IndexByte(, '\n'); >= 0 { , = [:], [+1:] } else { = [len():] } = bytes.TrimSpace()iflen() == 0 && ! { // Blank line// Remember position of most recent blank line. // When we find the first non-blank, non-// line, // this "end" position marks the latest file position // where a //go:build line can appear. // (It must appear _before_ a blank line before the non-blank, non-// line. // Yes, that's confusing, which is part of why we moved to //go:build lines.) // Note that ended==false here means that inSlashStar==false, // since seeing a /* would have set ended==true. = len() - len()continue }if !bytes.HasPrefix(, slashSlash) { // Not comment line = true }if ! && isGoBuildComment() {if != nil {returnnil, nil, false, errMultipleGoBuild } = }if ! && bytes.Equal(, binaryOnlyComment) { = true } :forlen() > 0 {if {if := bytes.Index(, starSlash); >= 0 { = false = bytes.TrimSpace([+len(starSlash):])continue }continue }ifbytes.HasPrefix(, slashSlash) {continue }ifbytes.HasPrefix(, slashStar) { = true = bytes.TrimSpace([len(slashStar):])continue }// Found non-comment text.break } }return [:], , , nil}// saveCgo saves the information from the #cgo lines in the import "C" comment.// These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives// that affect the way cgo's C code is built.func ( *Context) ( string, *Package, *ast.CommentGroup) error { := .Text()for , := rangestrings.Split(, "\n") { := // Line is // #cgo [GOOS/GOARCH...] LDFLAGS: stuff // = strings.TrimSpace()iflen() < 5 || [:4] != "#cgo" || ([4] != ' ' && [4] != '\t') {continue }// #cgo (nocallback|noescape) <function name>if := strings.Fields(); len() == 3 && ([1] == "nocallback" || [1] == "noescape") {continue }// Split at colon. , , := strings.Cut(strings.TrimSpace([4:]), ":")if ! {returnfmt.Errorf("%s: invalid #cgo line: %s", , ) }// Parse GOOS/GOARCH stuff. := strings.Fields()iflen() < 1 {returnfmt.Errorf("%s: invalid #cgo line: %s", , ) } , := [:len()-1], [len()-1]iflen() > 0 { := falsefor , := range {if .matchAuto(, nil) { = truebreak } }if ! {continue } } , := splitQuoted()if != nil {returnfmt.Errorf("%s: invalid #cgo line: %s", , ) }for , := range {if , = expandSrcDir(, .Dir); ! {returnfmt.Errorf("%s: malformed #cgo argument: %s", , ) } [] = }switch {case"CFLAGS", "CPPFLAGS", "CXXFLAGS", "FFLAGS", "LDFLAGS":// Change relative paths to absolute. .makePathsAbsolute(, .Dir) }switch {case"CFLAGS": .CgoCFLAGS = append(.CgoCFLAGS, ...)case"CPPFLAGS": .CgoCPPFLAGS = append(.CgoCPPFLAGS, ...)case"CXXFLAGS": .CgoCXXFLAGS = append(.CgoCXXFLAGS, ...)case"FFLAGS": .CgoFFLAGS = append(.CgoFFLAGS, ...)case"LDFLAGS": .CgoLDFLAGS = append(.CgoLDFLAGS, ...)case"pkg-config": .CgoPkgConfig = append(.CgoPkgConfig, ...)default:returnfmt.Errorf("%s: invalid #cgo verb: %s", , ) } }returnnil}// expandSrcDir expands any occurrence of ${SRCDIR}, making sure// the result is safe for the shell.func expandSrcDir( string, string) (string, bool) {// "\" delimited paths cause safeCgoName to fail // so convert native paths with a different delimiter // to "/" before starting (eg: on windows). = filepath.ToSlash() := strings.Split(, "${SRCDIR}")iflen() < 2 {return , safeCgoName() } := truefor , := range { = && ( == "" || safeCgoName()) } = && ( == "" || safeCgoName()) := strings.Join(, )return , && != ""}// makePathsAbsolute looks for compiler options that take paths and// makes them absolute. We do this because through the 1.8 release we// ran the compiler in the package directory, so any relative -I or -L// options would be relative to that directory. In 1.9 we changed to// running the compiler in the build directory, to get consistent// build results (issue #19964). To keep builds working, we change any// relative -I or -L options to be absolute.//// Using filepath.IsAbs and filepath.Join here means the results will be// different on different systems, but that's OK: -I and -L options are// inherently system-dependent.func ( *Context) ( []string, string) { := falsefor , := range {if {if !filepath.IsAbs() { [] = filepath.Join(, ) } = false } elseifstrings.HasPrefix(, "-I") || strings.HasPrefix(, "-L") {iflen() == 2 { = true } else {if !filepath.IsAbs([2:]) { [] = [:2] + filepath.Join(, [2:]) } } } }}// NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN.// We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay.// See golang.org/issue/6038.// The @ is for OS X. See golang.org/issue/13720.// The % is for Jenkins. See golang.org/issue/16959.// The ! is because module paths may use them. See golang.org/issue/26716.// The ~ and ^ are for sr.ht. See golang.org/issue/32260.const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@%! ~^"func safeCgoName( string) bool {if == "" {returnfalse }for := 0; < len(); ++ {if := []; < utf8.RuneSelf && strings.IndexByte(safeString, ) < 0 {returnfalse } }returntrue}// splitQuoted splits the string s around each instance of one or more consecutive// white space characters while taking into account quotes and escaping, and// returns an array of substrings of s or an empty list if s contains only white space.// Single quotes and double quotes are recognized to prevent splitting within the// quoted region, and are removed from the resulting substrings. If a quote in s// isn't closed err will be set and r will have the unclosed argument as the// last element. The backslash is used for escaping.//// For example, the following string://// a b:"c d" 'e''f' "g\""//// Would be parsed as://// []string{"a", "b:c d", "ef", `g"`}func splitQuoted( string) ( []string, error) {var []string := make([]rune, len()) := false := false := '\x00' := 0for , := range {switch {case : = falsecase == '\\': = truecontinuecase != '\x00':if == { = '\x00'continue }case == '"' || == '\'': = true = continuecaseunicode.IsSpace():if || > 0 { = false = append(, string([:])) = 0 }continue } [] = ++ }if || > 0 { = append(, string([:])) }if != 0 { = errors.New("unclosed quote") } elseif { = errors.New("unfinished escaping") }return , }// matchAuto interprets text as either a +build or //go:build expression (whichever works),// reporting whether the expression matches the build context.//// matchAuto is only used for testing of tag evaluation// and in #cgo lines, which accept either syntax.func ( *Context) ( string, map[string]bool) bool {ifstrings.ContainsAny(, "&|()") { = "//go:build " + } else { = "// +build " + } , := constraint.Parse()if != nil {returnfalse }return .eval(, )}func ( *Context) ( constraint.Expr, map[string]bool) bool {return .Eval(func( string) bool { return .matchTag(, ) })}// matchTag reports whether the name is one of://// cgo (if cgo is enabled)// $GOOS// $GOARCH// ctxt.Compiler// linux (if GOOS = android)// solaris (if GOOS = illumos)// darwin (if GOOS = ios)// unix (if this is a Unix GOOS)// boringcrypto (if GOEXPERIMENT=boringcrypto is enabled)// tag (if tag is listed in ctxt.BuildTags, ctxt.ToolTags, or ctxt.ReleaseTags)//// It records all consulted tags in allTags.func ( *Context) ( string, map[string]bool) bool {if != nil { [] = true }// special tagsif .CgoEnabled && == "cgo" {returntrue }if == .GOOS || == .GOARCH || == .Compiler {returntrue }if .GOOS == "android" && == "linux" {returntrue }if .GOOS == "illumos" && == "solaris" {returntrue }if .GOOS == "ios" && == "darwin" {returntrue }if == "unix" && unixOS[.GOOS] {returntrue }if == "boringcrypto" { = "goexperiment.boringcrypto"// boringcrypto is an old name for goexperiment.boringcrypto }// other tagsfor , := range .BuildTags {if == {returntrue } }for , := range .ToolTags {if == {returntrue } }for , := range .ReleaseTags {if == {returntrue } }returnfalse}// goodOSArchFile returns false if the name contains a $GOOS or $GOARCH// suffix which does not match the current system.// The recognized name formats are://// name_$(GOOS).*// name_$(GOARCH).*// name_$(GOOS)_$(GOARCH).*// name_$(GOOS)_test.*// name_$(GOARCH)_test.*// name_$(GOOS)_$(GOARCH)_test.*//// Exceptions:// if GOOS=android, then files with GOOS=linux are also matched.// if GOOS=illumos, then files with GOOS=solaris are also matched.// if GOOS=ios, then files with GOOS=darwin are also matched.func ( *Context) ( string, map[string]bool) bool { , _, _ = strings.Cut(, ".")// Before Go 1.4, a file called "linux.go" would be equivalent to having a // build tag "linux" in that file. For Go 1.4 and beyond, we require this // auto-tagging to apply only to files with a non-empty prefix, so // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating // systems, such as android, to arrive without breaking existing code with // innocuous source code in "android.go". The easiest fix: cut everything // in the name before the initial _. := strings.Index(, "_")if < 0 {returntrue } = [:] // ignore everything before first _ := strings.Split(, "_")if := len(); > 0 && [-1] == "test" { = [:-1] } := len()if >= 2 && knownOS[[-2]] && knownArch[[-1]] {if != nil {// In case we short-circuit on l[n-1]. [[-2]] = true }return .matchTag([-1], ) && .matchTag([-2], ) }if >= 1 && (knownOS[[-1]] || knownArch[[-1]]) {return .matchTag([-1], ) }returntrue}// ToolDir is the directory containing build tools.varToolDir = getToolDir()// IsLocalImport reports whether the import path is// a local import path, like ".", "..", "./foo", or "../foo".func ( string) bool {return == "." || == ".." ||strings.HasPrefix(, "./") || strings.HasPrefix(, "../")}// ArchChar returns "?" and an error.// In earlier versions of Go, the returned string was used to derive// the compiler and linker tool names, the default object file suffix,// and the default linker output name. As of Go 1.5, those strings// no longer vary by architecture; they are compile, link, .o, and a.out, respectively.func ( string) (string, error) {return"?", errors.New("architecture letter no longer used")}
The pages are generated with Goldsv0.6.9-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds.