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