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