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