github.com/euank/go@v0.0.0-20160829210321-495514729181/src/go/build/build.go (about) 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/doc" 13 "go/parser" 14 "go/token" 15 "io" 16 "io/ioutil" 17 "log" 18 "os" 19 pathpkg "path" 20 "path/filepath" 21 "runtime" 22 "sort" 23 "strconv" 24 "strings" 25 "unicode" 26 "unicode/utf8" 27 ) 28 29 // A Context specifies the supporting context for a build. 30 type Context struct { 31 GOARCH string // target architecture 32 GOOS string // target operating system 33 GOROOT string // Go root 34 GOPATH string // Go path 35 CgoEnabled bool // whether cgo can be used 36 UseAllFiles bool // use files regardless of +build lines, file names 37 Compiler string // compiler to assume when computing target paths 38 39 // The build and release tags specify build constraints 40 // that should be considered satisfied when processing +build lines. 41 // Clients creating a new context may customize BuildTags, which 42 // defaults to empty, but it is usually an error to customize ReleaseTags, 43 // which defaults to the list of Go releases the current release is compatible with. 44 // In addition to the BuildTags and ReleaseTags, build constraints 45 // consider the values of GOARCH and GOOS as satisfied tags. 46 BuildTags []string 47 ReleaseTags []string 48 49 // The install suffix specifies a suffix to use in the name of the installation 50 // directory. By default it is empty, but custom builds that need to keep 51 // their outputs separate can set InstallSuffix to do so. For example, when 52 // using the race detector, the go command uses InstallSuffix = "race", so 53 // that on a Linux/386 system, packages are written to a directory named 54 // "linux_386_race" instead of the usual "linux_386". 55 InstallSuffix string 56 57 // By default, Import uses the operating system's file system calls 58 // to read directories and files. To read from other sources, 59 // callers can set the following functions. They all have default 60 // behaviors that use the local file system, so clients need only set 61 // the functions whose behaviors they wish to change. 62 63 // JoinPath joins the sequence of path fragments into a single path. 64 // If JoinPath is nil, Import uses filepath.Join. 65 JoinPath func(elem ...string) string 66 67 // SplitPathList splits the path list into a slice of individual paths. 68 // If SplitPathList is nil, Import uses filepath.SplitList. 69 SplitPathList func(list string) []string 70 71 // IsAbsPath reports whether path is an absolute path. 72 // If IsAbsPath is nil, Import uses filepath.IsAbs. 73 IsAbsPath func(path string) bool 74 75 // IsDir reports whether the path names a directory. 76 // If IsDir is nil, Import calls os.Stat and uses the result's IsDir method. 77 IsDir func(path string) bool 78 79 // HasSubdir reports whether dir is a subdirectory of 80 // (perhaps multiple levels below) root. 81 // If so, HasSubdir sets rel to a slash-separated path that 82 // can be joined to root to produce a path equivalent to dir. 83 // If HasSubdir is nil, Import uses an implementation built on 84 // filepath.EvalSymlinks. 85 HasSubdir func(root, dir string) (rel string, ok bool) 86 87 // ReadDir returns a slice of os.FileInfo, sorted by Name, 88 // describing the content of the named directory. 89 // If ReadDir is nil, Import uses ioutil.ReadDir. 90 ReadDir func(dir string) ([]os.FileInfo, error) 91 92 // OpenFile opens a file (not a directory) for reading. 93 // If OpenFile is nil, Import uses os.Open. 94 OpenFile func(path string) (io.ReadCloser, error) 95 } 96 97 // joinPath calls ctxt.JoinPath (if not nil) or else filepath.Join. 98 func (ctxt *Context) joinPath(elem ...string) string { 99 if f := ctxt.JoinPath; f != nil { 100 return f(elem...) 101 } 102 return filepath.Join(elem...) 103 } 104 105 // splitPathList calls ctxt.SplitPathList (if not nil) or else filepath.SplitList. 106 func (ctxt *Context) splitPathList(s string) []string { 107 if f := ctxt.SplitPathList; f != nil { 108 return f(s) 109 } 110 return filepath.SplitList(s) 111 } 112 113 // isAbsPath calls ctxt.IsAbsPath (if not nil) or else filepath.IsAbs. 114 func (ctxt *Context) isAbsPath(path string) bool { 115 if f := ctxt.IsAbsPath; f != nil { 116 return f(path) 117 } 118 return filepath.IsAbs(path) 119 } 120 121 // isDir calls ctxt.IsDir (if not nil) or else uses os.Stat. 122 func (ctxt *Context) isDir(path string) bool { 123 if f := ctxt.IsDir; f != nil { 124 return f(path) 125 } 126 fi, err := os.Stat(path) 127 return err == nil && fi.IsDir() 128 } 129 130 // hasSubdir calls ctxt.HasSubdir (if not nil) or else uses 131 // the local file system to answer the question. 132 func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) { 133 if f := ctxt.HasSubdir; f != nil { 134 return f(root, dir) 135 } 136 137 // Try using paths we received. 138 if rel, ok = hasSubdir(root, dir); ok { 139 return 140 } 141 142 // Try expanding symlinks and comparing 143 // expanded against unexpanded and 144 // expanded against expanded. 145 rootSym, _ := filepath.EvalSymlinks(root) 146 dirSym, _ := filepath.EvalSymlinks(dir) 147 148 if rel, ok = hasSubdir(rootSym, dir); ok { 149 return 150 } 151 if rel, ok = hasSubdir(root, dirSym); ok { 152 return 153 } 154 return hasSubdir(rootSym, dirSym) 155 } 156 157 func hasSubdir(root, dir string) (rel string, ok bool) { 158 const sep = string(filepath.Separator) 159 root = filepath.Clean(root) 160 if !strings.HasSuffix(root, sep) { 161 root += sep 162 } 163 dir = filepath.Clean(dir) 164 if !strings.HasPrefix(dir, root) { 165 return "", false 166 } 167 return filepath.ToSlash(dir[len(root):]), true 168 } 169 170 // readDir calls ctxt.ReadDir (if not nil) or else ioutil.ReadDir. 171 func (ctxt *Context) readDir(path string) ([]os.FileInfo, error) { 172 if f := ctxt.ReadDir; f != nil { 173 return f(path) 174 } 175 return ioutil.ReadDir(path) 176 } 177 178 // openFile calls ctxt.OpenFile (if not nil) or else os.Open. 179 func (ctxt *Context) openFile(path string) (io.ReadCloser, error) { 180 if fn := ctxt.OpenFile; fn != nil { 181 return fn(path) 182 } 183 184 f, err := os.Open(path) 185 if err != nil { 186 return nil, err // nil interface 187 } 188 return f, nil 189 } 190 191 // isFile determines whether path is a file by trying to open it. 192 // It reuses openFile instead of adding another function to the 193 // list in Context. 194 func (ctxt *Context) isFile(path string) bool { 195 f, err := ctxt.openFile(path) 196 if err != nil { 197 return false 198 } 199 f.Close() 200 return true 201 } 202 203 // gopath returns the list of Go path directories. 204 func (ctxt *Context) gopath() []string { 205 var all []string 206 for _, p := range ctxt.splitPathList(ctxt.GOPATH) { 207 if p == "" || p == ctxt.GOROOT { 208 // Empty paths are uninteresting. 209 // If the path is the GOROOT, ignore it. 210 // People sometimes set GOPATH=$GOROOT. 211 // Do not get confused by this common mistake. 212 continue 213 } 214 if strings.HasPrefix(p, "~") { 215 // Path segments starting with ~ on Unix are almost always 216 // users who have incorrectly quoted ~ while setting GOPATH, 217 // preventing it from expanding to $HOME. 218 // The situation is made more confusing by the fact that 219 // bash allows quoted ~ in $PATH (most shells do not). 220 // Do not get confused by this, and do not try to use the path. 221 // It does not exist, and printing errors about it confuses 222 // those users even more, because they think "sure ~ exists!". 223 // The go command diagnoses this situation and prints a 224 // useful error. 225 // On Windows, ~ is used in short names, such as c:\progra~1 226 // for c:\program files. 227 continue 228 } 229 all = append(all, p) 230 } 231 return all 232 } 233 234 // SrcDirs returns a list of package source root directories. 235 // It draws from the current Go root and Go path but omits directories 236 // that do not exist. 237 func (ctxt *Context) SrcDirs() []string { 238 var all []string 239 if ctxt.GOROOT != "" { 240 dir := ctxt.joinPath(ctxt.GOROOT, "src") 241 if ctxt.isDir(dir) { 242 all = append(all, dir) 243 } 244 } 245 for _, p := range ctxt.gopath() { 246 dir := ctxt.joinPath(p, "src") 247 if ctxt.isDir(dir) { 248 all = append(all, dir) 249 } 250 } 251 return all 252 } 253 254 // Default is the default Context for builds. 255 // It uses the GOARCH, GOOS, GOROOT, and GOPATH environment variables 256 // if set, or else the compiled code's GOARCH, GOOS, and GOROOT. 257 var Default Context = defaultContext() 258 259 func defaultContext() Context { 260 var c Context 261 262 c.GOARCH = envOr("GOARCH", runtime.GOARCH) 263 c.GOOS = envOr("GOOS", runtime.GOOS) 264 c.GOROOT = pathpkg.Clean(runtime.GOROOT()) 265 c.GOPATH = envOr("GOPATH", "") 266 c.Compiler = runtime.Compiler 267 268 // Each major Go release in the Go 1.x series should add a tag here. 269 // Old tags should not be removed. That is, the go1.x tag is present 270 // in all releases >= Go 1.x. Code that requires Go 1.x or later should 271 // say "+build go1.x", and code that should only be built before Go 1.x 272 // (perhaps it is the stub to use in that case) should say "+build !go1.x". 273 c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5", "go1.6", "go1.7", "go1.8"} 274 275 switch os.Getenv("CGO_ENABLED") { 276 case "1": 277 c.CgoEnabled = true 278 case "0": 279 c.CgoEnabled = false 280 default: 281 // cgo must be explicitly enabled for cross compilation builds 282 if runtime.GOARCH == c.GOARCH && runtime.GOOS == c.GOOS { 283 c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH] 284 break 285 } 286 c.CgoEnabled = false 287 } 288 289 return c 290 } 291 292 func envOr(name, def string) string { 293 s := os.Getenv(name) 294 if s == "" { 295 return def 296 } 297 return s 298 } 299 300 // An ImportMode controls the behavior of the Import method. 301 type ImportMode uint 302 303 const ( 304 // If FindOnly is set, Import stops after locating the directory 305 // that should contain the sources for a package. It does not 306 // read any files in the directory. 307 FindOnly ImportMode = 1 << iota 308 309 // If AllowBinary is set, Import can be satisfied by a compiled 310 // package object without corresponding sources. 311 // 312 // Deprecated: 313 // The supported way to create a compiled-only package is to 314 // write source code containing a //go:binary-only-package comment at 315 // the top of the file. Such a package will be recognized 316 // regardless of this flag setting (because it has source code) 317 // and will have BinaryOnly set to true in the returned Package. 318 AllowBinary 319 320 // If ImportComment is set, parse import comments on package statements. 321 // Import returns an error if it finds a comment it cannot understand 322 // or finds conflicting comments in multiple source files. 323 // See golang.org/s/go14customimport for more information. 324 ImportComment 325 326 // By default, Import searches vendor directories 327 // that apply in the given source directory before searching 328 // the GOROOT and GOPATH roots. 329 // If an Import finds and returns a package using a vendor 330 // directory, the resulting ImportPath is the complete path 331 // to the package, including the path elements leading up 332 // to and including "vendor". 333 // For example, if Import("y", "x/subdir", 0) finds 334 // "x/vendor/y", the returned package's ImportPath is "x/vendor/y", 335 // not plain "y". 336 // See golang.org/s/go15vendor for more information. 337 // 338 // Setting IgnoreVendor ignores vendor directories. 339 IgnoreVendor 340 ) 341 342 // A Package describes the Go package found in a directory. 343 type Package struct { 344 Dir string // directory containing package sources 345 Name string // package name 346 ImportComment string // path in import comment on package statement 347 Doc string // documentation synopsis 348 ImportPath string // import path of package ("" if unknown) 349 Root string // root of Go tree where this package lives 350 SrcRoot string // package source root directory ("" if unknown) 351 PkgRoot string // package install root directory ("" if unknown) 352 PkgTargetRoot string // architecture dependent install root directory ("" if unknown) 353 BinDir string // command install directory ("" if unknown) 354 Goroot bool // package found in Go root 355 PkgObj string // installed .a file 356 AllTags []string // tags that can influence file selection in this directory 357 ConflictDir string // this directory shadows Dir in $GOPATH 358 BinaryOnly bool // cannot be rebuilt from source (has //go:binary-only-package comment) 359 360 // Source files 361 GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) 362 CgoFiles []string // .go source files that import "C" 363 IgnoredGoFiles []string // .go source files ignored for this build 364 InvalidGoFiles []string // .go source files with detected problems (parse error, wrong package name, and so on) 365 CFiles []string // .c source files 366 CXXFiles []string // .cc, .cpp and .cxx source files 367 MFiles []string // .m (Objective-C) source files 368 HFiles []string // .h, .hh, .hpp and .hxx source files 369 FFiles []string // .f, .F, .for and .f90 Fortran source files 370 SFiles []string // .s source files 371 SwigFiles []string // .swig files 372 SwigCXXFiles []string // .swigcxx files 373 SysoFiles []string // .syso system object files to add to archive 374 375 // Cgo directives 376 CgoCFLAGS []string // Cgo CFLAGS directives 377 CgoCPPFLAGS []string // Cgo CPPFLAGS directives 378 CgoCXXFLAGS []string // Cgo CXXFLAGS directives 379 CgoFFLAGS []string // Cgo FFLAGS directives 380 CgoLDFLAGS []string // Cgo LDFLAGS directives 381 CgoPkgConfig []string // Cgo pkg-config directives 382 383 // Dependency information 384 Imports []string // imports from GoFiles, CgoFiles 385 ImportPos map[string][]token.Position // line information for Imports 386 387 // Test information 388 TestGoFiles []string // _test.go files in package 389 TestImports []string // imports from TestGoFiles 390 TestImportPos map[string][]token.Position // line information for TestImports 391 XTestGoFiles []string // _test.go files outside package 392 XTestImports []string // imports from XTestGoFiles 393 XTestImportPos map[string][]token.Position // line information for XTestImports 394 } 395 396 // IsCommand reports whether the package is considered a 397 // command to be installed (not just a library). 398 // Packages named "main" are treated as commands. 399 func (p *Package) IsCommand() bool { 400 return p.Name == "main" 401 } 402 403 // ImportDir is like Import but processes the Go package found in 404 // the named directory. 405 func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) { 406 return ctxt.Import(".", dir, mode) 407 } 408 409 // NoGoError is the error used by Import to describe a directory 410 // containing no buildable Go source files. (It may still contain 411 // test files, files hidden by build tags, and so on.) 412 type NoGoError struct { 413 Dir string 414 } 415 416 func (e *NoGoError) Error() string { 417 return "no buildable Go source files in " + e.Dir 418 } 419 420 // MultiplePackageError describes a directory containing 421 // multiple buildable Go source files for multiple packages. 422 type MultiplePackageError struct { 423 Dir string // directory containing files 424 Packages []string // package names found 425 Files []string // corresponding files: Files[i] declares package Packages[i] 426 } 427 428 func (e *MultiplePackageError) Error() string { 429 // Error string limited to two entries for compatibility. 430 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) 431 } 432 433 func nameExt(name string) string { 434 i := strings.LastIndex(name, ".") 435 if i < 0 { 436 return "" 437 } 438 return name[i:] 439 } 440 441 // Import returns details about the Go package named by the import path, 442 // interpreting local import paths relative to the srcDir directory. 443 // If the path is a local import path naming a package that can be imported 444 // using a standard import path, the returned package will set p.ImportPath 445 // to that path. 446 // 447 // In the directory containing the package, .go, .c, .h, and .s files are 448 // considered part of the package except for: 449 // 450 // - .go files in package documentation 451 // - files starting with _ or . (likely editor temporary files) 452 // - files with build constraints not satisfied by the context 453 // 454 // If an error occurs, Import returns a non-nil error and a non-nil 455 // *Package containing partial information. 456 // 457 func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) { 458 p := &Package{ 459 ImportPath: path, 460 } 461 if path == "" { 462 return p, fmt.Errorf("import %q: invalid import path", path) 463 } 464 465 var pkgtargetroot string 466 var pkga string 467 var pkgerr error 468 suffix := "" 469 if ctxt.InstallSuffix != "" { 470 suffix = "_" + ctxt.InstallSuffix 471 } 472 switch ctxt.Compiler { 473 case "gccgo": 474 pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix 475 case "gc": 476 pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix 477 default: 478 // Save error for end of function. 479 pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler) 480 } 481 setPkga := func() { 482 switch ctxt.Compiler { 483 case "gccgo": 484 dir, elem := pathpkg.Split(p.ImportPath) 485 pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a" 486 case "gc": 487 pkga = pkgtargetroot + "/" + p.ImportPath + ".a" 488 } 489 } 490 setPkga() 491 492 binaryOnly := false 493 if IsLocalImport(path) { 494 pkga = "" // local imports have no installed path 495 if srcDir == "" { 496 return p, fmt.Errorf("import %q: import relative to unknown directory", path) 497 } 498 if !ctxt.isAbsPath(path) { 499 p.Dir = ctxt.joinPath(srcDir, path) 500 } 501 // Determine canonical import path, if any. 502 // Exclude results where the import path would include /testdata/. 503 inTestdata := func(sub string) bool { 504 return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || strings.HasPrefix(sub, "testdata/") || sub == "testdata" 505 } 506 if ctxt.GOROOT != "" { 507 root := ctxt.joinPath(ctxt.GOROOT, "src") 508 if sub, ok := ctxt.hasSubdir(root, p.Dir); ok && !inTestdata(sub) { 509 p.Goroot = true 510 p.ImportPath = sub 511 p.Root = ctxt.GOROOT 512 goto Found 513 } 514 } 515 all := ctxt.gopath() 516 for i, root := range all { 517 rootsrc := ctxt.joinPath(root, "src") 518 if sub, ok := ctxt.hasSubdir(rootsrc, p.Dir); ok && !inTestdata(sub) { 519 // We found a potential import path for dir, 520 // but check that using it wouldn't find something 521 // else first. 522 if ctxt.GOROOT != "" { 523 if dir := ctxt.joinPath(ctxt.GOROOT, "src", sub); ctxt.isDir(dir) { 524 p.ConflictDir = dir 525 goto Found 526 } 527 } 528 for _, earlyRoot := range all[:i] { 529 if dir := ctxt.joinPath(earlyRoot, "src", sub); ctxt.isDir(dir) { 530 p.ConflictDir = dir 531 goto Found 532 } 533 } 534 535 // sub would not name some other directory instead of this one. 536 // Record it. 537 p.ImportPath = sub 538 p.Root = root 539 goto Found 540 } 541 } 542 // It's okay that we didn't find a root containing dir. 543 // Keep going with the information we have. 544 } else { 545 if strings.HasPrefix(path, "/") { 546 return p, fmt.Errorf("import %q: cannot import absolute path", path) 547 } 548 549 // tried records the location of unsuccessful package lookups 550 var tried struct { 551 vendor []string 552 goroot string 553 gopath []string 554 } 555 gopath := ctxt.gopath() 556 557 // Vendor directories get first chance to satisfy import. 558 if mode&IgnoreVendor == 0 && srcDir != "" { 559 searchVendor := func(root string, isGoroot bool) bool { 560 sub, ok := ctxt.hasSubdir(root, srcDir) 561 if !ok || !strings.HasPrefix(sub, "src/") || strings.Contains(sub, "/testdata/") { 562 return false 563 } 564 for { 565 vendor := ctxt.joinPath(root, sub, "vendor") 566 if ctxt.isDir(vendor) { 567 dir := ctxt.joinPath(vendor, path) 568 if ctxt.isDir(dir) && hasGoFiles(ctxt, dir) { 569 p.Dir = dir 570 p.ImportPath = strings.TrimPrefix(pathpkg.Join(sub, "vendor", path), "src/") 571 p.Goroot = isGoroot 572 p.Root = root 573 setPkga() // p.ImportPath changed 574 return true 575 } 576 tried.vendor = append(tried.vendor, dir) 577 } 578 i := strings.LastIndex(sub, "/") 579 if i < 0 { 580 break 581 } 582 sub = sub[:i] 583 } 584 return false 585 } 586 if searchVendor(ctxt.GOROOT, true) { 587 goto Found 588 } 589 for _, root := range gopath { 590 if searchVendor(root, false) { 591 goto Found 592 } 593 } 594 } 595 596 // Determine directory from import path. 597 if ctxt.GOROOT != "" { 598 dir := ctxt.joinPath(ctxt.GOROOT, "src", path) 599 isDir := ctxt.isDir(dir) 600 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga)) 601 if isDir || binaryOnly { 602 p.Dir = dir 603 p.Goroot = true 604 p.Root = ctxt.GOROOT 605 goto Found 606 } 607 tried.goroot = dir 608 } 609 for _, root := range gopath { 610 dir := ctxt.joinPath(root, "src", path) 611 isDir := ctxt.isDir(dir) 612 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(root, pkga)) 613 if isDir || binaryOnly { 614 p.Dir = dir 615 p.Root = root 616 goto Found 617 } 618 tried.gopath = append(tried.gopath, dir) 619 } 620 621 // package was not found 622 var paths []string 623 format := "\t%s (vendor tree)" 624 for _, dir := range tried.vendor { 625 paths = append(paths, fmt.Sprintf(format, dir)) 626 format = "\t%s" 627 } 628 if tried.goroot != "" { 629 paths = append(paths, fmt.Sprintf("\t%s (from $GOROOT)", tried.goroot)) 630 } else { 631 paths = append(paths, "\t($GOROOT not set)") 632 } 633 format = "\t%s (from $GOPATH)" 634 for _, dir := range tried.gopath { 635 paths = append(paths, fmt.Sprintf(format, dir)) 636 format = "\t%s" 637 } 638 if len(tried.gopath) == 0 { 639 paths = append(paths, "\t($GOPATH not set)") 640 } 641 return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, strings.Join(paths, "\n")) 642 } 643 644 Found: 645 if p.Root != "" { 646 p.SrcRoot = ctxt.joinPath(p.Root, "src") 647 p.PkgRoot = ctxt.joinPath(p.Root, "pkg") 648 p.BinDir = ctxt.joinPath(p.Root, "bin") 649 if pkga != "" { 650 p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot) 651 p.PkgObj = ctxt.joinPath(p.Root, pkga) 652 } 653 } 654 655 if mode&FindOnly != 0 { 656 return p, pkgerr 657 } 658 if binaryOnly && (mode&AllowBinary) != 0 { 659 return p, pkgerr 660 } 661 662 dirs, err := ctxt.readDir(p.Dir) 663 if err != nil { 664 return p, err 665 } 666 667 var badGoError error 668 var Sfiles []string // files with ".S" (capital S) 669 var firstFile, firstCommentFile string 670 imported := make(map[string][]token.Position) 671 testImported := make(map[string][]token.Position) 672 xTestImported := make(map[string][]token.Position) 673 allTags := make(map[string]bool) 674 fset := token.NewFileSet() 675 for _, d := range dirs { 676 if d.IsDir() { 677 continue 678 } 679 680 name := d.Name() 681 ext := nameExt(name) 682 683 badFile := func(err error) { 684 if badGoError == nil { 685 badGoError = err 686 } 687 p.InvalidGoFiles = append(p.InvalidGoFiles, name) 688 } 689 690 match, data, filename, err := ctxt.matchFile(p.Dir, name, true, allTags, &p.BinaryOnly) 691 if err != nil { 692 badFile(err) 693 continue 694 } 695 if !match { 696 if ext == ".go" { 697 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) 698 } 699 continue 700 } 701 702 // Going to save the file. For non-Go files, can stop here. 703 switch ext { 704 case ".c": 705 p.CFiles = append(p.CFiles, name) 706 continue 707 case ".cc", ".cpp", ".cxx": 708 p.CXXFiles = append(p.CXXFiles, name) 709 continue 710 case ".m": 711 p.MFiles = append(p.MFiles, name) 712 continue 713 case ".h", ".hh", ".hpp", ".hxx": 714 p.HFiles = append(p.HFiles, name) 715 continue 716 case ".f", ".F", ".for", ".f90": 717 p.FFiles = append(p.FFiles, name) 718 continue 719 case ".s": 720 p.SFiles = append(p.SFiles, name) 721 continue 722 case ".S": 723 Sfiles = append(Sfiles, name) 724 continue 725 case ".swig": 726 p.SwigFiles = append(p.SwigFiles, name) 727 continue 728 case ".swigcxx": 729 p.SwigCXXFiles = append(p.SwigCXXFiles, name) 730 continue 731 case ".syso": 732 // binary objects to add to package archive 733 // Likely of the form foo_windows.syso, but 734 // the name was vetted above with goodOSArchFile. 735 p.SysoFiles = append(p.SysoFiles, name) 736 continue 737 } 738 739 pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments) 740 if err != nil { 741 badFile(err) 742 continue 743 } 744 745 pkg := pf.Name.Name 746 if pkg == "documentation" { 747 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) 748 continue 749 } 750 751 isTest := strings.HasSuffix(name, "_test.go") 752 isXTest := false 753 if isTest && strings.HasSuffix(pkg, "_test") { 754 isXTest = true 755 pkg = pkg[:len(pkg)-len("_test")] 756 } 757 758 if p.Name == "" { 759 p.Name = pkg 760 firstFile = name 761 } else if pkg != p.Name { 762 badFile(&MultiplePackageError{ 763 Dir: p.Dir, 764 Packages: []string{p.Name, pkg}, 765 Files: []string{firstFile, name}, 766 }) 767 p.InvalidGoFiles = append(p.InvalidGoFiles, name) 768 } 769 if pf.Doc != nil && p.Doc == "" { 770 p.Doc = doc.Synopsis(pf.Doc.Text()) 771 } 772 773 if mode&ImportComment != 0 { 774 qcom, line := findImportComment(data) 775 if line != 0 { 776 com, err := strconv.Unquote(qcom) 777 if err != nil { 778 badFile(fmt.Errorf("%s:%d: cannot parse import comment", filename, line)) 779 } else if p.ImportComment == "" { 780 p.ImportComment = com 781 firstCommentFile = name 782 } else if p.ImportComment != com { 783 badFile(fmt.Errorf("found import comments %q (%s) and %q (%s) in %s", p.ImportComment, firstCommentFile, com, name, p.Dir)) 784 } 785 } 786 } 787 788 // Record imports and information about cgo. 789 isCgo := false 790 for _, decl := range pf.Decls { 791 d, ok := decl.(*ast.GenDecl) 792 if !ok { 793 continue 794 } 795 for _, dspec := range d.Specs { 796 spec, ok := dspec.(*ast.ImportSpec) 797 if !ok { 798 continue 799 } 800 quoted := spec.Path.Value 801 path, err := strconv.Unquote(quoted) 802 if err != nil { 803 log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted) 804 } 805 if isXTest { 806 xTestImported[path] = append(xTestImported[path], fset.Position(spec.Pos())) 807 } else if isTest { 808 testImported[path] = append(testImported[path], fset.Position(spec.Pos())) 809 } else { 810 imported[path] = append(imported[path], fset.Position(spec.Pos())) 811 } 812 if path == "C" { 813 if isTest { 814 badFile(fmt.Errorf("use of cgo in test %s not supported", filename)) 815 } else { 816 cg := spec.Doc 817 if cg == nil && len(d.Specs) == 1 { 818 cg = d.Doc 819 } 820 if cg != nil { 821 if err := ctxt.saveCgo(filename, p, cg); err != nil { 822 badFile(err) 823 } 824 } 825 isCgo = true 826 } 827 } 828 } 829 } 830 if isCgo { 831 allTags["cgo"] = true 832 if ctxt.CgoEnabled { 833 p.CgoFiles = append(p.CgoFiles, name) 834 } else { 835 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) 836 } 837 } else if isXTest { 838 p.XTestGoFiles = append(p.XTestGoFiles, name) 839 } else if isTest { 840 p.TestGoFiles = append(p.TestGoFiles, name) 841 } else { 842 p.GoFiles = append(p.GoFiles, name) 843 } 844 } 845 if badGoError != nil { 846 return p, badGoError 847 } 848 if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 { 849 return p, &NoGoError{p.Dir} 850 } 851 852 for tag := range allTags { 853 p.AllTags = append(p.AllTags, tag) 854 } 855 sort.Strings(p.AllTags) 856 857 p.Imports, p.ImportPos = cleanImports(imported) 858 p.TestImports, p.TestImportPos = cleanImports(testImported) 859 p.XTestImports, p.XTestImportPos = cleanImports(xTestImported) 860 861 // add the .S files only if we are using cgo 862 // (which means gcc will compile them). 863 // The standard assemblers expect .s files. 864 if len(p.CgoFiles) > 0 { 865 p.SFiles = append(p.SFiles, Sfiles...) 866 sort.Strings(p.SFiles) 867 } 868 869 return p, pkgerr 870 } 871 872 // hasGoFiles reports whether dir contains any files with names ending in .go. 873 // For a vendor check we must exclude directories that contain no .go files. 874 // Otherwise it is not possible to vendor just a/b/c and still import the 875 // non-vendored a/b. See golang.org/issue/13832. 876 func hasGoFiles(ctxt *Context, dir string) bool { 877 ents, _ := ctxt.readDir(dir) 878 for _, ent := range ents { 879 if !ent.IsDir() && strings.HasSuffix(ent.Name(), ".go") { 880 return true 881 } 882 } 883 return false 884 } 885 886 func findImportComment(data []byte) (s string, line int) { 887 // expect keyword package 888 word, data := parseWord(data) 889 if string(word) != "package" { 890 return "", 0 891 } 892 893 // expect package name 894 _, data = parseWord(data) 895 896 // now ready for import comment, a // or /* */ comment 897 // beginning and ending on the current line. 898 for len(data) > 0 && (data[0] == ' ' || data[0] == '\t' || data[0] == '\r') { 899 data = data[1:] 900 } 901 902 var comment []byte 903 switch { 904 case bytes.HasPrefix(data, slashSlash): 905 i := bytes.Index(data, newline) 906 if i < 0 { 907 i = len(data) 908 } 909 comment = data[2:i] 910 case bytes.HasPrefix(data, slashStar): 911 data = data[2:] 912 i := bytes.Index(data, starSlash) 913 if i < 0 { 914 // malformed comment 915 return "", 0 916 } 917 comment = data[:i] 918 if bytes.Contains(comment, newline) { 919 return "", 0 920 } 921 } 922 comment = bytes.TrimSpace(comment) 923 924 // split comment into `import`, `"pkg"` 925 word, arg := parseWord(comment) 926 if string(word) != "import" { 927 return "", 0 928 } 929 930 line = 1 + bytes.Count(data[:cap(data)-cap(arg)], newline) 931 return strings.TrimSpace(string(arg)), line 932 } 933 934 var ( 935 slashSlash = []byte("//") 936 slashStar = []byte("/*") 937 starSlash = []byte("*/") 938 newline = []byte("\n") 939 ) 940 941 // skipSpaceOrComment returns data with any leading spaces or comments removed. 942 func skipSpaceOrComment(data []byte) []byte { 943 for len(data) > 0 { 944 switch data[0] { 945 case ' ', '\t', '\r', '\n': 946 data = data[1:] 947 continue 948 case '/': 949 if bytes.HasPrefix(data, slashSlash) { 950 i := bytes.Index(data, newline) 951 if i < 0 { 952 return nil 953 } 954 data = data[i+1:] 955 continue 956 } 957 if bytes.HasPrefix(data, slashStar) { 958 data = data[2:] 959 i := bytes.Index(data, starSlash) 960 if i < 0 { 961 return nil 962 } 963 data = data[i+2:] 964 continue 965 } 966 } 967 break 968 } 969 return data 970 } 971 972 // parseWord skips any leading spaces or comments in data 973 // and then parses the beginning of data as an identifier or keyword, 974 // returning that word and what remains after the word. 975 func parseWord(data []byte) (word, rest []byte) { 976 data = skipSpaceOrComment(data) 977 978 // Parse past leading word characters. 979 rest = data 980 for { 981 r, size := utf8.DecodeRune(rest) 982 if unicode.IsLetter(r) || '0' <= r && r <= '9' || r == '_' { 983 rest = rest[size:] 984 continue 985 } 986 break 987 } 988 989 word = data[:len(data)-len(rest)] 990 if len(word) == 0 { 991 return nil, nil 992 } 993 994 return word, rest 995 } 996 997 // MatchFile reports whether the file with the given name in the given directory 998 // matches the context and would be included in a Package created by ImportDir 999 // of that directory. 1000 // 1001 // MatchFile considers the name of the file and may use ctxt.OpenFile to 1002 // read some or all of the file's content. 1003 func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) { 1004 match, _, _, err = ctxt.matchFile(dir, name, false, nil, nil) 1005 return 1006 } 1007 1008 // matchFile determines whether the file with the given name in the given directory 1009 // should be included in the package being constructed. 1010 // It returns the data read from the file. 1011 // If returnImports is true and name denotes a Go program, matchFile reads 1012 // until the end of the imports (and returns that data) even though it only 1013 // considers text until the first non-comment. 1014 // If allTags is non-nil, matchFile records any encountered build tag 1015 // by setting allTags[tag] = true. 1016 func (ctxt *Context) matchFile(dir, name string, returnImports bool, allTags map[string]bool, binaryOnly *bool) (match bool, data []byte, filename string, err error) { 1017 if strings.HasPrefix(name, "_") || 1018 strings.HasPrefix(name, ".") { 1019 return 1020 } 1021 1022 i := strings.LastIndex(name, ".") 1023 if i < 0 { 1024 i = len(name) 1025 } 1026 ext := name[i:] 1027 1028 if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles { 1029 return 1030 } 1031 1032 switch ext { 1033 case ".go", ".c", ".cc", ".cxx", ".cpp", ".m", ".s", ".h", ".hh", ".hpp", ".hxx", ".f", ".F", ".f90", ".S", ".swig", ".swigcxx": 1034 // tentatively okay - read to make sure 1035 case ".syso": 1036 // binary, no reading 1037 match = true 1038 return 1039 default: 1040 // skip 1041 return 1042 } 1043 1044 filename = ctxt.joinPath(dir, name) 1045 f, err := ctxt.openFile(filename) 1046 if err != nil { 1047 return 1048 } 1049 1050 if strings.HasSuffix(filename, ".go") { 1051 data, err = readImports(f, false, nil) 1052 if strings.HasSuffix(filename, "_test.go") { 1053 binaryOnly = nil // ignore //go:binary-only-package comments in _test.go files 1054 } 1055 } else { 1056 binaryOnly = nil // ignore //go:binary-only-package comments in non-Go sources 1057 data, err = readComments(f) 1058 } 1059 f.Close() 1060 if err != nil { 1061 err = fmt.Errorf("read %s: %v", filename, err) 1062 return 1063 } 1064 1065 // Look for +build comments to accept or reject the file. 1066 if !ctxt.shouldBuild(data, allTags, binaryOnly) && !ctxt.UseAllFiles { 1067 return 1068 } 1069 1070 match = true 1071 return 1072 } 1073 1074 func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) { 1075 all := make([]string, 0, len(m)) 1076 for path := range m { 1077 all = append(all, path) 1078 } 1079 sort.Strings(all) 1080 return all, m 1081 } 1082 1083 // Import is shorthand for Default.Import. 1084 func Import(path, srcDir string, mode ImportMode) (*Package, error) { 1085 return Default.Import(path, srcDir, mode) 1086 } 1087 1088 // ImportDir is shorthand for Default.ImportDir. 1089 func ImportDir(dir string, mode ImportMode) (*Package, error) { 1090 return Default.ImportDir(dir, mode) 1091 } 1092 1093 var slashslash = []byte("//") 1094 1095 // Special comment denoting a binary-only package. 1096 // See https://golang.org/design/2775-binary-only-packages 1097 // for more about the design of binary-only packages. 1098 var binaryOnlyComment = []byte("//go:binary-only-package") 1099 1100 // shouldBuild reports whether it is okay to use this file, 1101 // The rule is that in the file's leading run of // comments 1102 // and blank lines, which must be followed by a blank line 1103 // (to avoid including a Go package clause doc comment), 1104 // lines beginning with '// +build' are taken as build directives. 1105 // 1106 // The file is accepted only if each such line lists something 1107 // matching the file. For example: 1108 // 1109 // // +build windows linux 1110 // 1111 // marks the file as applicable only on Windows and Linux. 1112 // 1113 // If shouldBuild finds a //go:binary-only-package comment in a file that 1114 // should be built, it sets *binaryOnly to true. Otherwise it does 1115 // not change *binaryOnly. 1116 // 1117 func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool, binaryOnly *bool) bool { 1118 sawBinaryOnly := false 1119 1120 // Pass 1. Identify leading run of // comments and blank lines, 1121 // which must be followed by a blank line. 1122 end := 0 1123 p := content 1124 for len(p) > 0 { 1125 line := p 1126 if i := bytes.IndexByte(line, '\n'); i >= 0 { 1127 line, p = line[:i], p[i+1:] 1128 } else { 1129 p = p[len(p):] 1130 } 1131 line = bytes.TrimSpace(line) 1132 if len(line) == 0 { // Blank line 1133 end = len(content) - len(p) 1134 continue 1135 } 1136 if !bytes.HasPrefix(line, slashslash) { // Not comment line 1137 break 1138 } 1139 } 1140 content = content[:end] 1141 1142 // Pass 2. Process each line in the run. 1143 p = content 1144 allok := true 1145 for len(p) > 0 { 1146 line := p 1147 if i := bytes.IndexByte(line, '\n'); i >= 0 { 1148 line, p = line[:i], p[i+1:] 1149 } else { 1150 p = p[len(p):] 1151 } 1152 line = bytes.TrimSpace(line) 1153 if bytes.HasPrefix(line, slashslash) { 1154 if bytes.Equal(line, binaryOnlyComment) { 1155 sawBinaryOnly = true 1156 } 1157 line = bytes.TrimSpace(line[len(slashslash):]) 1158 if len(line) > 0 && line[0] == '+' { 1159 // Looks like a comment +line. 1160 f := strings.Fields(string(line)) 1161 if f[0] == "+build" { 1162 ok := false 1163 for _, tok := range f[1:] { 1164 if ctxt.match(tok, allTags) { 1165 ok = true 1166 } 1167 } 1168 if !ok { 1169 allok = false 1170 } 1171 } 1172 } 1173 } 1174 } 1175 1176 if binaryOnly != nil && sawBinaryOnly { 1177 *binaryOnly = true 1178 } 1179 1180 return allok 1181 } 1182 1183 // saveCgo saves the information from the #cgo lines in the import "C" comment. 1184 // These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives 1185 // that affect the way cgo's C code is built. 1186 func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error { 1187 text := cg.Text() 1188 for _, line := range strings.Split(text, "\n") { 1189 orig := line 1190 1191 // Line is 1192 // #cgo [GOOS/GOARCH...] LDFLAGS: stuff 1193 // 1194 line = strings.TrimSpace(line) 1195 if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') { 1196 continue 1197 } 1198 1199 // Split at colon. 1200 line = strings.TrimSpace(line[4:]) 1201 i := strings.Index(line, ":") 1202 if i < 0 { 1203 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) 1204 } 1205 line, argstr := line[:i], line[i+1:] 1206 1207 // Parse GOOS/GOARCH stuff. 1208 f := strings.Fields(line) 1209 if len(f) < 1 { 1210 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) 1211 } 1212 1213 cond, verb := f[:len(f)-1], f[len(f)-1] 1214 if len(cond) > 0 { 1215 ok := false 1216 for _, c := range cond { 1217 if ctxt.match(c, nil) { 1218 ok = true 1219 break 1220 } 1221 } 1222 if !ok { 1223 continue 1224 } 1225 } 1226 1227 args, err := splitQuoted(argstr) 1228 if err != nil { 1229 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) 1230 } 1231 var ok bool 1232 for i, arg := range args { 1233 if arg, ok = expandSrcDir(arg, di.Dir); !ok { 1234 return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg) 1235 } 1236 args[i] = arg 1237 } 1238 1239 switch verb { 1240 case "CFLAGS": 1241 di.CgoCFLAGS = append(di.CgoCFLAGS, args...) 1242 case "CPPFLAGS": 1243 di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...) 1244 case "CXXFLAGS": 1245 di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...) 1246 case "FFLAGS": 1247 di.CgoFFLAGS = append(di.CgoFFLAGS, args...) 1248 case "LDFLAGS": 1249 di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...) 1250 case "pkg-config": 1251 di.CgoPkgConfig = append(di.CgoPkgConfig, args...) 1252 default: 1253 return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig) 1254 } 1255 } 1256 return nil 1257 } 1258 1259 // expandSrcDir expands any occurrence of ${SRCDIR}, making sure 1260 // the result is safe for the shell. 1261 func expandSrcDir(str string, srcdir string) (string, bool) { 1262 // "\" delimited paths cause safeCgoName to fail 1263 // so convert native paths with a different delimiter 1264 // to "/" before starting (eg: on windows). 1265 srcdir = filepath.ToSlash(srcdir) 1266 1267 // Spaces are tolerated in ${SRCDIR}, but not anywhere else. 1268 chunks := strings.Split(str, "${SRCDIR}") 1269 if len(chunks) < 2 { 1270 return str, safeCgoName(str, false) 1271 } 1272 ok := true 1273 for _, chunk := range chunks { 1274 ok = ok && (chunk == "" || safeCgoName(chunk, false)) 1275 } 1276 ok = ok && (srcdir == "" || safeCgoName(srcdir, true)) 1277 res := strings.Join(chunks, srcdir) 1278 return res, ok && res != "" 1279 } 1280 1281 // NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN. 1282 // We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay. 1283 // See golang.org/issue/6038. 1284 // The @ is for OS X. See golang.org/issue/13720. 1285 const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@" 1286 const safeSpaces = " " 1287 1288 var safeBytes = []byte(safeSpaces + safeString) 1289 1290 func safeCgoName(s string, spaces bool) bool { 1291 if s == "" { 1292 return false 1293 } 1294 safe := safeBytes 1295 if !spaces { 1296 safe = safe[len(safeSpaces):] 1297 } 1298 for i := 0; i < len(s); i++ { 1299 if c := s[i]; c < utf8.RuneSelf && bytes.IndexByte(safe, c) < 0 { 1300 return false 1301 } 1302 } 1303 return true 1304 } 1305 1306 // splitQuoted splits the string s around each instance of one or more consecutive 1307 // white space characters while taking into account quotes and escaping, and 1308 // returns an array of substrings of s or an empty list if s contains only white space. 1309 // Single quotes and double quotes are recognized to prevent splitting within the 1310 // quoted region, and are removed from the resulting substrings. If a quote in s 1311 // isn't closed err will be set and r will have the unclosed argument as the 1312 // last element. The backslash is used for escaping. 1313 // 1314 // For example, the following string: 1315 // 1316 // a b:"c d" 'e''f' "g\"" 1317 // 1318 // Would be parsed as: 1319 // 1320 // []string{"a", "b:c d", "ef", `g"`} 1321 // 1322 func splitQuoted(s string) (r []string, err error) { 1323 var args []string 1324 arg := make([]rune, len(s)) 1325 escaped := false 1326 quoted := false 1327 quote := '\x00' 1328 i := 0 1329 for _, rune := range s { 1330 switch { 1331 case escaped: 1332 escaped = false 1333 case rune == '\\': 1334 escaped = true 1335 continue 1336 case quote != '\x00': 1337 if rune == quote { 1338 quote = '\x00' 1339 continue 1340 } 1341 case rune == '"' || rune == '\'': 1342 quoted = true 1343 quote = rune 1344 continue 1345 case unicode.IsSpace(rune): 1346 if quoted || i > 0 { 1347 quoted = false 1348 args = append(args, string(arg[:i])) 1349 i = 0 1350 } 1351 continue 1352 } 1353 arg[i] = rune 1354 i++ 1355 } 1356 if quoted || i > 0 { 1357 args = append(args, string(arg[:i])) 1358 } 1359 if quote != 0 { 1360 err = errors.New("unclosed quote") 1361 } else if escaped { 1362 err = errors.New("unfinished escaping") 1363 } 1364 return args, err 1365 } 1366 1367 // match reports whether the name is one of: 1368 // 1369 // $GOOS 1370 // $GOARCH 1371 // cgo (if cgo is enabled) 1372 // !cgo (if cgo is disabled) 1373 // ctxt.Compiler 1374 // !ctxt.Compiler 1375 // tag (if tag is listed in ctxt.BuildTags or ctxt.ReleaseTags) 1376 // !tag (if tag is not listed in ctxt.BuildTags or ctxt.ReleaseTags) 1377 // a comma-separated list of any of these 1378 // 1379 func (ctxt *Context) match(name string, allTags map[string]bool) bool { 1380 if name == "" { 1381 if allTags != nil { 1382 allTags[name] = true 1383 } 1384 return false 1385 } 1386 if i := strings.Index(name, ","); i >= 0 { 1387 // comma-separated list 1388 ok1 := ctxt.match(name[:i], allTags) 1389 ok2 := ctxt.match(name[i+1:], allTags) 1390 return ok1 && ok2 1391 } 1392 if strings.HasPrefix(name, "!!") { // bad syntax, reject always 1393 return false 1394 } 1395 if strings.HasPrefix(name, "!") { // negation 1396 return len(name) > 1 && !ctxt.match(name[1:], allTags) 1397 } 1398 1399 if allTags != nil { 1400 allTags[name] = true 1401 } 1402 1403 // Tags must be letters, digits, underscores or dots. 1404 // Unlike in Go identifiers, all digits are fine (e.g., "386"). 1405 for _, c := range name { 1406 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { 1407 return false 1408 } 1409 } 1410 1411 // special tags 1412 if ctxt.CgoEnabled && name == "cgo" { 1413 return true 1414 } 1415 if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler { 1416 return true 1417 } 1418 if ctxt.GOOS == "android" && name == "linux" { 1419 return true 1420 } 1421 1422 // other tags 1423 for _, tag := range ctxt.BuildTags { 1424 if tag == name { 1425 return true 1426 } 1427 } 1428 for _, tag := range ctxt.ReleaseTags { 1429 if tag == name { 1430 return true 1431 } 1432 } 1433 1434 return false 1435 } 1436 1437 // goodOSArchFile returns false if the name contains a $GOOS or $GOARCH 1438 // suffix which does not match the current system. 1439 // The recognized name formats are: 1440 // 1441 // name_$(GOOS).* 1442 // name_$(GOARCH).* 1443 // name_$(GOOS)_$(GOARCH).* 1444 // name_$(GOOS)_test.* 1445 // name_$(GOARCH)_test.* 1446 // name_$(GOOS)_$(GOARCH)_test.* 1447 // 1448 // An exception: if GOOS=android, then files with GOOS=linux are also matched. 1449 func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool { 1450 if dot := strings.Index(name, "."); dot != -1 { 1451 name = name[:dot] 1452 } 1453 1454 // Before Go 1.4, a file called "linux.go" would be equivalent to having a 1455 // build tag "linux" in that file. For Go 1.4 and beyond, we require this 1456 // auto-tagging to apply only to files with a non-empty prefix, so 1457 // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating 1458 // systems, such as android, to arrive without breaking existing code with 1459 // innocuous source code in "android.go". The easiest fix: cut everything 1460 // in the name before the initial _. 1461 i := strings.Index(name, "_") 1462 if i < 0 { 1463 return true 1464 } 1465 name = name[i:] // ignore everything before first _ 1466 1467 l := strings.Split(name, "_") 1468 if n := len(l); n > 0 && l[n-1] == "test" { 1469 l = l[:n-1] 1470 } 1471 n := len(l) 1472 if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] { 1473 if allTags != nil { 1474 allTags[l[n-2]] = true 1475 allTags[l[n-1]] = true 1476 } 1477 if l[n-1] != ctxt.GOARCH { 1478 return false 1479 } 1480 if ctxt.GOOS == "android" && l[n-2] == "linux" { 1481 return true 1482 } 1483 return l[n-2] == ctxt.GOOS 1484 } 1485 if n >= 1 && knownOS[l[n-1]] { 1486 if allTags != nil { 1487 allTags[l[n-1]] = true 1488 } 1489 if ctxt.GOOS == "android" && l[n-1] == "linux" { 1490 return true 1491 } 1492 return l[n-1] == ctxt.GOOS 1493 } 1494 if n >= 1 && knownArch[l[n-1]] { 1495 if allTags != nil { 1496 allTags[l[n-1]] = true 1497 } 1498 return l[n-1] == ctxt.GOARCH 1499 } 1500 return true 1501 } 1502 1503 var knownOS = make(map[string]bool) 1504 var knownArch = make(map[string]bool) 1505 1506 func init() { 1507 for _, v := range strings.Fields(goosList) { 1508 knownOS[v] = true 1509 } 1510 for _, v := range strings.Fields(goarchList) { 1511 knownArch[v] = true 1512 } 1513 } 1514 1515 // ToolDir is the directory containing build tools. 1516 var ToolDir = filepath.Join(runtime.GOROOT(), "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH) 1517 1518 // IsLocalImport reports whether the import path is 1519 // a local import path, like ".", "..", "./foo", or "../foo". 1520 func IsLocalImport(path string) bool { 1521 return path == "." || path == ".." || 1522 strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../") 1523 } 1524 1525 // ArchChar returns "?" and an error. 1526 // In earlier versions of Go, the returned string was used to derive 1527 // the compiler and linker tool names, the default object file suffix, 1528 // and the default linker output name. As of Go 1.5, those strings 1529 // no longer vary by architecture; they are compile, link, .o, and a.out, respectively. 1530 func ArchChar(goarch string) (string, error) { 1531 return "?", errors.New("architecture letter no longer used") 1532 }