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 5package build 6 7import ( 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 "io" 21 "io/fs" 22 "os" 23 "os/exec" 24 pathpkg "path" 25 "path/filepath" 26 "runtime" 27 "slices" 28 "strconv" 29 "strings" 30 "unicode" 31 "unicode/utf8" 32 _ "unsafe" // for linkname 33) 34 35// A Context specifies the supporting context for a build. 36type 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 []string 65 ToolTags []string 66 ReleaseTags []string 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 ...string) 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) []string 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. 118func (ctxt *Context) joinPath(elem ...string) 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. 126func (ctxt *Context) splitPathList(s string) []string { 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. 134func (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. 142func (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. 152func (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. 178func hasSubdir(root, dir string) (rel string, ok bool) { 179 const sep = string(filepath.Separator) 180 root = filepath.Clean(root) 181 if !strings.HasSuffix(root, sep) { 182 root += sep 183 } 184 dir = filepath.Clean(dir) 185 after, found := strings.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. 193func (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 := make([]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. 210func (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. 225func (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. 235func (ctxt *Context) gopath() []string { 236 var all []string 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 strings.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. 268func (ctxt *Context) SrcDirs() []string { 269 var all []string 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. 288var Default Context = defaultContext() 289 290// Keep consistent with cmd/go/internal/cfg.defaultGOPATH. 291func 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 319var defaultToolTags []string 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 330var defaultReleaseTags []string 331 332func 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([]string{}, 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([]string{}, c.ReleaseTags...) // our own private copy 358 359 env := os.Getenv("CGO_ENABLED") 360 if env == "" { 361 env = 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 380func 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. 389type ImportMode uint 390 391const ( 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. 436type 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 []string // 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 []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) 455 CgoFiles []string // .go source files that import "C" 456 IgnoredGoFiles []string // .go source files ignored for this build (including ignored _test.go files) 457 InvalidGoFiles []string // .go source files with detected problems (parse error, wrong package name, and so on) 458 IgnoredOtherFiles []string // non-.go source files ignored for this build 459 CFiles []string // .c source files 460 CXXFiles []string // .cc, .cpp and .cxx source files 461 MFiles []string // .m (Objective-C) source files 462 HFiles []string // .h, .hh, .hpp and .hxx source files 463 FFiles []string // .f, .F, .for and .f90 Fortran source files 464 SFiles []string // .s source files 465 SwigFiles []string // .swig files 466 SwigCXXFiles []string // .swigcxx files 467 SysoFiles []string // .syso system object files to add to archive 468 469 // Cgo directives 470 CgoCFLAGS []string // Cgo CFLAGS directives 471 CgoCPPFLAGS []string // Cgo CPPFLAGS directives 472 CgoCXXFLAGS []string // Cgo CXXFLAGS directives 473 CgoFFLAGS []string // Cgo FFLAGS directives 474 CgoLDFLAGS []string // Cgo LDFLAGS directives 475 CgoPkgConfig []string // Cgo pkg-config directives 476 477 // Test information 478 TestGoFiles []string // _test.go files in package 479 XTestGoFiles []string // _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 []string // import paths from GoFiles, CgoFiles 488 ImportPos map[string][]token.Position // line information for Imports 489 TestImports []string // import paths from TestGoFiles 490 TestImportPos map[string][]token.Position // line information for TestImports 491 XTestImports []string // 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 []string // patterns from GoFiles, CgoFiles 500 EmbedPatternPos map[string][]token.Position // line information for EmbedPatterns 501 TestEmbedPatterns []string // patterns from TestGoFiles 502 TestEmbedPatternPos map[string][]token.Position // line information for TestEmbedPatterns 503 XTestEmbedPatterns []string // 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. 508type 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. 516func (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. 522func (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.) 529type NoGoError struct { 530 Dir string 531} 532 533func (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. 539type MultiplePackageError struct { 540 Dir string // directory containing files 541 Packages []string // package names found 542 Files []string // corresponding files: Files[i] declares package Packages[i] 543} 544 545func (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 550func nameExt(name string) string { 551 i := strings.LastIndex(name, ".") 552 if i < 0 { 553 return "" 554 } 555 return name[i:] 556} 557 558var 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. 575func (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 strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || strings.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 strings.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 []string 681 goroot string 682 gopath []string 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 || !strings.HasPrefix(sub, "src/") || strings.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 = strings.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 := strings.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 == "" || !strings.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 []string 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, strings.Join(paths, "\n")) 809 } 810 811Found: 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 := make(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 []string // files with ".S"(capital S)/.sx(capital s equivalent for case insensitive filesystems) 876 var firstFile, firstCommentFile string 877 embedPos := make(map[string][]token.Position) 878 testEmbedPos := make(map[string][]token.Position) 879 xTestEmbedPos := make(map[string][]token.Position) 880 importPos := make(map[string][]token.Position) 881 testImportPos := make(map[string][]token.Position) 882 xTestImportPos := make(map[string][]token.Position) 883 allTags := make(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 && strings.HasSuffix(name, ".go") { 901 badGoFile(name, err) 902 continue 903 } 904 if info == nil { 905 if strings.HasPrefix(name, "_") || strings.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 := strings.HasSuffix(name, "_test.go") 948 isXTest := false 949 if isTest && strings.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: []string{p.Name, pkg}, 964 Files: []string{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 *[]string 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 1084func fileListForExt(p *Package, ext string) *[]string { 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 1108func uniq(list []string) []string { 1109 if list == nil { 1110 return nil 1111 } 1112 out := make([]string, 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 1124var 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. 1136func (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 := make([]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="+strings.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 strings.Builder 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 := strings.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 := strings.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 1281func equal(x, y []string) 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. 1297func hasGoFiles(ctxt *Context, dir string) bool { 1298 ents, _ := ctxt.readDir(dir) 1299 for _, ent := range ents { 1300 if !ent.IsDir() && strings.HasSuffix(ent.Name(), ".go") { 1301 return true 1302 } 1303 } 1304 return false 1305} 1306 1307func 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 strings.TrimSpace(string(arg)), line 1348} 1349 1350var ( 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. 1358func 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. 1391func 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. 1419func (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 1424var dummyPkg Package 1425 1426// fileInfo records information learned about a file included in a build. 1427type 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 1438type fileImport struct { 1439 path string 1440 pos token.Pos 1441 doc *ast.CommentGroup 1442} 1443 1444type 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. 1461func (ctxt *Context) matchFile(dir, name string, allTags map[string]bool, binaryOnly *bool, fset *token.FileSet) (*fileInfo, error) { 1462 if strings.HasPrefix(name, "_") || 1463 strings.HasPrefix(name, ".") { 1464 return nil, nil 1465 } 1466 1467 i := strings.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 strings.HasSuffix(name, ".go") { 1494 err = readGoInfo(f, info) 1495 if strings.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 1523func cleanDecls(m map[string][]token.Position) ([]string, map[string][]token.Position) { 1524 all := make([]string, 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. 1533func Import(path, srcDir string, mode ImportMode) (*Package, error) { 1534 return Default.Import(path, srcDir, mode) 1535} 1536 1537// ImportDir is shorthand for Default.ImportDir. 1538func ImportDir(dir string, mode ImportMode) (*Package, error) { 1539 return Default.ImportDir(dir, mode) 1540} 1541 1542var ( 1543 plusBuild = []byte("+build") 1544 1545 goBuildComment = []byte("//go:build") 1546 1547 errMultipleGoBuild = errors.New("multiple //go:build comments") 1548) 1549 1550func 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. 1562var 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. 1581func (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 1638func 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 1644Lines: 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. 1708func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error { 1709 text := cg.Text() 1710 for _, line := range strings.Split(text, "\n") { 1711 orig := line 1712 1713 // Line is 1714 // #cgo [GOOS/GOARCH...] LDFLAGS: stuff 1715 // 1716 line = strings.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 := strings.Fields(line); len(fields) == 3 && (fields[1] == "nocallback" || fields[1] == "noescape") { 1723 continue 1724 } 1725 1726 // Split at colon. 1727 line, argstr, ok := strings.Cut(strings.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 := strings.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. 1791func 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 := strings.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 := strings.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. 1821func (ctxt *Context) makePathsAbsolute(args []string, 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 strings.HasPrefix(arg, "-I") || strings.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. 1848const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@%! ~^" 1849 1850func 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 && strings.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 substrings. 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// []string{"a", "b:c d", "ef", `g"`} 1877func splitQuoted(s string) (r []string, err error) { 1878 var args []string 1879 arg := make([]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. 1927func (ctxt *Context) matchAuto(text string, allTags map[string]bool) bool { 1928 if strings.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 1940func (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. 1958func (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" && 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 for _, tag := range ctxt.BuildTags { 1988 if tag == name { 1989 return true 1990 } 1991 } 1992 for _, tag := range ctxt.ToolTags { 1993 if tag == name { 1994 return true 1995 } 1996 } 1997 for _, tag := range ctxt.ReleaseTags { 1998 if tag == name { 1999 return true 2000 } 2001 } 2002 2003 return false 2004} 2005 2006// goodOSArchFile returns false if the name contains a $GOOS or $GOARCH 2007// suffix which does not match the current system. 2008// The recognized name formats are: 2009// 2010// name_$(GOOS).* 2011// name_$(GOARCH).* 2012// name_$(GOOS)_$(GOARCH).* 2013// name_$(GOOS)_test.* 2014// name_$(GOARCH)_test.* 2015// name_$(GOOS)_$(GOARCH)_test.* 2016// 2017// Exceptions: 2018// if GOOS=android, then files with GOOS=linux are also matched. 2019// if GOOS=illumos, then files with GOOS=solaris are also matched. 2020// if GOOS=ios, then files with GOOS=darwin are also matched. 2021func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool { 2022 name, _, _ = strings.Cut(name, ".") 2023 2024 // Before Go 1.4, a file called "linux.go" would be equivalent to having a 2025 // build tag "linux" in that file. For Go 1.4 and beyond, we require this 2026 // auto-tagging to apply only to files with a non-empty prefix, so 2027 // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating 2028 // systems, such as android, to arrive without breaking existing code with 2029 // innocuous source code in "android.go". The easiest fix: cut everything 2030 // in the name before the initial _. 2031 i := strings.Index(name, "_") 2032 if i < 0 { 2033 return true 2034 } 2035 name = name[i:] // ignore everything before first _ 2036 2037 l := strings.Split(name, "_") 2038 if n := len(l); n > 0 && l[n-1] == "test" { 2039 l = l[:n-1] 2040 } 2041 n := len(l) 2042 if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] { 2043 if allTags != nil { 2044 // In case we short-circuit on l[n-1]. 2045 allTags[l[n-2]] = true 2046 } 2047 return ctxt.matchTag(l[n-1], allTags) && ctxt.matchTag(l[n-2], allTags) 2048 } 2049 if n >= 1 && (knownOS[l[n-1]] || knownArch[l[n-1]]) { 2050 return ctxt.matchTag(l[n-1], allTags) 2051 } 2052 return true 2053} 2054 2055// ToolDir is the directory containing build tools. 2056var ToolDir = getToolDir() 2057 2058// IsLocalImport reports whether the import path is 2059// a local import path, like ".", "..", "./foo", or "../foo". 2060func IsLocalImport(path string) bool { 2061 return path == "." || path == ".." || 2062 strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../") 2063} 2064 2065// ArchChar returns "?" and an error. 2066// In earlier versions of Go, the returned string was used to derive 2067// the compiler and linker tool names, the default object file suffix, 2068// and the default linker output name. As of Go 1.5, those strings 2069// no longer vary by architecture; they are compile, link, .o, and a.out, respectively. 2070func ArchChar(goarch string) (string, error) { 2071 return "?", errors.New("architecture letter no longer used") 2072} 2073