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