github.com/shijuvar/go@v0.0.0-20141209052335-e8f13700b70c/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{p.Dir, []string{firstFile, name}, []string{p.Name, pkg}} 692 } 693 if pf.Doc != nil && p.Doc == "" { 694 p.Doc = doc.Synopsis(pf.Doc.Text()) 695 } 696 697 if mode&ImportComment != 0 { 698 qcom, line := findImportComment(data) 699 if line != 0 { 700 com, err := strconv.Unquote(qcom) 701 if err != nil { 702 return p, fmt.Errorf("%s:%d: cannot parse import comment", filename, line) 703 } 704 if p.ImportComment == "" { 705 p.ImportComment = com 706 firstCommentFile = name 707 } else if p.ImportComment != com { 708 return p, fmt.Errorf("found import comments %q (%s) and %q (%s) in %s", p.ImportComment, firstCommentFile, com, name, p.Dir) 709 } 710 } 711 } 712 713 // Record imports and information about cgo. 714 isCgo := false 715 for _, decl := range pf.Decls { 716 d, ok := decl.(*ast.GenDecl) 717 if !ok { 718 continue 719 } 720 for _, dspec := range d.Specs { 721 spec, ok := dspec.(*ast.ImportSpec) 722 if !ok { 723 continue 724 } 725 quoted := spec.Path.Value 726 path, err := strconv.Unquote(quoted) 727 if err != nil { 728 log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted) 729 } 730 if isXTest { 731 xTestImported[path] = append(xTestImported[path], fset.Position(spec.Pos())) 732 } else if isTest { 733 testImported[path] = append(testImported[path], fset.Position(spec.Pos())) 734 } else { 735 imported[path] = append(imported[path], fset.Position(spec.Pos())) 736 } 737 if path == "C" { 738 if isTest { 739 return p, fmt.Errorf("use of cgo in test %s not supported", filename) 740 } 741 cg := spec.Doc 742 if cg == nil && len(d.Specs) == 1 { 743 cg = d.Doc 744 } 745 if cg != nil { 746 if err := ctxt.saveCgo(filename, p, cg); err != nil { 747 return p, err 748 } 749 } 750 isCgo = true 751 } 752 } 753 } 754 if isCgo { 755 allTags["cgo"] = true 756 if ctxt.CgoEnabled { 757 p.CgoFiles = append(p.CgoFiles, name) 758 } else { 759 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) 760 } 761 } else if isXTest { 762 p.XTestGoFiles = append(p.XTestGoFiles, name) 763 } else if isTest { 764 p.TestGoFiles = append(p.TestGoFiles, name) 765 } else { 766 p.GoFiles = append(p.GoFiles, name) 767 } 768 } 769 if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 { 770 return p, &NoGoError{p.Dir} 771 } 772 773 for tag := range allTags { 774 p.AllTags = append(p.AllTags, tag) 775 } 776 sort.Strings(p.AllTags) 777 778 p.Imports, p.ImportPos = cleanImports(imported) 779 p.TestImports, p.TestImportPos = cleanImports(testImported) 780 p.XTestImports, p.XTestImportPos = cleanImports(xTestImported) 781 782 // add the .S files only if we are using cgo 783 // (which means gcc will compile them). 784 // The standard assemblers expect .s files. 785 if len(p.CgoFiles) > 0 { 786 p.SFiles = append(p.SFiles, Sfiles...) 787 sort.Strings(p.SFiles) 788 } 789 790 return p, pkgerr 791 } 792 793 func findImportComment(data []byte) (s string, line int) { 794 // expect keyword package 795 word, data := parseWord(data) 796 if string(word) != "package" { 797 return "", 0 798 } 799 800 // expect package name 801 _, data = parseWord(data) 802 803 // now ready for import comment, a // or /* */ comment 804 // beginning and ending on the current line. 805 for len(data) > 0 && (data[0] == ' ' || data[0] == '\t' || data[0] == '\r') { 806 data = data[1:] 807 } 808 809 var comment []byte 810 switch { 811 case bytes.HasPrefix(data, slashSlash): 812 i := bytes.Index(data, newline) 813 if i < 0 { 814 i = len(data) 815 } 816 comment = data[2:i] 817 case bytes.HasPrefix(data, slashStar): 818 data = data[2:] 819 i := bytes.Index(data, starSlash) 820 if i < 0 { 821 // malformed comment 822 return "", 0 823 } 824 comment = data[:i] 825 if bytes.Contains(comment, newline) { 826 return "", 0 827 } 828 } 829 comment = bytes.TrimSpace(comment) 830 831 // split comment into `import`, `"pkg"` 832 word, arg := parseWord(comment) 833 if string(word) != "import" { 834 return "", 0 835 } 836 837 line = 1 + bytes.Count(data[:cap(data)-cap(arg)], newline) 838 return strings.TrimSpace(string(arg)), line 839 } 840 841 var ( 842 slashSlash = []byte("//") 843 slashStar = []byte("/*") 844 starSlash = []byte("*/") 845 newline = []byte("\n") 846 ) 847 848 // skipSpaceOrComment returns data with any leading spaces or comments removed. 849 func skipSpaceOrComment(data []byte) []byte { 850 for len(data) > 0 { 851 switch data[0] { 852 case ' ', '\t', '\r', '\n': 853 data = data[1:] 854 continue 855 case '/': 856 if bytes.HasPrefix(data, slashSlash) { 857 i := bytes.Index(data, newline) 858 if i < 0 { 859 return nil 860 } 861 data = data[i+1:] 862 continue 863 } 864 if bytes.HasPrefix(data, slashStar) { 865 data = data[2:] 866 i := bytes.Index(data, starSlash) 867 if i < 0 { 868 return nil 869 } 870 data = data[i+2:] 871 continue 872 } 873 } 874 break 875 } 876 return data 877 } 878 879 // parseWord skips any leading spaces or comments in data 880 // and then parses the beginning of data as an identifier or keyword, 881 // returning that word and what remains after the word. 882 func parseWord(data []byte) (word, rest []byte) { 883 data = skipSpaceOrComment(data) 884 885 // Parse past leading word characters. 886 rest = data 887 for { 888 r, size := utf8.DecodeRune(rest) 889 if unicode.IsLetter(r) || '0' <= r && r <= '9' || r == '_' { 890 rest = rest[size:] 891 continue 892 } 893 break 894 } 895 896 word = data[:len(data)-len(rest)] 897 if len(word) == 0 { 898 return nil, nil 899 } 900 901 return word, rest 902 } 903 904 // MatchFile reports whether the file with the given name in the given directory 905 // matches the context and would be included in a Package created by ImportDir 906 // of that directory. 907 // 908 // MatchFile considers the name of the file and may use ctxt.OpenFile to 909 // read some or all of the file's content. 910 func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) { 911 match, _, _, err = ctxt.matchFile(dir, name, false, nil) 912 return 913 } 914 915 // matchFile determines whether the file with the given name in the given directory 916 // should be included in the package being constructed. 917 // It returns the data read from the file. 918 // If returnImports is true and name denotes a Go program, matchFile reads 919 // until the end of the imports (and returns that data) even though it only 920 // considers text until the first non-comment. 921 // If allTags is non-nil, matchFile records any encountered build tag 922 // by setting allTags[tag] = true. 923 func (ctxt *Context) matchFile(dir, name string, returnImports bool, allTags map[string]bool) (match bool, data []byte, filename string, err error) { 924 if strings.HasPrefix(name, "_") || 925 strings.HasPrefix(name, ".") { 926 return 927 } 928 929 i := strings.LastIndex(name, ".") 930 if i < 0 { 931 i = len(name) 932 } 933 ext := name[i:] 934 935 if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles { 936 return 937 } 938 939 switch ext { 940 case ".go", ".c", ".cc", ".cxx", ".cpp", ".m", ".s", ".h", ".hh", ".hpp", ".hxx", ".S", ".swig", ".swigcxx": 941 // tentatively okay - read to make sure 942 case ".syso": 943 // binary, no reading 944 match = true 945 return 946 default: 947 // skip 948 return 949 } 950 951 filename = ctxt.joinPath(dir, name) 952 f, err := ctxt.openFile(filename) 953 if err != nil { 954 return 955 } 956 957 if strings.HasSuffix(filename, ".go") { 958 data, err = readImports(f, false) 959 } else { 960 data, err = readComments(f) 961 } 962 f.Close() 963 if err != nil { 964 err = fmt.Errorf("read %s: %v", filename, err) 965 return 966 } 967 968 // Look for +build comments to accept or reject the file. 969 if !ctxt.shouldBuild(data, allTags) && !ctxt.UseAllFiles { 970 return 971 } 972 973 match = true 974 return 975 } 976 977 func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) { 978 all := make([]string, 0, len(m)) 979 for path := range m { 980 all = append(all, path) 981 } 982 sort.Strings(all) 983 return all, m 984 } 985 986 // Import is shorthand for Default.Import. 987 func Import(path, srcDir string, mode ImportMode) (*Package, error) { 988 return Default.Import(path, srcDir, mode) 989 } 990 991 // ImportDir is shorthand for Default.ImportDir. 992 func ImportDir(dir string, mode ImportMode) (*Package, error) { 993 return Default.ImportDir(dir, mode) 994 } 995 996 var slashslash = []byte("//") 997 998 // shouldBuild reports whether it is okay to use this file, 999 // The rule is that in the file's leading run of // comments 1000 // and blank lines, which must be followed by a blank line 1001 // (to avoid including a Go package clause doc comment), 1002 // lines beginning with '// +build' are taken as build directives. 1003 // 1004 // The file is accepted only if each such line lists something 1005 // matching the file. For example: 1006 // 1007 // // +build windows linux 1008 // 1009 // marks the file as applicable only on Windows and Linux. 1010 // 1011 func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool) bool { 1012 // Pass 1. Identify leading run of // comments and blank lines, 1013 // which must be followed by a blank line. 1014 end := 0 1015 p := content 1016 for len(p) > 0 { 1017 line := p 1018 if i := bytes.IndexByte(line, '\n'); i >= 0 { 1019 line, p = line[:i], p[i+1:] 1020 } else { 1021 p = p[len(p):] 1022 } 1023 line = bytes.TrimSpace(line) 1024 if len(line) == 0 { // Blank line 1025 end = len(content) - len(p) 1026 continue 1027 } 1028 if !bytes.HasPrefix(line, slashslash) { // Not comment line 1029 break 1030 } 1031 } 1032 content = content[:end] 1033 1034 // Pass 2. Process each line in the run. 1035 p = content 1036 allok := true 1037 for len(p) > 0 { 1038 line := p 1039 if i := bytes.IndexByte(line, '\n'); i >= 0 { 1040 line, p = line[:i], p[i+1:] 1041 } else { 1042 p = p[len(p):] 1043 } 1044 line = bytes.TrimSpace(line) 1045 if bytes.HasPrefix(line, slashslash) { 1046 line = bytes.TrimSpace(line[len(slashslash):]) 1047 if len(line) > 0 && line[0] == '+' { 1048 // Looks like a comment +line. 1049 f := strings.Fields(string(line)) 1050 if f[0] == "+build" { 1051 ok := false 1052 for _, tok := range f[1:] { 1053 if ctxt.match(tok, allTags) { 1054 ok = true 1055 } 1056 } 1057 if !ok { 1058 allok = false 1059 } 1060 } 1061 } 1062 } 1063 } 1064 1065 return allok 1066 } 1067 1068 // saveCgo saves the information from the #cgo lines in the import "C" comment. 1069 // These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives 1070 // that affect the way cgo's C code is built. 1071 // 1072 // TODO(rsc): This duplicates code in cgo. 1073 // Once the dust settles, remove this code from cgo. 1074 func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error { 1075 text := cg.Text() 1076 for _, line := range strings.Split(text, "\n") { 1077 orig := line 1078 1079 // Line is 1080 // #cgo [GOOS/GOARCH...] LDFLAGS: stuff 1081 // 1082 line = strings.TrimSpace(line) 1083 if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') { 1084 continue 1085 } 1086 1087 // Split at colon. 1088 line = strings.TrimSpace(line[4:]) 1089 i := strings.Index(line, ":") 1090 if i < 0 { 1091 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) 1092 } 1093 line, argstr := line[:i], line[i+1:] 1094 1095 // Parse GOOS/GOARCH stuff. 1096 f := strings.Fields(line) 1097 if len(f) < 1 { 1098 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) 1099 } 1100 1101 cond, verb := f[:len(f)-1], f[len(f)-1] 1102 if len(cond) > 0 { 1103 ok := false 1104 for _, c := range cond { 1105 if ctxt.match(c, nil) { 1106 ok = true 1107 break 1108 } 1109 } 1110 if !ok { 1111 continue 1112 } 1113 } 1114 1115 args, err := splitQuoted(argstr) 1116 if err != nil { 1117 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) 1118 } 1119 for _, arg := range args { 1120 if !safeCgoName(arg) { 1121 return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg) 1122 } 1123 } 1124 1125 switch verb { 1126 case "CFLAGS": 1127 di.CgoCFLAGS = append(di.CgoCFLAGS, args...) 1128 case "CPPFLAGS": 1129 di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...) 1130 case "CXXFLAGS": 1131 di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...) 1132 case "LDFLAGS": 1133 di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...) 1134 case "pkg-config": 1135 di.CgoPkgConfig = append(di.CgoPkgConfig, args...) 1136 default: 1137 return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig) 1138 } 1139 } 1140 return nil 1141 } 1142 1143 // NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN. 1144 // We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay. 1145 // See golang.org/issue/6038. 1146 var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$") 1147 1148 func safeCgoName(s string) bool { 1149 if s == "" { 1150 return false 1151 } 1152 for i := 0; i < len(s); i++ { 1153 if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 { 1154 return false 1155 } 1156 } 1157 return true 1158 } 1159 1160 // splitQuoted splits the string s around each instance of one or more consecutive 1161 // white space characters while taking into account quotes and escaping, and 1162 // returns an array of substrings of s or an empty list if s contains only white space. 1163 // Single quotes and double quotes are recognized to prevent splitting within the 1164 // quoted region, and are removed from the resulting substrings. If a quote in s 1165 // isn't closed err will be set and r will have the unclosed argument as the 1166 // last element. The backslash is used for escaping. 1167 // 1168 // For example, the following string: 1169 // 1170 // a b:"c d" 'e''f' "g\"" 1171 // 1172 // Would be parsed as: 1173 // 1174 // []string{"a", "b:c d", "ef", `g"`} 1175 // 1176 func splitQuoted(s string) (r []string, err error) { 1177 var args []string 1178 arg := make([]rune, len(s)) 1179 escaped := false 1180 quoted := false 1181 quote := '\x00' 1182 i := 0 1183 for _, rune := range s { 1184 switch { 1185 case escaped: 1186 escaped = false 1187 case rune == '\\': 1188 escaped = true 1189 continue 1190 case quote != '\x00': 1191 if rune == quote { 1192 quote = '\x00' 1193 continue 1194 } 1195 case rune == '"' || rune == '\'': 1196 quoted = true 1197 quote = rune 1198 continue 1199 case unicode.IsSpace(rune): 1200 if quoted || i > 0 { 1201 quoted = false 1202 args = append(args, string(arg[:i])) 1203 i = 0 1204 } 1205 continue 1206 } 1207 arg[i] = rune 1208 i++ 1209 } 1210 if quoted || i > 0 { 1211 args = append(args, string(arg[:i])) 1212 } 1213 if quote != 0 { 1214 err = errors.New("unclosed quote") 1215 } else if escaped { 1216 err = errors.New("unfinished escaping") 1217 } 1218 return args, err 1219 } 1220 1221 // match returns true if the name is one of: 1222 // 1223 // $GOOS 1224 // $GOARCH 1225 // cgo (if cgo is enabled) 1226 // !cgo (if cgo is disabled) 1227 // ctxt.Compiler 1228 // !ctxt.Compiler 1229 // tag (if tag is listed in ctxt.BuildTags or ctxt.ReleaseTags) 1230 // !tag (if tag is not listed in ctxt.BuildTags or ctxt.ReleaseTags) 1231 // a comma-separated list of any of these 1232 // 1233 func (ctxt *Context) match(name string, allTags map[string]bool) bool { 1234 if name == "" { 1235 if allTags != nil { 1236 allTags[name] = true 1237 } 1238 return false 1239 } 1240 if i := strings.Index(name, ","); i >= 0 { 1241 // comma-separated list 1242 ok1 := ctxt.match(name[:i], allTags) 1243 ok2 := ctxt.match(name[i+1:], allTags) 1244 return ok1 && ok2 1245 } 1246 if strings.HasPrefix(name, "!!") { // bad syntax, reject always 1247 return false 1248 } 1249 if strings.HasPrefix(name, "!") { // negation 1250 return len(name) > 1 && !ctxt.match(name[1:], allTags) 1251 } 1252 1253 if allTags != nil { 1254 allTags[name] = true 1255 } 1256 1257 // Tags must be letters, digits, underscores or dots. 1258 // Unlike in Go identifiers, all digits are fine (e.g., "386"). 1259 for _, c := range name { 1260 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { 1261 return false 1262 } 1263 } 1264 1265 // special tags 1266 if ctxt.CgoEnabled && name == "cgo" { 1267 return true 1268 } 1269 if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler { 1270 return true 1271 } 1272 if ctxt.GOOS == "android" && name == "linux" { 1273 return true 1274 } 1275 1276 // other tags 1277 for _, tag := range ctxt.BuildTags { 1278 if tag == name { 1279 return true 1280 } 1281 } 1282 for _, tag := range ctxt.ReleaseTags { 1283 if tag == name { 1284 return true 1285 } 1286 } 1287 1288 return false 1289 } 1290 1291 // goodOSArchFile returns false if the name contains a $GOOS or $GOARCH 1292 // suffix which does not match the current system. 1293 // The recognized name formats are: 1294 // 1295 // name_$(GOOS).* 1296 // name_$(GOARCH).* 1297 // name_$(GOOS)_$(GOARCH).* 1298 // name_$(GOOS)_test.* 1299 // name_$(GOARCH)_test.* 1300 // name_$(GOOS)_$(GOARCH)_test.* 1301 // 1302 // An exception: if GOOS=android, then files with GOOS=linux are also matched. 1303 func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool { 1304 if dot := strings.Index(name, "."); dot != -1 { 1305 name = name[:dot] 1306 } 1307 1308 // Before Go 1.4, a file called "linux.go" would be equivalent to having a 1309 // build tag "linux" in that file. For Go 1.4 and beyond, we require this 1310 // auto-tagging to apply only to files with a non-empty prefix, so 1311 // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating 1312 // sytems, such as android, to arrive without breaking existing code with 1313 // innocuous source code in "android.go". The easiest fix: cut everything 1314 // in the name before the initial _. 1315 i := strings.Index(name, "_") 1316 if i < 0 { 1317 return true 1318 } 1319 name = name[i:] // ignore everything before first _ 1320 1321 l := strings.Split(name, "_") 1322 if n := len(l); n > 0 && l[n-1] == "test" { 1323 l = l[:n-1] 1324 } 1325 n := len(l) 1326 if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] { 1327 if allTags != nil { 1328 allTags[l[n-2]] = true 1329 allTags[l[n-1]] = true 1330 } 1331 if l[n-1] != ctxt.GOARCH { 1332 return false 1333 } 1334 if ctxt.GOOS == "android" && l[n-2] == "linux" { 1335 return true 1336 } 1337 return l[n-2] == ctxt.GOOS 1338 } 1339 if n >= 1 && knownOS[l[n-1]] { 1340 if allTags != nil { 1341 allTags[l[n-1]] = true 1342 } 1343 if ctxt.GOOS == "android" && l[n-1] == "linux" { 1344 return true 1345 } 1346 return l[n-1] == ctxt.GOOS 1347 } 1348 if n >= 1 && knownArch[l[n-1]] { 1349 if allTags != nil { 1350 allTags[l[n-1]] = true 1351 } 1352 return l[n-1] == ctxt.GOARCH 1353 } 1354 return true 1355 } 1356 1357 var knownOS = make(map[string]bool) 1358 var knownArch = make(map[string]bool) 1359 1360 func init() { 1361 for _, v := range strings.Fields(goosList) { 1362 knownOS[v] = true 1363 } 1364 for _, v := range strings.Fields(goarchList) { 1365 knownArch[v] = true 1366 } 1367 } 1368 1369 // ToolDir is the directory containing build tools. 1370 var ToolDir = filepath.Join(runtime.GOROOT(), "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH) 1371 1372 // IsLocalImport reports whether the import path is 1373 // a local import path, like ".", "..", "./foo", or "../foo". 1374 func IsLocalImport(path string) bool { 1375 return path == "." || path == ".." || 1376 strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../") 1377 } 1378 1379 // ArchChar returns the architecture character for the given goarch. 1380 // For example, ArchChar("amd64") returns "6". 1381 func ArchChar(goarch string) (string, error) { 1382 switch goarch { 1383 case "386": 1384 return "8", nil 1385 case "amd64", "amd64p32": 1386 return "6", nil 1387 case "arm": 1388 return "5", nil 1389 case "ppc64", "ppc64le": 1390 return "9", nil 1391 } 1392 return "", errors.New("unsupported GOARCH " + goarch) 1393 }