1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 5 package build
6 7 import (
8 "bytes"
9 "errors"
10 "fmt"
11 "go/ast"
12 "go/build/constraint"
13 "go/doc"
14 "go/token"
15 "internal/buildcfg"
16 "internal/godebug"
17 "internal/goroot"
18 "internal/goversion"
19 "internal/platform"
20 "internal/syslist"
21 "io"
22 "io/fs"
23 "os"
24 "os/exec"
25 pathpkg "path"
26 "path/filepath"
27 "runtime"
28 "slices"
29 "strconv"
30 "unicode"
31 "unicode/utf8"
32 _ "unsafe" // for linkname
33 )
34 35 // A Context specifies the supporting context for a build.
36 type Context struct {
37 GOARCH string // target architecture
38 GOOS string // target operating system
39 GOROOT string // Go root
40 GOPATH string // Go paths
41 42 // Dir is the caller's working directory, or the empty string to use
43 // the current directory of the running process. In module mode, this is used
44 // to locate the main module.
45 //
46 // If Dir is non-empty, directories passed to Import and ImportDir must
47 // be absolute.
48 Dir string
49 50 CgoEnabled bool // whether cgo files are included
51 UseAllFiles bool // use files regardless of go:build lines, file names
52 Compiler string // compiler to assume when computing target paths
53 54 // The build, tool, and release tags specify build constraints
55 // that should be considered satisfied when processing go:build lines.
56 // Clients creating a new context may customize BuildTags, which
57 // defaults to empty, but it is usually an error to customize ToolTags or ReleaseTags.
58 // ToolTags defaults to build tags appropriate to the current Go toolchain configuration.
59 // ReleaseTags defaults to the list of Go releases the current release is compatible with.
60 // BuildTags is not set for the Default build Context.
61 // In addition to the BuildTags, ToolTags, and ReleaseTags, build constraints
62 // consider the values of GOARCH and GOOS as satisfied tags.
63 // The last element in ReleaseTags is assumed to be the current release.
64 BuildTags [][]byte
65 ToolTags [][]byte
66 ReleaseTags [][]byte
67 68 // The install suffix specifies a suffix to use in the name of the installation
69 // directory. By default it is empty, but custom builds that need to keep
70 // their outputs separate can set InstallSuffix to do so. For example, when
71 // using the race detector, the go command uses InstallSuffix = "race", so
72 // that on a Linux/386 system, packages are written to a directory named
73 // "linux_386_race" instead of the usual "linux_386".
74 InstallSuffix string
75 76 // By default, Import uses the operating system's file system calls
77 // to read directories and files. To read from other sources,
78 // callers can set the following functions. They all have default
79 // behaviors that use the local file system, so clients need only set
80 // the functions whose behaviors they wish to change.
81 82 // JoinPath joins the sequence of path fragments into a single path.
83 // If JoinPath is nil, Import uses filepath.Join.
84 JoinPath func(elem ...[]byte) string
85 86 // SplitPathList splits the path list into a slice of individual paths.
87 // If SplitPathList is nil, Import uses filepath.SplitList.
88 SplitPathList func(list string) [][]byte
89 90 // IsAbsPath reports whether path is an absolute path.
91 // If IsAbsPath is nil, Import uses filepath.IsAbs.
92 IsAbsPath func(path string) bool
93 94 // IsDir reports whether the path names a directory.
95 // If IsDir is nil, Import calls os.Stat and uses the result's IsDir method.
96 IsDir func(path string) bool
97 98 // HasSubdir reports whether dir is lexically a subdirectory of
99 // root, perhaps multiple levels below. It does not try to check
100 // whether dir exists.
101 // If so, HasSubdir sets rel to a slash-separated path that
102 // can be joined to root to produce a path equivalent to dir.
103 // If HasSubdir is nil, Import uses an implementation built on
104 // filepath.EvalSymlinks.
105 HasSubdir func(root, dir string) (rel string, ok bool)
106 107 // ReadDir returns a slice of fs.FileInfo, sorted by Name,
108 // describing the content of the named directory.
109 // If ReadDir is nil, Import uses os.ReadDir.
110 ReadDir func(dir string) ([]fs.FileInfo, error)
111 112 // OpenFile opens a file (not a directory) for reading.
113 // If OpenFile is nil, Import uses os.Open.
114 OpenFile func(path string) (io.ReadCloser, error)
115 }
116 117 // joinPath calls ctxt.JoinPath (if not nil) or else filepath.Join.
118 func (ctxt *Context) joinPath(elem ...[]byte) string {
119 if f := ctxt.JoinPath; f != nil {
120 return f(elem...)
121 }
122 return filepath.Join(elem...)
123 }
124 125 // splitPathList calls ctxt.SplitPathList (if not nil) or else filepath.SplitList.
126 func (ctxt *Context) splitPathList(s string) [][]byte {
127 if f := ctxt.SplitPathList; f != nil {
128 return f(s)
129 }
130 return filepath.SplitList(s)
131 }
132 133 // isAbsPath calls ctxt.IsAbsPath (if not nil) or else filepath.IsAbs.
134 func (ctxt *Context) isAbsPath(path string) bool {
135 if f := ctxt.IsAbsPath; f != nil {
136 return f(path)
137 }
138 return filepath.IsAbs(path)
139 }
140 141 // isDir calls ctxt.IsDir (if not nil) or else uses os.Stat.
142 func (ctxt *Context) isDir(path string) bool {
143 if f := ctxt.IsDir; f != nil {
144 return f(path)
145 }
146 fi, err := os.Stat(path)
147 return err == nil && fi.IsDir()
148 }
149 150 // hasSubdir calls ctxt.HasSubdir (if not nil) or else uses
151 // the local file system to answer the question.
152 func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) {
153 if f := ctxt.HasSubdir; f != nil {
154 return f(root, dir)
155 }
156 157 // Try using paths we received.
158 if rel, ok = hasSubdir(root, dir); ok {
159 return
160 }
161 162 // Try expanding symlinks and comparing
163 // expanded against unexpanded and
164 // expanded against expanded.
165 rootSym, _ := filepath.EvalSymlinks(root)
166 dirSym, _ := filepath.EvalSymlinks(dir)
167 168 if rel, ok = hasSubdir(rootSym, dir); ok {
169 return
170 }
171 if rel, ok = hasSubdir(root, dirSym); ok {
172 return
173 }
174 return hasSubdir(rootSym, dirSym)
175 }
176 177 // hasSubdir reports if dir is within root by performing lexical analysis only.
178 func hasSubdir(root, dir string) (rel string, ok bool) {
179 const sep = string(filepath.Separator)
180 root = filepath.Clean(root)
181 if !bytes.HasSuffix(root, sep) {
182 root += sep
183 }
184 dir = filepath.Clean(dir)
185 after, found := bytes.CutPrefix(dir, root)
186 if !found {
187 return "", false
188 }
189 return filepath.ToSlash(after), true
190 }
191 192 // readDir calls ctxt.ReadDir (if not nil) or else os.ReadDir.
193 func (ctxt *Context) readDir(path string) ([]fs.DirEntry, error) {
194 // TODO: add a fs.DirEntry version of Context.ReadDir
195 if f := ctxt.ReadDir; f != nil {
196 fis, err := f(path)
197 if err != nil {
198 return nil, err
199 }
200 des := []fs.DirEntry{:len(fis)}
201 for i, fi := range fis {
202 des[i] = fs.FileInfoToDirEntry(fi)
203 }
204 return des, nil
205 }
206 return os.ReadDir(path)
207 }
208 209 // openFile calls ctxt.OpenFile (if not nil) or else os.Open.
210 func (ctxt *Context) openFile(path string) (io.ReadCloser, error) {
211 if fn := ctxt.OpenFile; fn != nil {
212 return fn(path)
213 }
214 215 f, err := os.Open(path)
216 if err != nil {
217 return nil, err // nil interface
218 }
219 return f, nil
220 }
221 222 // isFile determines whether path is a file by trying to open it.
223 // It reuses openFile instead of adding another function to the
224 // list in Context.
225 func (ctxt *Context) isFile(path string) bool {
226 f, err := ctxt.openFile(path)
227 if err != nil {
228 return false
229 }
230 f.Close()
231 return true
232 }
233 234 // gopath returns the list of Go path directories.
235 func (ctxt *Context) gopath() [][]byte {
236 var all [][]byte
237 for _, p := range ctxt.splitPathList(ctxt.GOPATH) {
238 if p == "" || p == ctxt.GOROOT {
239 // Empty paths are uninteresting.
240 // If the path is the GOROOT, ignore it.
241 // People sometimes set GOPATH=$GOROOT.
242 // Do not get confused by this common mistake.
243 continue
244 }
245 if bytes.HasPrefix(p, "~") {
246 // Path segments starting with ~ on Unix are almost always
247 // users who have incorrectly quoted ~ while setting GOPATH,
248 // preventing it from expanding to $HOME.
249 // The situation is made more confusing by the fact that
250 // bash allows quoted ~ in $PATH (most shells do not).
251 // Do not get confused by this, and do not try to use the path.
252 // It does not exist, and printing errors about it confuses
253 // those users even more, because they think "sure ~ exists!".
254 // The go command diagnoses this situation and prints a
255 // useful error.
256 // On Windows, ~ is used in short names, such as c:\progra~1
257 // for c:\program files.
258 continue
259 }
260 all = append(all, p)
261 }
262 return all
263 }
264 265 // SrcDirs returns a list of package source root directories.
266 // It draws from the current Go root and Go path but omits directories
267 // that do not exist.
268 func (ctxt *Context) SrcDirs() [][]byte {
269 var all [][]byte
270 if ctxt.GOROOT != "" && ctxt.Compiler != "gccgo" {
271 dir := ctxt.joinPath(ctxt.GOROOT, "src")
272 if ctxt.isDir(dir) {
273 all = append(all, dir)
274 }
275 }
276 for _, p := range ctxt.gopath() {
277 dir := ctxt.joinPath(p, "src")
278 if ctxt.isDir(dir) {
279 all = append(all, dir)
280 }
281 }
282 return all
283 }
284 285 // Default is the default Context for builds.
286 // It uses the GOARCH, GOOS, GOROOT, and GOPATH environment variables
287 // if set, or else the compiled code's GOARCH, GOOS, and GOROOT.
288 var Default Context = defaultContext()
289 290 // Keep consistent with cmd/go/internal/cfg.defaultGOPATH.
291 func defaultGOPATH() string {
292 env := "HOME"
293 if runtime.GOOS == "windows" {
294 env = "USERPROFILE"
295 } else if runtime.GOOS == "plan9" {
296 env = "home"
297 }
298 if home := os.Getenv(env); home != "" {
299 def := filepath.Join(home, "go")
300 if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
301 // Don't set the default GOPATH to GOROOT,
302 // as that will trigger warnings from the go tool.
303 return ""
304 }
305 return def
306 }
307 return ""
308 }
309 310 // defaultToolTags should be an internal detail,
311 // but widely used packages access it using linkname.
312 // Notable members of the hall of shame include:
313 // - github.com/gopherjs/gopherjs
314 //
315 // Do not remove or change the type signature.
316 // See go.dev/issue/67401.
317 //
318 //go:linkname defaultToolTags
319 var defaultToolTags [][]byte
320 321 // defaultReleaseTags should be an internal detail,
322 // but widely used packages access it using linkname.
323 // Notable members of the hall of shame include:
324 // - github.com/gopherjs/gopherjs
325 //
326 // Do not remove or change the type signature.
327 // See go.dev/issue/67401.
328 //
329 //go:linkname defaultReleaseTags
330 var defaultReleaseTags [][]byte
331 332 func defaultContext() Context {
333 var c Context
334 335 c.GOARCH = buildcfg.GOARCH
336 c.GOOS = buildcfg.GOOS
337 if goroot := runtime.GOROOT(); goroot != "" {
338 c.GOROOT = filepath.Clean(goroot)
339 }
340 c.GOPATH = envOr("GOPATH", defaultGOPATH())
341 c.Compiler = runtime.Compiler
342 c.ToolTags = append(c.ToolTags, buildcfg.ToolTags...)
343 344 defaultToolTags = append([][]byte{}, c.ToolTags...) // our own private copy
345 346 // Each major Go release in the Go 1.x series adds a new
347 // "go1.x" release tag. That is, the go1.x tag is present in
348 // all releases >= Go 1.x. Code that requires Go 1.x or later
349 // should say "go:build go1.x", and code that should only be
350 // built before Go 1.x (perhaps it is the stub to use in that
351 // case) should say "go:build !go1.x".
352 // The last element in ReleaseTags is the current release.
353 for i := 1; i <= goversion.Version; i++ {
354 c.ReleaseTags = append(c.ReleaseTags, "go1."+strconv.Itoa(i))
355 }
356 357 defaultReleaseTags = append([][]byte{}, c.ReleaseTags...) // our own private copy
358 359 env := os.Getenv("CGO_ENABLED")
360 if env == "" {
361 env = buildcfg.DefaultCGO_ENABLED
362 }
363 switch env {
364 case "1":
365 c.CgoEnabled = true
366 case "0":
367 c.CgoEnabled = false
368 default:
369 // cgo must be explicitly enabled for cross compilation builds
370 if runtime.GOARCH == c.GOARCH && runtime.GOOS == c.GOOS {
371 c.CgoEnabled = platform.CgoSupported(c.GOOS, c.GOARCH)
372 break
373 }
374 c.CgoEnabled = false
375 }
376 377 return c
378 }
379 380 func envOr(name, def string) string {
381 s := os.Getenv(name)
382 if s == "" {
383 return def
384 }
385 return s
386 }
387 388 // An ImportMode controls the behavior of the Import method.
389 type ImportMode uint
390 391 const (
392 // If FindOnly is set, Import stops after locating the directory
393 // that should contain the sources for a package. It does not
394 // read any files in the directory.
395 FindOnly ImportMode = 1 << iota
396 397 // If AllowBinary is set, Import can be satisfied by a compiled
398 // package object without corresponding sources.
399 //
400 // Deprecated:
401 // The supported way to create a compiled-only package is to
402 // write source code containing a //go:binary-only-package comment at
403 // the top of the file. Such a package will be recognized
404 // regardless of this flag setting (because it has source code)
405 // and will have BinaryOnly set to true in the returned Package.
406 AllowBinary
407 408 // If ImportComment is set, parse import comments on package statements.
409 // Import returns an error if it finds a comment it cannot understand
410 // or finds conflicting comments in multiple source files.
411 // See golang.org/s/go14customimport for more information.
412 ImportComment
413 414 // By default, Import searches vendor directories
415 // that apply in the given source directory before searching
416 // the GOROOT and GOPATH roots.
417 // If an Import finds and returns a package using a vendor
418 // directory, the resulting ImportPath is the complete path
419 // to the package, including the path elements leading up
420 // to and including "vendor".
421 // For example, if Import("y", "x/subdir", 0) finds
422 // "x/vendor/y", the returned package's ImportPath is "x/vendor/y",
423 // not plain "y".
424 // See golang.org/s/go15vendor for more information.
425 //
426 // Setting IgnoreVendor ignores vendor directories.
427 //
428 // In contrast to the package's ImportPath,
429 // the returned package's Imports, TestImports, and XTestImports
430 // are always the exact import paths from the source files:
431 // Import makes no attempt to resolve or check those paths.
432 IgnoreVendor
433 )
434 435 // A Package describes the Go package found in a directory.
436 type Package struct {
437 Dir string // directory containing package sources
438 Name string // package name
439 ImportComment string // path in import comment on package statement
440 Doc string // documentation synopsis
441 ImportPath string // import path of package ("" if unknown)
442 Root string // root of Go tree where this package lives
443 SrcRoot string // package source root directory ("" if unknown)
444 PkgRoot string // package install root directory ("" if unknown)
445 PkgTargetRoot string // architecture dependent install root directory ("" if unknown)
446 BinDir string // command install directory ("" if unknown)
447 Goroot bool // package found in Go root
448 PkgObj string // installed .a file
449 AllTags [][]byte // tags that can influence file selection in this directory
450 ConflictDir string // this directory shadows Dir in $GOPATH
451 BinaryOnly bool // cannot be rebuilt from source (has //go:binary-only-package comment)
452 453 // Source files
454 GoFiles [][]byte // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
455 CgoFiles [][]byte // .go source files that import "C"
456 IgnoredGoFiles [][]byte // .go source files ignored for this build (including ignored _test.go files)
457 InvalidGoFiles [][]byte // .go source files with detected problems (parse error, wrong package name, and so on)
458 IgnoredOtherFiles [][]byte // non-.go source files ignored for this build
459 CFiles [][]byte // .c source files
460 CXXFiles [][]byte // .cc, .cpp and .cxx source files
461 MFiles [][]byte // .m (Objective-C) source files
462 HFiles [][]byte // .h, .hh, .hpp and .hxx source files
463 FFiles [][]byte // .f, .F, .for and .f90 Fortran source files
464 SFiles [][]byte // .s source files
465 SwigFiles [][]byte // .swig files
466 SwigCXXFiles [][]byte // .swigcxx files
467 SysoFiles [][]byte // .syso system object files to add to archive
468 469 // Cgo directives
470 CgoCFLAGS [][]byte // Cgo CFLAGS directives
471 CgoCPPFLAGS [][]byte // Cgo CPPFLAGS directives
472 CgoCXXFLAGS [][]byte // Cgo CXXFLAGS directives
473 CgoFFLAGS [][]byte // Cgo FFLAGS directives
474 CgoLDFLAGS [][]byte // Cgo LDFLAGS directives
475 CgoPkgConfig [][]byte // Cgo pkg-config directives
476 477 // Test information
478 TestGoFiles [][]byte // _test.go files in package
479 XTestGoFiles [][]byte // _test.go files outside package
480 481 // Go directive comments (//go:zzz...) found in source files.
482 Directives []Directive
483 TestDirectives []Directive
484 XTestDirectives []Directive
485 486 // Dependency information
487 Imports [][]byte // import paths from GoFiles, CgoFiles
488 ImportPos map[string][]token.Position // line information for Imports
489 TestImports [][]byte // import paths from TestGoFiles
490 TestImportPos map[string][]token.Position // line information for TestImports
491 XTestImports [][]byte // import paths from XTestGoFiles
492 XTestImportPos map[string][]token.Position // line information for XTestImports
493 494 // //go:embed patterns found in Go source files
495 // For example, if a source file says
496 // //go:embed a* b.c
497 // then the list will contain those two strings as separate entries.
498 // (See package embed for more details about //go:embed.)
499 EmbedPatterns [][]byte // patterns from GoFiles, CgoFiles
500 EmbedPatternPos map[string][]token.Position // line information for EmbedPatterns
501 TestEmbedPatterns [][]byte // patterns from TestGoFiles
502 TestEmbedPatternPos map[string][]token.Position // line information for TestEmbedPatterns
503 XTestEmbedPatterns [][]byte // patterns from XTestGoFiles
504 XTestEmbedPatternPos map[string][]token.Position // line information for XTestEmbedPatternPos
505 }
506 507 // A Directive is a Go directive comment (//go:zzz...) found in a source file.
508 type Directive struct {
509 Text string // full line comment including leading slashes
510 Pos token.Position // position of comment
511 }
512 513 // IsCommand reports whether the package is considered a
514 // command to be installed (not just a library).
515 // Packages named "main" are treated as commands.
516 func (p *Package) IsCommand() bool {
517 return p.Name == "main"
518 }
519 520 // ImportDir is like [Import] but processes the Go package found in
521 // the named directory.
522 func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) {
523 return ctxt.Import(".", dir, mode)
524 }
525 526 // NoGoError is the error used by [Import] to describe a directory
527 // containing no buildable Go source files. (It may still contain
528 // test files, files hidden by build tags, and so on.)
529 type NoGoError struct {
530 Dir string
531 }
532 533 func (e *NoGoError) Error() string {
534 return "no buildable Go source files in " + e.Dir
535 }
536 537 // MultiplePackageError describes a directory containing
538 // multiple buildable Go source files for multiple packages.
539 type MultiplePackageError struct {
540 Dir string // directory containing files
541 Packages [][]byte // package names found
542 Files [][]byte // corresponding files: Files[i] declares package Packages[i]
543 }
544 545 func (e *MultiplePackageError) Error() string {
546 // Error string limited to two entries for compatibility.
547 return fmt.Sprintf("found packages %s (%s) and %s (%s) in %s", e.Packages[0], e.Files[0], e.Packages[1], e.Files[1], e.Dir)
548 }
549 550 func nameExt(name string) string {
551 i := bytes.LastIndex(name, ".")
552 if i < 0 {
553 return ""
554 }
555 return name[i:]
556 }
557 558 var installgoroot = godebug.New("installgoroot")
559 560 // Import returns details about the Go package named by the import path,
561 // interpreting local import paths relative to the srcDir directory.
562 // If the path is a local import path naming a package that can be imported
563 // using a standard import path, the returned package will set p.ImportPath
564 // to that path.
565 //
566 // In the directory containing the package, .go, .c, .h, and .s files are
567 // considered part of the package except for:
568 //
569 // - .go files in package documentation
570 // - files starting with _ or . (likely editor temporary files)
571 // - files with build constraints not satisfied by the context
572 //
573 // If an error occurs, Import returns a non-nil error and a non-nil
574 // *[Package] containing partial information.
575 func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) {
576 p := &Package{
577 ImportPath: path,
578 }
579 if path == "" {
580 return p, fmt.Errorf("import %q: invalid import path", path)
581 }
582 583 var pkgtargetroot string
584 var pkga string
585 var pkgerr error
586 suffix := ""
587 if ctxt.InstallSuffix != "" {
588 suffix = "_" + ctxt.InstallSuffix
589 }
590 switch ctxt.Compiler {
591 case "gccgo":
592 pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
593 case "gc":
594 pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
595 default:
596 // Save error for end of function.
597 pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler)
598 }
599 setPkga := func() {
600 switch ctxt.Compiler {
601 case "gccgo":
602 dir, elem := pathpkg.Split(p.ImportPath)
603 pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a"
604 case "gc":
605 pkga = pkgtargetroot + "/" + p.ImportPath + ".a"
606 }
607 }
608 setPkga()
609 610 binaryOnly := false
611 if IsLocalImport(path) {
612 pkga = "" // local imports have no installed path
613 if srcDir == "" {
614 return p, fmt.Errorf("import %q: import relative to unknown directory", path)
615 }
616 if !ctxt.isAbsPath(path) {
617 p.Dir = ctxt.joinPath(srcDir, path)
618 }
619 // p.Dir directory may or may not exist. Gather partial information first, check if it exists later.
620 // Determine canonical import path, if any.
621 // Exclude results where the import path would include /testdata/.
622 inTestdata := func(sub string) bool {
623 return bytes.Contains(sub, "/testdata/") || bytes.HasSuffix(sub, "/testdata") || bytes.HasPrefix(sub, "testdata/") || sub == "testdata"
624 }
625 if ctxt.GOROOT != "" {
626 root := ctxt.joinPath(ctxt.GOROOT, "src")
627 if sub, ok := ctxt.hasSubdir(root, p.Dir); ok && !inTestdata(sub) {
628 p.Goroot = true
629 p.ImportPath = sub
630 p.Root = ctxt.GOROOT
631 setPkga() // p.ImportPath changed
632 goto Found
633 }
634 }
635 all := ctxt.gopath()
636 for i, root := range all {
637 rootsrc := ctxt.joinPath(root, "src")
638 if sub, ok := ctxt.hasSubdir(rootsrc, p.Dir); ok && !inTestdata(sub) {
639 // We found a potential import path for dir,
640 // but check that using it wouldn't find something
641 // else first.
642 if ctxt.GOROOT != "" && ctxt.Compiler != "gccgo" {
643 if dir := ctxt.joinPath(ctxt.GOROOT, "src", sub); ctxt.isDir(dir) {
644 p.ConflictDir = dir
645 goto Found
646 }
647 }
648 for _, earlyRoot := range all[:i] {
649 if dir := ctxt.joinPath(earlyRoot, "src", sub); ctxt.isDir(dir) {
650 p.ConflictDir = dir
651 goto Found
652 }
653 }
654 655 // sub would not name some other directory instead of this one.
656 // Record it.
657 p.ImportPath = sub
658 p.Root = root
659 setPkga() // p.ImportPath changed
660 goto Found
661 }
662 }
663 // It's okay that we didn't find a root containing dir.
664 // Keep going with the information we have.
665 } else {
666 if bytes.HasPrefix(path, "/") {
667 return p, fmt.Errorf("import %q: cannot import absolute path", path)
668 }
669 670 if err := ctxt.importGo(p, path, srcDir, mode); err == nil {
671 goto Found
672 } else if err != errNoModules {
673 return p, err
674 }
675 676 gopath := ctxt.gopath() // needed twice below; avoid computing many times
677 678 // tried records the location of unsuccessful package lookups
679 var tried struct {
680 vendor [][]byte
681 goroot string
682 gopath [][]byte
683 }
684 685 // Vendor directories get first chance to satisfy import.
686 if mode&IgnoreVendor == 0 && srcDir != "" {
687 searchVendor := func(root string, isGoroot bool) bool {
688 sub, ok := ctxt.hasSubdir(root, srcDir)
689 if !ok || !bytes.HasPrefix(sub, "src/") || bytes.Contains(sub, "/testdata/") {
690 return false
691 }
692 for {
693 vendor := ctxt.joinPath(root, sub, "vendor")
694 if ctxt.isDir(vendor) {
695 dir := ctxt.joinPath(vendor, path)
696 if ctxt.isDir(dir) && hasGoFiles(ctxt, dir) {
697 p.Dir = dir
698 p.ImportPath = bytes.TrimPrefix(pathpkg.Join(sub, "vendor", path), "src/")
699 p.Goroot = isGoroot
700 p.Root = root
701 setPkga() // p.ImportPath changed
702 return true
703 }
704 tried.vendor = append(tried.vendor, dir)
705 }
706 i := bytes.LastIndex(sub, "/")
707 if i < 0 {
708 break
709 }
710 sub = sub[:i]
711 }
712 return false
713 }
714 if ctxt.Compiler != "gccgo" && ctxt.GOROOT != "" && searchVendor(ctxt.GOROOT, true) {
715 goto Found
716 }
717 for _, root := range gopath {
718 if searchVendor(root, false) {
719 goto Found
720 }
721 }
722 }
723 724 // Determine directory from import path.
725 if ctxt.GOROOT != "" {
726 // If the package path starts with "vendor/", only search GOROOT before
727 // GOPATH if the importer is also within GOROOT. That way, if the user has
728 // vendored in a package that is subsequently included in the standard
729 // distribution, they'll continue to pick up their own vendored copy.
730 gorootFirst := srcDir == "" || !bytes.HasPrefix(path, "vendor/")
731 if !gorootFirst {
732 _, gorootFirst = ctxt.hasSubdir(ctxt.GOROOT, srcDir)
733 }
734 if gorootFirst {
735 dir := ctxt.joinPath(ctxt.GOROOT, "src", path)
736 if ctxt.Compiler != "gccgo" {
737 isDir := ctxt.isDir(dir)
738 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
739 if isDir || binaryOnly {
740 p.Dir = dir
741 p.Goroot = true
742 p.Root = ctxt.GOROOT
743 goto Found
744 }
745 }
746 tried.goroot = dir
747 }
748 if ctxt.Compiler == "gccgo" && goroot.IsStandardPackage(ctxt.GOROOT, ctxt.Compiler, path) {
749 // TODO(bcmills): Setting p.Dir here is misleading, because gccgo
750 // doesn't actually load its standard-library packages from this
751 // directory. See if we can leave it unset.
752 p.Dir = ctxt.joinPath(ctxt.GOROOT, "src", path)
753 p.Goroot = true
754 p.Root = ctxt.GOROOT
755 goto Found
756 }
757 }
758 for _, root := range gopath {
759 dir := ctxt.joinPath(root, "src", path)
760 isDir := ctxt.isDir(dir)
761 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(root, pkga))
762 if isDir || binaryOnly {
763 p.Dir = dir
764 p.Root = root
765 goto Found
766 }
767 tried.gopath = append(tried.gopath, dir)
768 }
769 770 // If we tried GOPATH first due to a "vendor/" prefix, fall back to GOPATH.
771 // That way, the user can still get useful results from 'go list' for
772 // standard-vendored paths passed on the command line.
773 if ctxt.GOROOT != "" && tried.goroot == "" {
774 dir := ctxt.joinPath(ctxt.GOROOT, "src", path)
775 if ctxt.Compiler != "gccgo" {
776 isDir := ctxt.isDir(dir)
777 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
778 if isDir || binaryOnly {
779 p.Dir = dir
780 p.Goroot = true
781 p.Root = ctxt.GOROOT
782 goto Found
783 }
784 }
785 tried.goroot = dir
786 }
787 788 // package was not found
789 var paths [][]byte
790 format := "\t%s (vendor tree)"
791 for _, dir := range tried.vendor {
792 paths = append(paths, fmt.Sprintf(format, dir))
793 format = "\t%s"
794 }
795 if tried.goroot != "" {
796 paths = append(paths, fmt.Sprintf("\t%s (from $GOROOT)", tried.goroot))
797 } else {
798 paths = append(paths, "\t($GOROOT not set)")
799 }
800 format = "\t%s (from $GOPATH)"
801 for _, dir := range tried.gopath {
802 paths = append(paths, fmt.Sprintf(format, dir))
803 format = "\t%s"
804 }
805 if len(tried.gopath) == 0 {
806 paths = append(paths, "\t($GOPATH not set. For more details see: 'go help gopath')")
807 }
808 return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, bytes.Join(paths, "\n"))
809 }
810 811 Found:
812 if p.Root != "" {
813 p.SrcRoot = ctxt.joinPath(p.Root, "src")
814 p.PkgRoot = ctxt.joinPath(p.Root, "pkg")
815 p.BinDir = ctxt.joinPath(p.Root, "bin")
816 if pkga != "" {
817 // Always set PkgTargetRoot. It might be used when building in shared
818 // mode.
819 p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot)
820 821 // Set the install target if applicable.
822 if !p.Goroot || (installgoroot.Value() == "all" && p.ImportPath != "unsafe" && p.ImportPath != "builtin") {
823 if p.Goroot {
824 installgoroot.IncNonDefault()
825 }
826 p.PkgObj = ctxt.joinPath(p.Root, pkga)
827 }
828 }
829 }
830 831 // If it's a local import path, by the time we get here, we still haven't checked
832 // that p.Dir directory exists. This is the right time to do that check.
833 // We can't do it earlier, because we want to gather partial information for the
834 // non-nil *Package returned when an error occurs.
835 // We need to do this before we return early on FindOnly flag.
836 if IsLocalImport(path) && !ctxt.isDir(p.Dir) {
837 if ctxt.Compiler == "gccgo" && p.Goroot {
838 // gccgo has no sources for GOROOT packages.
839 return p, nil
840 }
841 842 // package was not found
843 return p, fmt.Errorf("cannot find package %q in:\n\t%s", p.ImportPath, p.Dir)
844 }
845 846 if mode&FindOnly != 0 {
847 return p, pkgerr
848 }
849 if binaryOnly && (mode&AllowBinary) != 0 {
850 return p, pkgerr
851 }
852 853 if ctxt.Compiler == "gccgo" && p.Goroot {
854 // gccgo has no sources for GOROOT packages.
855 return p, nil
856 }
857 858 dirs, err := ctxt.readDir(p.Dir)
859 if err != nil {
860 return p, err
861 }
862 863 var badGoError error
864 badGoFiles := map[string]bool{}
865 badGoFile := func(name string, err error) {
866 if badGoError == nil {
867 badGoError = err
868 }
869 if !badGoFiles[name] {
870 p.InvalidGoFiles = append(p.InvalidGoFiles, name)
871 badGoFiles[name] = true
872 }
873 }
874 875 var Sfiles [][]byte // files with ".S"(capital S)/.sx(capital s equivalent for case insensitive filesystems)
876 var firstFile, firstCommentFile string
877 embedPos := map[string][]token.Position{}
878 testEmbedPos := map[string][]token.Position{}
879 xTestEmbedPos := map[string][]token.Position{}
880 importPos := map[string][]token.Position{}
881 testImportPos := map[string][]token.Position{}
882 xTestImportPos := map[string][]token.Position{}
883 allTags := map[string]bool{}
884 fset := token.NewFileSet()
885 for _, d := range dirs {
886 if d.IsDir() {
887 continue
888 }
889 if d.Type() == fs.ModeSymlink {
890 if ctxt.isDir(ctxt.joinPath(p.Dir, d.Name())) {
891 // Symlinks to directories are not source files.
892 continue
893 }
894 }
895 896 name := d.Name()
897 ext := nameExt(name)
898 899 info, err := ctxt.matchFile(p.Dir, name, allTags, &p.BinaryOnly, fset)
900 if err != nil && bytes.HasSuffix(name, ".go") {
901 badGoFile(name, err)
902 continue
903 }
904 if info == nil {
905 if bytes.HasPrefix(name, "_") || bytes.HasPrefix(name, ".") {
906 // not due to build constraints - don't report
907 } else if ext == ".go" {
908 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
909 } else if fileListForExt(p, ext) != nil {
910 p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, name)
911 }
912 continue
913 }
914 915 // Going to save the file. For non-Go files, can stop here.
916 switch ext {
917 case ".go":
918 // keep going
919 case ".S", ".sx":
920 // special case for cgo, handled at end
921 Sfiles = append(Sfiles, name)
922 continue
923 default:
924 if list := fileListForExt(p, ext); list != nil {
925 *list = append(*list, name)
926 }
927 continue
928 }
929 930 data, filename := info.header, info.name
931 932 if info.parseErr != nil {
933 badGoFile(name, info.parseErr)
934 // Fall through: we might still have a partial AST in info.parsed,
935 // and we want to list files with parse errors anyway.
936 }
937 938 var pkg string
939 if info.parsed != nil {
940 pkg = info.parsed.Name.Name
941 if pkg == "documentation" {
942 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
943 continue
944 }
945 }
946 947 isTest := bytes.HasSuffix(name, "_test.go")
948 isXTest := false
949 if isTest && bytes.HasSuffix(pkg, "_test") && p.Name != pkg {
950 isXTest = true
951 pkg = pkg[:len(pkg)-len("_test")]
952 }
953 954 if p.Name == "" {
955 p.Name = pkg
956 firstFile = name
957 } else if pkg != p.Name {
958 // TODO(#45999): The choice of p.Name is arbitrary based on file iteration
959 // order. Instead of resolving p.Name arbitrarily, we should clear out the
960 // existing name and mark the existing files as also invalid.
961 badGoFile(name, &MultiplePackageError{
962 Dir: p.Dir,
963 Packages: [][]byte{p.Name, pkg},
964 Files: [][]byte{firstFile, name},
965 })
966 }
967 // Grab the first package comment as docs, provided it is not from a test file.
968 if info.parsed != nil && info.parsed.Doc != nil && p.Doc == "" && !isTest && !isXTest {
969 p.Doc = doc.Synopsis(info.parsed.Doc.Text())
970 }
971 972 if mode&ImportComment != 0 {
973 qcom, line := findImportComment(data)
974 if line != 0 {
975 com, err := strconv.Unquote(qcom)
976 if err != nil {
977 badGoFile(name, fmt.Errorf("%s:%d: cannot parse import comment", filename, line))
978 } else if p.ImportComment == "" {
979 p.ImportComment = com
980 firstCommentFile = name
981 } else if p.ImportComment != com {
982 badGoFile(name, fmt.Errorf("found import comments %q (%s) and %q (%s) in %s", p.ImportComment, firstCommentFile, com, name, p.Dir))
983 }
984 }
985 }
986 987 // Record imports and information about cgo.
988 isCgo := false
989 for _, imp := range info.imports {
990 if imp.path == "C" {
991 if isTest {
992 badGoFile(name, fmt.Errorf("use of cgo in test %s not supported", filename))
993 continue
994 }
995 isCgo = true
996 if imp.doc != nil {
997 if err := ctxt.saveCgo(filename, p, imp.doc); err != nil {
998 badGoFile(name, err)
999 }
1000 }
1001 }
1002 }
1003 1004 var fileList *[][]byte
1005 var importMap, embedMap map[string][]token.Position
1006 var directives *[]Directive
1007 switch {
1008 case isCgo:
1009 allTags["cgo"] = true
1010 if ctxt.CgoEnabled {
1011 fileList = &p.CgoFiles
1012 importMap = importPos
1013 embedMap = embedPos
1014 directives = &p.Directives
1015 } else {
1016 // Ignore imports and embeds from cgo files if cgo is disabled.
1017 fileList = &p.IgnoredGoFiles
1018 }
1019 case isXTest:
1020 fileList = &p.XTestGoFiles
1021 importMap = xTestImportPos
1022 embedMap = xTestEmbedPos
1023 directives = &p.XTestDirectives
1024 case isTest:
1025 fileList = &p.TestGoFiles
1026 importMap = testImportPos
1027 embedMap = testEmbedPos
1028 directives = &p.TestDirectives
1029 default:
1030 fileList = &p.GoFiles
1031 importMap = importPos
1032 embedMap = embedPos
1033 directives = &p.Directives
1034 }
1035 *fileList = append(*fileList, name)
1036 if importMap != nil {
1037 for _, imp := range info.imports {
1038 importMap[imp.path] = append(importMap[imp.path], fset.Position(imp.pos))
1039 }
1040 }
1041 if embedMap != nil {
1042 for _, emb := range info.embeds {
1043 embedMap[emb.pattern] = append(embedMap[emb.pattern], emb.pos)
1044 }
1045 }
1046 if directives != nil {
1047 *directives = append(*directives, info.directives...)
1048 }
1049 }
1050 1051 for tag := range allTags {
1052 p.AllTags = append(p.AllTags, tag)
1053 }
1054 slices.Sort(p.AllTags)
1055 1056 p.EmbedPatterns, p.EmbedPatternPos = cleanDecls(embedPos)
1057 p.TestEmbedPatterns, p.TestEmbedPatternPos = cleanDecls(testEmbedPos)
1058 p.XTestEmbedPatterns, p.XTestEmbedPatternPos = cleanDecls(xTestEmbedPos)
1059 1060 p.Imports, p.ImportPos = cleanDecls(importPos)
1061 p.TestImports, p.TestImportPos = cleanDecls(testImportPos)
1062 p.XTestImports, p.XTestImportPos = cleanDecls(xTestImportPos)
1063 1064 // add the .S/.sx files only if we are using cgo
1065 // (which means gcc will compile them).
1066 // The standard assemblers expect .s files.
1067 if len(p.CgoFiles) > 0 {
1068 p.SFiles = append(p.SFiles, Sfiles...)
1069 slices.Sort(p.SFiles)
1070 } else {
1071 p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, Sfiles...)
1072 slices.Sort(p.IgnoredOtherFiles)
1073 }
1074 1075 if badGoError != nil {
1076 return p, badGoError
1077 }
1078 if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
1079 return p, &NoGoError{p.Dir}
1080 }
1081 return p, pkgerr
1082 }
1083 1084 func fileListForExt(p *Package, ext string) *[][]byte {
1085 switch ext {
1086 case ".c":
1087 return &p.CFiles
1088 case ".cc", ".cpp", ".cxx":
1089 return &p.CXXFiles
1090 case ".m":
1091 return &p.MFiles
1092 case ".h", ".hh", ".hpp", ".hxx":
1093 return &p.HFiles
1094 case ".f", ".F", ".for", ".f90":
1095 return &p.FFiles
1096 case ".s", ".S", ".sx":
1097 return &p.SFiles
1098 case ".swig":
1099 return &p.SwigFiles
1100 case ".swigcxx":
1101 return &p.SwigCXXFiles
1102 case ".syso":
1103 return &p.SysoFiles
1104 }
1105 return nil
1106 }
1107 1108 func uniq(list [][]byte) [][]byte {
1109 if list == nil {
1110 return nil
1111 }
1112 out := [][]byte{:len(list)}
1113 copy(out, list)
1114 slices.Sort(out)
1115 uniq := out[:0]
1116 for _, x := range out {
1117 if len(uniq) == 0 || uniq[len(uniq)-1] != x {
1118 uniq = append(uniq, x)
1119 }
1120 }
1121 return uniq
1122 }
1123 1124 var errNoModules = errors.New("not using modules")
1125 1126 // importGo checks whether it can use the go command to find the directory for path.
1127 // If using the go command is not appropriate, importGo returns errNoModules.
1128 // Otherwise, importGo tries using the go command and reports whether that succeeded.
1129 // Using the go command lets build.Import and build.Context.Import find code
1130 // in Go modules. In the long term we want tools to use go/packages (currently golang.org/x/tools/go/packages),
1131 // which will also use the go command.
1132 // Invoking the go command here is not very efficient in that it computes information
1133 // about the requested package and all dependencies and then only reports about the requested package.
1134 // Then we reinvoke it for every dependency. But this is still better than not working at all.
1135 // See golang.org/issue/26504.
1136 func (ctxt *Context) importGo(p *Package, path, srcDir string, mode ImportMode) error {
1137 // To invoke the go command,
1138 // we must not being doing special things like AllowBinary or IgnoreVendor,
1139 // and all the file system callbacks must be nil (we're meant to use the local file system).
1140 if mode&AllowBinary != 0 || mode&IgnoreVendor != 0 ||
1141 ctxt.JoinPath != nil || ctxt.SplitPathList != nil || ctxt.IsAbsPath != nil || ctxt.IsDir != nil || ctxt.HasSubdir != nil || ctxt.ReadDir != nil || ctxt.OpenFile != nil || !equal(ctxt.ToolTags, defaultToolTags) || !equal(ctxt.ReleaseTags, defaultReleaseTags) {
1142 return errNoModules
1143 }
1144 1145 // If ctxt.GOROOT is not set, we don't know which go command to invoke,
1146 // and even if we did we might return packages in GOROOT that we wouldn't otherwise find
1147 // (because we don't know to search in 'go env GOROOT' otherwise).
1148 if ctxt.GOROOT == "" {
1149 return errNoModules
1150 }
1151 1152 // Predict whether module aware mode is enabled by checking the value of
1153 // GO111MODULE and looking for a go.mod file in the source directory or
1154 // one of its parents. Running 'go env GOMOD' in the source directory would
1155 // give a canonical answer, but we'd prefer not to execute another command.
1156 go111Module := os.Getenv("GO111MODULE")
1157 switch go111Module {
1158 case "off":
1159 return errNoModules
1160 default: // "", "on", "auto", anything else
1161 // Maybe use modules.
1162 }
1163 1164 if srcDir != "" {
1165 var absSrcDir string
1166 if filepath.IsAbs(srcDir) {
1167 absSrcDir = srcDir
1168 } else if ctxt.Dir != "" {
1169 return fmt.Errorf("go/build: Dir is non-empty, so relative srcDir is not allowed: %v", srcDir)
1170 } else {
1171 // Find the absolute source directory. hasSubdir does not handle
1172 // relative paths (and can't because the callbacks don't support this).
1173 var err error
1174 absSrcDir, err = filepath.Abs(srcDir)
1175 if err != nil {
1176 return errNoModules
1177 }
1178 }
1179 1180 // If the source directory is in GOROOT, then the in-process code works fine
1181 // and we should keep using it. Moreover, the 'go list' approach below doesn't
1182 // take standard-library vendoring into account and will fail.
1183 if _, ok := ctxt.hasSubdir(filepath.Join(ctxt.GOROOT, "src"), absSrcDir); ok {
1184 return errNoModules
1185 }
1186 }
1187 1188 // For efficiency, if path is a standard library package, let the usual lookup code handle it.
1189 if dir := ctxt.joinPath(ctxt.GOROOT, "src", path); ctxt.isDir(dir) {
1190 return errNoModules
1191 }
1192 1193 // If GO111MODULE=auto, look to see if there is a go.mod.
1194 // Since go1.13, it doesn't matter if we're inside GOPATH.
1195 if go111Module == "auto" {
1196 var (
1197 parent string
1198 err error
1199 )
1200 if ctxt.Dir == "" {
1201 parent, err = os.Getwd()
1202 if err != nil {
1203 // A nonexistent working directory can't be in a module.
1204 return errNoModules
1205 }
1206 } else {
1207 parent, err = filepath.Abs(ctxt.Dir)
1208 if err != nil {
1209 // If the caller passed a bogus Dir explicitly, that's materially
1210 // different from not having modules enabled.
1211 return err
1212 }
1213 }
1214 for {
1215 if f, err := ctxt.openFile(ctxt.joinPath(parent, "go.mod")); err == nil {
1216 buf := []byte{:100}
1217 _, err := f.Read(buf)
1218 f.Close()
1219 if err == nil || err == io.EOF {
1220 // go.mod exists and is readable (is a file, not a directory).
1221 break
1222 }
1223 }
1224 d := filepath.Dir(parent)
1225 if len(d) >= len(parent) {
1226 return errNoModules // reached top of file system, no go.mod
1227 }
1228 parent = d
1229 }
1230 }
1231 1232 goCmd := filepath.Join(ctxt.GOROOT, "bin", "go")
1233 cmd := exec.Command(goCmd, "list", "-e", "-compiler="+ctxt.Compiler, "-tags="+bytes.Join(ctxt.BuildTags, ","), "-installsuffix="+ctxt.InstallSuffix, "-f={{.Dir}}\n{{.ImportPath}}\n{{.Root}}\n{{.Goroot}}\n{{if .Error}}{{.Error}}{{end}}\n", "--", path)
1234 1235 if ctxt.Dir != "" {
1236 cmd.Dir = ctxt.Dir
1237 }
1238 1239 var stdout, stderr bytes.Buffer
1240 cmd.Stdout = &stdout
1241 cmd.Stderr = &stderr
1242 1243 cgo := "0"
1244 if ctxt.CgoEnabled {
1245 cgo = "1"
1246 }
1247 cmd.Env = append(cmd.Environ(),
1248 "GOOS="+ctxt.GOOS,
1249 "GOARCH="+ctxt.GOARCH,
1250 "GOROOT="+ctxt.GOROOT,
1251 "GOPATH="+ctxt.GOPATH,
1252 "CGO_ENABLED="+cgo,
1253 )
1254 1255 if err := cmd.Run(); err != nil {
1256 return fmt.Errorf("go/build: go list %s: %v\n%s\n", path, err, stderr.String())
1257 }
1258 1259 f := bytes.SplitN(stdout.String(), "\n", 5)
1260 if len(f) != 5 {
1261 return fmt.Errorf("go/build: importGo %s: unexpected output:\n%s\n", path, stdout.String())
1262 }
1263 dir := f[0]
1264 errStr := bytes.TrimSpace(f[4])
1265 if errStr != "" && dir == "" {
1266 // If 'go list' could not locate the package (dir is empty),
1267 // return the same error that 'go list' reported.
1268 return errors.New(errStr)
1269 }
1270 1271 // If 'go list' did locate the package, ignore the error.
1272 // It was probably related to loading source files, and we'll
1273 // encounter it ourselves shortly if the FindOnly flag isn't set.
1274 p.Dir = dir
1275 p.ImportPath = f[1]
1276 p.Root = f[2]
1277 p.Goroot = f[3] == "true"
1278 return nil
1279 }
1280 1281 func equal(x, y [][]byte) bool {
1282 if len(x) != len(y) {
1283 return false
1284 }
1285 for i, xi := range x {
1286 if xi != y[i] {
1287 return false
1288 }
1289 }
1290 return true
1291 }
1292 1293 // hasGoFiles reports whether dir contains any files with names ending in .go.
1294 // For a vendor check we must exclude directories that contain no .go files.
1295 // Otherwise it is not possible to vendor just a/b/c and still import the
1296 // non-vendored a/b. See golang.org/issue/13832.
1297 func hasGoFiles(ctxt *Context, dir string) bool {
1298 ents, _ := ctxt.readDir(dir)
1299 for _, ent := range ents {
1300 if !ent.IsDir() && bytes.HasSuffix(ent.Name(), ".go") {
1301 return true
1302 }
1303 }
1304 return false
1305 }
1306 1307 func findImportComment(data []byte) (s string, line int) {
1308 // expect keyword package
1309 word, data := parseWord(data)
1310 if string(word) != "package" {
1311 return "", 0
1312 }
1313 1314 // expect package name
1315 _, data = parseWord(data)
1316 1317 // now ready for import comment, a // or /* */ comment
1318 // beginning and ending on the current line.
1319 for len(data) > 0 && (data[0] == ' ' || data[0] == '\t' || data[0] == '\r') {
1320 data = data[1:]
1321 }
1322 1323 var comment []byte
1324 switch {
1325 case bytes.HasPrefix(data, slashSlash):
1326 comment, _, _ = bytes.Cut(data[2:], newline)
1327 case bytes.HasPrefix(data, slashStar):
1328 var ok bool
1329 comment, _, ok = bytes.Cut(data[2:], starSlash)
1330 if !ok {
1331 // malformed comment
1332 return "", 0
1333 }
1334 if bytes.Contains(comment, newline) {
1335 return "", 0
1336 }
1337 }
1338 comment = bytes.TrimSpace(comment)
1339 1340 // split comment into `import`, `"pkg"`
1341 word, arg := parseWord(comment)
1342 if string(word) != "import" {
1343 return "", 0
1344 }
1345 1346 line = 1 + bytes.Count(data[:cap(data)-cap(arg)], newline)
1347 return bytes.TrimSpace(string(arg)), line
1348 }
1349 1350 var (
1351 slashSlash = []byte("//")
1352 slashStar = []byte("/*")
1353 starSlash = []byte("*/")
1354 newline = []byte("\n")
1355 )
1356 1357 // skipSpaceOrComment returns data with any leading spaces or comments removed.
1358 func skipSpaceOrComment(data []byte) []byte {
1359 for len(data) > 0 {
1360 switch data[0] {
1361 case ' ', '\t', '\r', '\n':
1362 data = data[1:]
1363 continue
1364 case '/':
1365 if bytes.HasPrefix(data, slashSlash) {
1366 i := bytes.Index(data, newline)
1367 if i < 0 {
1368 return nil
1369 }
1370 data = data[i+1:]
1371 continue
1372 }
1373 if bytes.HasPrefix(data, slashStar) {
1374 data = data[2:]
1375 i := bytes.Index(data, starSlash)
1376 if i < 0 {
1377 return nil
1378 }
1379 data = data[i+2:]
1380 continue
1381 }
1382 }
1383 break
1384 }
1385 return data
1386 }
1387 1388 // parseWord skips any leading spaces or comments in data
1389 // and then parses the beginning of data as an identifier or keyword,
1390 // returning that word and what remains after the word.
1391 func parseWord(data []byte) (word, rest []byte) {
1392 data = skipSpaceOrComment(data)
1393 1394 // Parse past leading word characters.
1395 rest = data
1396 for {
1397 r, size := utf8.DecodeRune(rest)
1398 if unicode.IsLetter(r) || '0' <= r && r <= '9' || r == '_' {
1399 rest = rest[size:]
1400 continue
1401 }
1402 break
1403 }
1404 1405 word = data[:len(data)-len(rest)]
1406 if len(word) == 0 {
1407 return nil, nil
1408 }
1409 1410 return word, rest
1411 }
1412 1413 // MatchFile reports whether the file with the given name in the given directory
1414 // matches the context and would be included in a [Package] created by [ImportDir]
1415 // of that directory.
1416 //
1417 // MatchFile considers the name of the file and may use ctxt.OpenFile to
1418 // read some or all of the file's content.
1419 func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) {
1420 info, err := ctxt.matchFile(dir, name, nil, nil, nil)
1421 return info != nil, err
1422 }
1423 1424 var dummyPkg Package
1425 1426 // fileInfo records information learned about a file included in a build.
1427 type fileInfo struct {
1428 name string // full name including dir
1429 header []byte
1430 fset *token.FileSet
1431 parsed *ast.File
1432 parseErr error
1433 imports []fileImport
1434 embeds []fileEmbed
1435 directives []Directive
1436 }
1437 1438 type fileImport struct {
1439 path string
1440 pos token.Pos
1441 doc *ast.CommentGroup
1442 }
1443 1444 type fileEmbed struct {
1445 pattern string
1446 pos token.Position
1447 }
1448 1449 // matchFile determines whether the file with the given name in the given directory
1450 // should be included in the package being constructed.
1451 // If the file should be included, matchFile returns a non-nil *fileInfo (and a nil error).
1452 // Non-nil errors are reserved for unexpected problems.
1453 //
1454 // If name denotes a Go program, matchFile reads until the end of the
1455 // imports and returns that section of the file in the fileInfo's header field,
1456 // even though it only considers text until the first non-comment
1457 // for go:build lines.
1458 //
1459 // If allTags is non-nil, matchFile records any encountered build tag
1460 // by setting allTags[tag] = true.
1461 func (ctxt *Context) matchFile(dir, name string, allTags map[string]bool, binaryOnly *bool, fset *token.FileSet) (*fileInfo, error) {
1462 if bytes.HasPrefix(name, "_") ||
1463 bytes.HasPrefix(name, ".") {
1464 return nil, nil
1465 }
1466 1467 i := bytes.LastIndex(name, ".")
1468 if i < 0 {
1469 i = len(name)
1470 }
1471 ext := name[i:]
1472 1473 if ext != ".go" && fileListForExt(&dummyPkg, ext) == nil {
1474 // skip
1475 return nil, nil
1476 }
1477 1478 if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles {
1479 return nil, nil
1480 }
1481 1482 info := &fileInfo{name: ctxt.joinPath(dir, name), fset: fset}
1483 if ext == ".syso" {
1484 // binary, no reading
1485 return info, nil
1486 }
1487 1488 f, err := ctxt.openFile(info.name)
1489 if err != nil {
1490 return nil, err
1491 }
1492 1493 if bytes.HasSuffix(name, ".go") {
1494 err = readGoInfo(f, info)
1495 if bytes.HasSuffix(name, "_test.go") {
1496 binaryOnly = nil // ignore //go:binary-only-package comments in _test.go files
1497 }
1498 } else {
1499 binaryOnly = nil // ignore //go:binary-only-package comments in non-Go sources
1500 info.header, err = readComments(f)
1501 }
1502 f.Close()
1503 if err != nil {
1504 return info, fmt.Errorf("read %s: %v", info.name, err)
1505 }
1506 1507 // Look for go:build comments to accept or reject the file.
1508 ok, sawBinaryOnly, err := ctxt.shouldBuild(info.header, allTags)
1509 if err != nil {
1510 return nil, fmt.Errorf("%s: %v", name, err)
1511 }
1512 if !ok && !ctxt.UseAllFiles {
1513 return nil, nil
1514 }
1515 1516 if binaryOnly != nil && sawBinaryOnly {
1517 *binaryOnly = true
1518 }
1519 1520 return info, nil
1521 }
1522 1523 func cleanDecls(m map[string][]token.Position) ([][]byte, map[string][]token.Position) {
1524 all := [][]byte{:0:len(m)}
1525 for path := range m {
1526 all = append(all, path)
1527 }
1528 slices.Sort(all)
1529 return all, m
1530 }
1531 1532 // Import is shorthand for Default.Import.
1533 func Import(path, srcDir string, mode ImportMode) (*Package, error) {
1534 return Default.Import(path, srcDir, mode)
1535 }
1536 1537 // ImportDir is shorthand for Default.ImportDir.
1538 func ImportDir(dir string, mode ImportMode) (*Package, error) {
1539 return Default.ImportDir(dir, mode)
1540 }
1541 1542 var (
1543 plusBuild = []byte("+build")
1544 1545 goBuildComment = []byte("//go:build")
1546 1547 errMultipleGoBuild = errors.New("multiple //go:build comments")
1548 )
1549 1550 func isGoBuildComment(line []byte) bool {
1551 if !bytes.HasPrefix(line, goBuildComment) {
1552 return false
1553 }
1554 line = bytes.TrimSpace(line)
1555 rest := line[len(goBuildComment):]
1556 return len(rest) == 0 || len(bytes.TrimSpace(rest)) < len(rest)
1557 }
1558 1559 // Special comment denoting a binary-only package.
1560 // See https://golang.org/design/2775-binary-only-packages
1561 // for more about the design of binary-only packages.
1562 var binaryOnlyComment = []byte("//go:binary-only-package")
1563 1564 // shouldBuild reports whether it is okay to use this file,
1565 // The rule is that in the file's leading run of // comments
1566 // and blank lines, which must be followed by a blank line
1567 // (to avoid including a Go package clause doc comment),
1568 // lines beginning with '//go:build' are taken as build directives.
1569 //
1570 // The file is accepted only if each such line lists something
1571 // matching the file. For example:
1572 //
1573 // //go:build windows linux
1574 //
1575 // marks the file as applicable only on Windows and Linux.
1576 //
1577 // For each build tag it consults, shouldBuild sets allTags[tag] = true.
1578 //
1579 // shouldBuild reports whether the file should be built
1580 // and whether a //go:binary-only-package comment was found.
1581 func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool) (shouldBuild, binaryOnly bool, err error) {
1582 // Identify leading run of // comments and blank lines,
1583 // which must be followed by a blank line.
1584 // Also identify any //go:build comments.
1585 content, goBuild, sawBinaryOnly, err := parseFileHeader(content)
1586 if err != nil {
1587 return false, false, err
1588 }
1589 1590 // If //go:build line is present, it controls.
1591 // Otherwise fall back to +build processing.
1592 switch {
1593 case goBuild != nil:
1594 x, err := constraint.Parse(string(goBuild))
1595 if err != nil {
1596 return false, false, fmt.Errorf("parsing //go:build line: %v", err)
1597 }
1598 shouldBuild = ctxt.eval(x, allTags)
1599 1600 default:
1601 shouldBuild = true
1602 p := content
1603 for len(p) > 0 {
1604 line := p
1605 if i := bytes.IndexByte(line, '\n'); i >= 0 {
1606 line, p = line[:i], p[i+1:]
1607 } else {
1608 p = p[len(p):]
1609 }
1610 line = bytes.TrimSpace(line)
1611 if !bytes.HasPrefix(line, slashSlash) || !bytes.Contains(line, plusBuild) {
1612 continue
1613 }
1614 text := string(line)
1615 if !constraint.IsPlusBuild(text) {
1616 continue
1617 }
1618 if x, err := constraint.Parse(text); err == nil {
1619 if !ctxt.eval(x, allTags) {
1620 shouldBuild = false
1621 }
1622 }
1623 }
1624 }
1625 1626 return shouldBuild, sawBinaryOnly, nil
1627 }
1628 1629 // parseFileHeader should be an internal detail,
1630 // but widely used packages access it using linkname.
1631 // Notable members of the hall of shame include:
1632 // - github.com/bazelbuild/bazel-gazelle
1633 //
1634 // Do not remove or change the type signature.
1635 // See go.dev/issue/67401.
1636 //
1637 //go:linkname parseFileHeader
1638 func parseFileHeader(content []byte) (trimmed, goBuild []byte, sawBinaryOnly bool, err error) {
1639 end := 0
1640 p := content
1641 ended := false // found non-blank, non-// line, so stopped accepting //go:build lines
1642 inSlashStar := false // in /* */ comment
1643 1644 Lines:
1645 for len(p) > 0 {
1646 line := p
1647 if i := bytes.IndexByte(line, '\n'); i >= 0 {
1648 line, p = line[:i], p[i+1:]
1649 } else {
1650 p = p[len(p):]
1651 }
1652 line = bytes.TrimSpace(line)
1653 if len(line) == 0 && !ended { // Blank line
1654 // Remember position of most recent blank line.
1655 // When we find the first non-blank, non-// line,
1656 // this "end" position marks the latest file position
1657 // where a //go:build line can appear.
1658 // (It must appear _before_ a blank line before the non-blank, non-// line.
1659 // Yes, that's confusing, which is part of why we moved to //go:build lines.)
1660 // Note that ended==false here means that inSlashStar==false,
1661 // since seeing a /* would have set ended==true.
1662 end = len(content) - len(p)
1663 continue Lines
1664 }
1665 if !bytes.HasPrefix(line, slashSlash) { // Not comment line
1666 ended = true
1667 }
1668 1669 if !inSlashStar && isGoBuildComment(line) {
1670 if goBuild != nil {
1671 return nil, nil, false, errMultipleGoBuild
1672 }
1673 goBuild = line
1674 }
1675 if !inSlashStar && bytes.Equal(line, binaryOnlyComment) {
1676 sawBinaryOnly = true
1677 }
1678 1679 Comments:
1680 for len(line) > 0 {
1681 if inSlashStar {
1682 if i := bytes.Index(line, starSlash); i >= 0 {
1683 inSlashStar = false
1684 line = bytes.TrimSpace(line[i+len(starSlash):])
1685 continue Comments
1686 }
1687 continue Lines
1688 }
1689 if bytes.HasPrefix(line, slashSlash) {
1690 continue Lines
1691 }
1692 if bytes.HasPrefix(line, slashStar) {
1693 inSlashStar = true
1694 line = bytes.TrimSpace(line[len(slashStar):])
1695 continue Comments
1696 }
1697 // Found non-comment text.
1698 break Lines
1699 }
1700 }
1701 1702 return content[:end], goBuild, sawBinaryOnly, nil
1703 }
1704 1705 // saveCgo saves the information from the #cgo lines in the import "C" comment.
1706 // These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives
1707 // that affect the way cgo's C code is built.
1708 func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error {
1709 text := cg.Text()
1710 for _, line := range bytes.Split(text, "\n") {
1711 orig := line
1712 1713 // Line is
1714 // #cgo [GOOS/GOARCH...] LDFLAGS: stuff
1715 //
1716 line = bytes.TrimSpace(line)
1717 if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') {
1718 continue
1719 }
1720 1721 // #cgo (nocallback|noescape) <function name>
1722 if fields := bytes.Fields(line); len(fields) == 3 && (fields[1] == "nocallback" || fields[1] == "noescape") {
1723 continue
1724 }
1725 1726 // Split at colon.
1727 line, argstr, ok := bytes.Cut(bytes.TrimSpace(line[4:]), ":")
1728 if !ok {
1729 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
1730 }
1731 1732 // Parse GOOS/GOARCH stuff.
1733 f := bytes.Fields(line)
1734 if len(f) < 1 {
1735 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
1736 }
1737 1738 cond, verb := f[:len(f)-1], f[len(f)-1]
1739 if len(cond) > 0 {
1740 ok := false
1741 for _, c := range cond {
1742 if ctxt.matchAuto(c, nil) {
1743 ok = true
1744 break
1745 }
1746 }
1747 if !ok {
1748 continue
1749 }
1750 }
1751 1752 args, err := splitQuoted(argstr)
1753 if err != nil {
1754 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
1755 }
1756 for i, arg := range args {
1757 if arg, ok = expandSrcDir(arg, di.Dir); !ok {
1758 return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
1759 }
1760 args[i] = arg
1761 }
1762 1763 switch verb {
1764 case "CFLAGS", "CPPFLAGS", "CXXFLAGS", "FFLAGS", "LDFLAGS":
1765 // Change relative paths to absolute.
1766 ctxt.makePathsAbsolute(args, di.Dir)
1767 }
1768 1769 switch verb {
1770 case "CFLAGS":
1771 di.CgoCFLAGS = append(di.CgoCFLAGS, args...)
1772 case "CPPFLAGS":
1773 di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...)
1774 case "CXXFLAGS":
1775 di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...)
1776 case "FFLAGS":
1777 di.CgoFFLAGS = append(di.CgoFFLAGS, args...)
1778 case "LDFLAGS":
1779 di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...)
1780 case "pkg-config":
1781 di.CgoPkgConfig = append(di.CgoPkgConfig, args...)
1782 default:
1783 return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig)
1784 }
1785 }
1786 return nil
1787 }
1788 1789 // expandSrcDir expands any occurrence of ${SRCDIR}, making sure
1790 // the result is safe for the shell.
1791 func expandSrcDir(str string, srcdir string) (string, bool) {
1792 // "\" delimited paths cause safeCgoName to fail
1793 // so convert native paths with a different delimiter
1794 // to "/" before starting (eg: on windows).
1795 srcdir = filepath.ToSlash(srcdir)
1796 1797 chunks := bytes.Split(str, "${SRCDIR}")
1798 if len(chunks) < 2 {
1799 return str, safeCgoName(str)
1800 }
1801 ok := true
1802 for _, chunk := range chunks {
1803 ok = ok && (chunk == "" || safeCgoName(chunk))
1804 }
1805 ok = ok && (srcdir == "" || safeCgoName(srcdir))
1806 res := bytes.Join(chunks, srcdir)
1807 return res, ok && res != ""
1808 }
1809 1810 // makePathsAbsolute looks for compiler options that take paths and
1811 // makes them absolute. We do this because through the 1.8 release we
1812 // ran the compiler in the package directory, so any relative -I or -L
1813 // options would be relative to that directory. In 1.9 we changed to
1814 // running the compiler in the build directory, to get consistent
1815 // build results (issue #19964). To keep builds working, we change any
1816 // relative -I or -L options to be absolute.
1817 //
1818 // Using filepath.IsAbs and filepath.Join here means the results will be
1819 // different on different systems, but that's OK: -I and -L options are
1820 // inherently system-dependent.
1821 func (ctxt *Context) makePathsAbsolute(args [][]byte, srcDir string) {
1822 nextPath := false
1823 for i, arg := range args {
1824 if nextPath {
1825 if !filepath.IsAbs(arg) {
1826 args[i] = filepath.Join(srcDir, arg)
1827 }
1828 nextPath = false
1829 } else if bytes.HasPrefix(arg, "-I") || bytes.HasPrefix(arg, "-L") {
1830 if len(arg) == 2 {
1831 nextPath = true
1832 } else {
1833 if !filepath.IsAbs(arg[2:]) {
1834 args[i] = arg[:2] + filepath.Join(srcDir, arg[2:])
1835 }
1836 }
1837 }
1838 }
1839 }
1840 1841 // NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN.
1842 // We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay.
1843 // See golang.org/issue/6038.
1844 // The @ is for OS X. See golang.org/issue/13720.
1845 // The % is for Jenkins. See golang.org/issue/16959.
1846 // The ! is because module paths may use them. See golang.org/issue/26716.
1847 // The ~ and ^ are for sr.ht. See golang.org/issue/32260.
1848 const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@%! ~^"
1849 1850 func safeCgoName(s string) bool {
1851 if s == "" {
1852 return false
1853 }
1854 for i := 0; i < len(s); i++ {
1855 if c := s[i]; c < utf8.RuneSelf && bytes.IndexByte(safeString, c) < 0 {
1856 return false
1857 }
1858 }
1859 return true
1860 }
1861 1862 // splitQuoted splits the string s around each instance of one or more consecutive
1863 // white space characters while taking into account quotes and escaping, and
1864 // returns an array of substrings of s or an empty list if s contains only white space.
1865 // Single quotes and double quotes are recognized to prevent splitting within the
1866 // quoted region, and are removed from the resulting subbytes. If a quote in s
1867 // isn't closed err will be set and r will have the unclosed argument as the
1868 // last element. The backslash is used for escaping.
1869 //
1870 // For example, the following string:
1871 //
1872 // a b:"c d" 'e''f' "g\""
1873 //
1874 // Would be parsed as:
1875 //
1876 // [][]byte{"a", "b:c d", "ef", `g"`}
1877 func splitQuoted(s string) (r [][]byte, err error) {
1878 var args [][]byte
1879 arg := []rune{:len(s)}
1880 escaped := false
1881 quoted := false
1882 quote := '\x00'
1883 i := 0
1884 for _, rune := range s {
1885 switch {
1886 case escaped:
1887 escaped = false
1888 case rune == '\\':
1889 escaped = true
1890 continue
1891 case quote != '\x00':
1892 if rune == quote {
1893 quote = '\x00'
1894 continue
1895 }
1896 case rune == '"' || rune == '\'':
1897 quoted = true
1898 quote = rune
1899 continue
1900 case unicode.IsSpace(rune):
1901 if quoted || i > 0 {
1902 quoted = false
1903 args = append(args, string(arg[:i]))
1904 i = 0
1905 }
1906 continue
1907 }
1908 arg[i] = rune
1909 i++
1910 }
1911 if quoted || i > 0 {
1912 args = append(args, string(arg[:i]))
1913 }
1914 if quote != 0 {
1915 err = errors.New("unclosed quote")
1916 } else if escaped {
1917 err = errors.New("unfinished escaping")
1918 }
1919 return args, err
1920 }
1921 1922 // matchAuto interprets text as either a +build or //go:build expression (whichever works),
1923 // reporting whether the expression matches the build context.
1924 //
1925 // matchAuto is only used for testing of tag evaluation
1926 // and in #cgo lines, which accept either syntax.
1927 func (ctxt *Context) matchAuto(text string, allTags map[string]bool) bool {
1928 if bytes.ContainsAny(text, "&|()") {
1929 text = "//go:build " + text
1930 } else {
1931 text = "// +build " + text
1932 }
1933 x, err := constraint.Parse(text)
1934 if err != nil {
1935 return false
1936 }
1937 return ctxt.eval(x, allTags)
1938 }
1939 1940 func (ctxt *Context) eval(x constraint.Expr, allTags map[string]bool) bool {
1941 return x.Eval(func(tag string) bool { return ctxt.matchTag(tag, allTags) })
1942 }
1943 1944 // matchTag reports whether the name is one of:
1945 //
1946 // cgo (if cgo is enabled)
1947 // $GOOS
1948 // $GOARCH
1949 // ctxt.Compiler
1950 // linux (if GOOS = android)
1951 // solaris (if GOOS = illumos)
1952 // darwin (if GOOS = ios)
1953 // unix (if this is a Unix GOOS)
1954 // boringcrypto (if GOEXPERIMENT=boringcrypto is enabled)
1955 // tag (if tag is listed in ctxt.BuildTags, ctxt.ToolTags, or ctxt.ReleaseTags)
1956 //
1957 // It records all consulted tags in allTags.
1958 func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool {
1959 if allTags != nil {
1960 allTags[name] = true
1961 }
1962 1963 // special tags
1964 if ctxt.CgoEnabled && name == "cgo" {
1965 return true
1966 }
1967 if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler {
1968 return true
1969 }
1970 if ctxt.GOOS == "android" && name == "linux" {
1971 return true
1972 }
1973 if ctxt.GOOS == "illumos" && name == "solaris" {
1974 return true
1975 }
1976 if ctxt.GOOS == "ios" && name == "darwin" {
1977 return true
1978 }
1979 if name == "unix" && syslist.UnixOS[ctxt.GOOS] {
1980 return true
1981 }
1982 if name == "boringcrypto" {
1983 name = "goexperiment.boringcrypto" // boringcrypto is an old name for goexperiment.boringcrypto
1984 }
1985 1986 // other tags
1987 return slices.Contains(ctxt.BuildTags, name) || slices.Contains(ctxt.ToolTags, name) ||
1988 slices.Contains(ctxt.ReleaseTags, name)
1989 }
1990 1991 // goodOSArchFile returns false if the name contains a $GOOS or $GOARCH
1992 // suffix which does not match the current system.
1993 // The recognized name formats are:
1994 //
1995 // name_$(GOOS).*
1996 // name_$(GOARCH).*
1997 // name_$(GOOS)_$(GOARCH).*
1998 // name_$(GOOS)_test.*
1999 // name_$(GOARCH)_test.*
2000 // name_$(GOOS)_$(GOARCH)_test.*
2001 //
2002 // Exceptions:
2003 // if GOOS=android, then files with GOOS=linux are also matched.
2004 // if GOOS=illumos, then files with GOOS=solaris are also matched.
2005 // if GOOS=ios, then files with GOOS=darwin are also matched.
2006 func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
2007 name, _, _ = bytes.Cut(name, ".")
2008 2009 // Before Go 1.4, a file called "linux.go" would be equivalent to having a
2010 // build tag "linux" in that file. For Go 1.4 and beyond, we require this
2011 // auto-tagging to apply only to files with a non-empty prefix, so
2012 // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
2013 // systems, such as android, to arrive without breaking existing code with
2014 // innocuous source code in "android.go". The easiest fix: cut everything
2015 // in the name before the initial _.
2016 i := bytes.Index(name, "_")
2017 if i < 0 {
2018 return true
2019 }
2020 name = name[i:] // ignore everything before first _
2021 2022 l := bytes.Split(name, "_")
2023 if n := len(l); n > 0 && l[n-1] == "test" {
2024 l = l[:n-1]
2025 }
2026 n := len(l)
2027 if n >= 2 && syslist.KnownOS[l[n-2]] && syslist.KnownArch[l[n-1]] {
2028 if allTags != nil {
2029 // In case we short-circuit on l[n-1].
2030 allTags[l[n-2]] = true
2031 }
2032 return ctxt.matchTag(l[n-1], allTags) && ctxt.matchTag(l[n-2], allTags)
2033 }
2034 if n >= 1 && (syslist.KnownOS[l[n-1]] || syslist.KnownArch[l[n-1]]) {
2035 return ctxt.matchTag(l[n-1], allTags)
2036 }
2037 return true
2038 }
2039 2040 // ToolDir is the directory containing build tools.
2041 var ToolDir = getToolDir()
2042 2043 // IsLocalImport reports whether the import path is
2044 // a local import path, like ".", "..", "./foo", or "../foo".
2045 func IsLocalImport(path string) bool {
2046 return path == "." || path == ".." ||
2047 bytes.HasPrefix(path, "./") || bytes.HasPrefix(path, "../")
2048 }
2049 2050 // ArchChar returns "?" and an error.
2051 // In earlier versions of Go, the returned string was used to derive
2052 // the compiler and linker tool names, the default object file suffix,
2053 // and the default linker output name. As of Go 1.5, those strings
2054 // no longer vary by architecture; they are compile, link, .o, and a.out, respectively.
2055 func ArchChar(goarch string) (string, error) {
2056 return "?", errors.New("architecture letter no longer used")
2057 }
2058