github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/vndr/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 // This is copy of package from golang repo 6 // It is modified to not treat MultiplePackageError as "bad". 7 8 package build 9 10 import ( 11 "bytes" 12 "errors" 13 "fmt" 14 "go/ast" 15 "go/doc" 16 "go/parser" 17 "go/token" 18 "io" 19 "io/ioutil" 20 "log" 21 "os" 22 pathpkg "path" 23 "path/filepath" 24 "runtime" 25 "sort" 26 "strconv" 27 "strings" 28 "unicode" 29 "unicode/utf8" 30 ) 31 32 // A Context specifies the supporting context for a build. 33 type Context struct { 34 GOARCH string // target architecture 35 GOOS string // target operating system 36 GOROOT string // Go root 37 GOPATH string // Go path 38 CgoEnabled bool // whether cgo can be used 39 UseAllFiles bool // use files regardless of +build lines, file names 40 Compiler string // compiler to assume when computing target paths 41 42 // The build and release tags specify build constraints 43 // that should be considered satisfied when processing +build lines. 44 // Clients creating a new context may customize BuildTags, which 45 // defaults to empty, but it is usually an error to customize ReleaseTags, 46 // which defaults to the list of Go releases the current release is compatible with. 47 // In addition to the BuildTags and ReleaseTags, build constraints 48 // consider the values of GOARCH and GOOS as satisfied tags. 49 BuildTags []string 50 ReleaseTags []string 51 52 // The install suffix specifies a suffix to use in the name of the installation 53 // directory. By default it is empty, but custom builds that need to keep 54 // their outputs separate can set InstallSuffix to do so. For example, when 55 // using the race detector, the go command uses InstallSuffix = "race", so 56 // that on a Linux/386 system, packages are written to a directory named 57 // "linux_386_race" instead of the usual "linux_386". 58 InstallSuffix string 59 60 // By default, Import uses the operating system's file system calls 61 // to read directories and files. To read from other sources, 62 // callers can set the following functions. They all have default 63 // behaviors that use the local file system, so clients need only set 64 // the functions whose behaviors they wish to change. 65 66 // JoinPath joins the sequence of path fragments into a single path. 67 // If JoinPath is nil, Import uses filepath.Join. 68 JoinPath func(elem ...string) string 69 70 // SplitPathList splits the path list into a slice of individual paths. 71 // If SplitPathList is nil, Import uses filepath.SplitList. 72 SplitPathList func(list string) []string 73 74 // IsAbsPath reports whether path is an absolute path. 75 // If IsAbsPath is nil, Import uses filepath.IsAbs. 76 IsAbsPath func(path string) bool 77 78 // IsDir reports whether the path names a directory. 79 // If IsDir is nil, Import calls os.Stat and uses the result's IsDir method. 80 IsDir func(path string) bool 81 82 // HasSubdir reports whether dir is a subdirectory of 83 // (perhaps multiple levels below) root. 84 // If so, HasSubdir sets rel to a slash-separated path that 85 // can be joined to root to produce a path equivalent to dir. 86 // If HasSubdir is nil, Import uses an implementation built on 87 // filepath.EvalSymlinks. 88 HasSubdir func(root, dir string) (rel string, ok bool) 89 90 // ReadDir returns a slice of os.FileInfo, sorted by Name, 91 // describing the content of the named directory. 92 // If ReadDir is nil, Import uses ioutil.ReadDir. 93 ReadDir func(dir string) ([]os.FileInfo, error) 94 95 // OpenFile opens a file (not a directory) for reading. 96 // If OpenFile is nil, Import uses os.Open. 97 OpenFile func(path string) (io.ReadCloser, error) 98 } 99 100 // joinPath calls ctxt.JoinPath (if not nil) or else filepath.Join. 101 func (ctxt *Context) joinPath(elem ...string) string { 102 if f := ctxt.JoinPath; f != nil { 103 return f(elem...) 104 } 105 return filepath.Join(elem...) 106 } 107 108 // splitPathList calls ctxt.SplitPathList (if not nil) or else filepath.SplitList. 109 func (ctxt *Context) splitPathList(s string) []string { 110 if f := ctxt.SplitPathList; f != nil { 111 return f(s) 112 } 113 return filepath.SplitList(s) 114 } 115 116 // isAbsPath calls ctxt.IsAbsPath (if not nil) or else filepath.IsAbs. 117 func (ctxt *Context) isAbsPath(path string) bool { 118 if f := ctxt.IsAbsPath; f != nil { 119 return f(path) 120 } 121 return filepath.IsAbs(path) 122 } 123 124 // isDir calls ctxt.IsDir (if not nil) or else uses os.Stat. 125 func (ctxt *Context) isDir(path string) bool { 126 if f := ctxt.IsDir; f != nil { 127 return f(path) 128 } 129 fi, err := os.Stat(path) 130 return err == nil && fi.IsDir() 131 } 132 133 // hasSubdir calls ctxt.HasSubdir (if not nil) or else uses 134 // the local file system to answer the question. 135 func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) { 136 if f := ctxt.HasSubdir; f != nil { 137 return f(root, dir) 138 } 139 140 // Try using paths we received. 141 if rel, ok = hasSubdir(root, dir); ok { 142 return 143 } 144 145 // Try expanding symlinks and comparing 146 // expanded against unexpanded and 147 // expanded against expanded. 148 rootSym, _ := filepath.EvalSymlinks(root) 149 dirSym, _ := filepath.EvalSymlinks(dir) 150 151 if rel, ok = hasSubdir(rootSym, dir); ok { 152 return 153 } 154 if rel, ok = hasSubdir(root, dirSym); ok { 155 return 156 } 157 return hasSubdir(rootSym, dirSym) 158 } 159 160 func hasSubdir(root, dir string) (rel string, ok bool) { 161 const sep = string(filepath.Separator) 162 root = filepath.Clean(root) 163 if !strings.HasSuffix(root, sep) { 164 root += sep 165 } 166 dir = filepath.Clean(dir) 167 if !strings.HasPrefix(dir, root) { 168 return "", false 169 } 170 return filepath.ToSlash(dir[len(root):]), true 171 } 172 173 // readDir calls ctxt.ReadDir (if not nil) or else ioutil.ReadDir. 174 func (ctxt *Context) readDir(path string) ([]os.FileInfo, error) { 175 if f := ctxt.ReadDir; f != nil { 176 return f(path) 177 } 178 return ioutil.ReadDir(path) 179 } 180 181 // openFile calls ctxt.OpenFile (if not nil) or else os.Open. 182 func (ctxt *Context) openFile(path string) (io.ReadCloser, error) { 183 if fn := ctxt.OpenFile; fn != nil { 184 return fn(path) 185 } 186 187 f, err := os.Open(path) 188 if err != nil { 189 return nil, err // nil interface 190 } 191 return f, nil 192 } 193 194 // isFile determines whether path is a file by trying to open it. 195 // It reuses openFile instead of adding another function to the 196 // list in Context. 197 func (ctxt *Context) isFile(path string) bool { 198 f, err := ctxt.openFile(path) 199 if err != nil { 200 return false 201 } 202 f.Close() 203 return true 204 } 205 206 // gopath returns the list of Go path directories. 207 func (ctxt *Context) gopath() []string { 208 var all []string 209 for _, p := range ctxt.splitPathList(ctxt.GOPATH) { 210 if p == "" || p == ctxt.GOROOT { 211 // Empty paths are uninteresting. 212 // If the path is the GOROOT, ignore it. 213 // People sometimes set GOPATH=$GOROOT. 214 // Do not get confused by this common mistake. 215 continue 216 } 217 if strings.HasPrefix(p, "~") { 218 // Path segments starting with ~ on Unix are almost always 219 // users who have incorrectly quoted ~ while setting GOPATH, 220 // preventing it from expanding to $HOME. 221 // The situation is made more confusing by the fact that 222 // bash allows quoted ~ in $PATH (most shells do not). 223 // Do not get confused by this, and do not try to use the path. 224 // It does not exist, and printing errors about it confuses 225 // those users even more, because they think "sure ~ exists!". 226 // The go command diagnoses this situation and prints a 227 // useful error. 228 // On Windows, ~ is used in short names, such as c:\progra~1 229 // for c:\program files. 230 continue 231 } 232 all = append(all, p) 233 } 234 return all 235 } 236 237 // SrcDirs returns a list of package source root directories. 238 // It draws from the current Go root and Go path but omits directories 239 // that do not exist. 240 func (ctxt *Context) SrcDirs() []string { 241 var all []string 242 if ctxt.GOROOT != "" { 243 dir := ctxt.joinPath(ctxt.GOROOT, "src") 244 if ctxt.isDir(dir) { 245 all = append(all, dir) 246 } 247 } 248 for _, p := range ctxt.gopath() { 249 dir := ctxt.joinPath(p, "src") 250 if ctxt.isDir(dir) { 251 all = append(all, dir) 252 } 253 } 254 return all 255 } 256 257 // An ImportMode controls the behavior of the Import method. 258 type ImportMode uint 259 260 // ImportMode constants 261 const ( 262 FindOnly ImportMode = 1 << iota 263 AllowBinary 264 ImportComment 265 IgnoreVendor 266 ) 267 268 // A Package describes the Go package found in a directory. 269 type Package struct { 270 Dir string // directory containing package sources 271 Name string // package name 272 ImportComment string // path in import comment on package statement 273 Doc string // documentation synopsis 274 ImportPath string // import path of package ("" if unknown) 275 Root string // root of Go tree where this package lives 276 SrcRoot string // package source root directory ("" if unknown) 277 PkgRoot string // package install root directory ("" if unknown) 278 PkgTargetRoot string // architecture dependent install root directory ("" if unknown) 279 BinDir string // command install directory ("" if unknown) 280 Goroot bool // package found in Go root 281 PkgObj string // installed .a file 282 AllTags []string // tags that can influence file selection in this directory 283 ConflictDir string // this directory shadows Dir in $GOPATH 284 BinaryOnly bool // cannot be rebuilt from source (has //go:binary-only-package comment) 285 286 // Source files 287 GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) 288 CgoFiles []string // .go source files that import "C" 289 IgnoredGoFiles []string // .go source files ignored for this build 290 InvalidGoFiles []string // .go source files with detected problems (parse error, wrong package name, and so on) 291 CFiles []string // .c source files 292 CXXFiles []string // .cc, .cpp and .cxx source files 293 MFiles []string // .m (Objective-C) source files 294 HFiles []string // .h, .hh, .hpp and .hxx source files 295 FFiles []string // .f, .F, .for and .f90 Fortran source files 296 SFiles []string // .s source files 297 SwigFiles []string // .swig files 298 SwigCXXFiles []string // .swigcxx files 299 SysoFiles []string // .syso system object files to add to archive 300 301 // Cgo directives 302 CgoCFLAGS []string // Cgo CFLAGS directives 303 CgoCPPFLAGS []string // Cgo CPPFLAGS directives 304 CgoCXXFLAGS []string // Cgo CXXFLAGS directives 305 CgoFFLAGS []string // Cgo FFLAGS directives 306 CgoLDFLAGS []string // Cgo LDFLAGS directives 307 CgoPkgConfig []string // Cgo pkg-config directives 308 309 // Dependency information 310 Imports []string // imports from GoFiles, CgoFiles 311 ImportPos map[string][]token.Position // line information for Imports 312 313 // Test information 314 TestGoFiles []string // _test.go files in package 315 TestImports []string // imports from TestGoFiles 316 TestImportPos map[string][]token.Position // line information for TestImports 317 XTestGoFiles []string // _test.go files outside package 318 XTestImports []string // imports from XTestGoFiles 319 XTestImportPos map[string][]token.Position // line information for XTestImports 320 } 321 322 // IsCommand reports whether the package is considered a 323 // command to be installed (not just a library). 324 // Packages named "main" are treated as commands. 325 func (p *Package) IsCommand() bool { 326 return p.Name == "main" 327 } 328 329 // ImportDir is like Import but processes the Go package found in 330 // the named directory. 331 func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) { 332 return ctxt.Import(".", dir, mode) 333 } 334 335 // NoGoError is the error used by Import to describe a directory 336 // containing no buildable Go source files. (It may still contain 337 // test files, files hidden by build tags, and so on.) 338 type NoGoError struct { 339 Dir string 340 } 341 342 func (e *NoGoError) Error() string { 343 return "no buildable Go source files in " + e.Dir 344 } 345 346 // MultiplePackageError describes a directory containing 347 // multiple buildable Go source files for multiple packages. 348 type MultiplePackageError struct { 349 Dir string // directory containing files 350 Packages []string // package names found 351 Files []string // corresponding files: Files[i] declares package Packages[i] 352 } 353 354 func (e *MultiplePackageError) Error() string { 355 // Error string limited to two entries for compatibility. 356 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) 357 } 358 359 func nameExt(name string) string { 360 i := strings.LastIndex(name, ".") 361 if i < 0 { 362 return "" 363 } 364 return name[i:] 365 } 366 367 // Import returns details about the Go package named by the import path, 368 // interpreting local import paths relative to the srcDir directory. 369 // If the path is a local import path naming a package that can be imported 370 // using a standard import path, the returned package will set p.ImportPath 371 // to that path. 372 // 373 // In the directory containing the package, .go, .c, .h, and .s files are 374 // considered part of the package except for: 375 // 376 // - .go files in package documentation 377 // - files starting with _ or . (likely editor temporary files) 378 // - files with build constraints not satisfied by the context 379 // 380 // If an error occurs, Import returns a non-nil error and a non-nil 381 // *Package containing partial information. 382 // 383 func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) { 384 p := &Package{ 385 ImportPath: path, 386 } 387 if path == "" { 388 return p, fmt.Errorf("import %q: invalid import path", path) 389 } 390 391 var pkgtargetroot string 392 var pkga string 393 var pkgerr error 394 suffix := "" 395 if ctxt.InstallSuffix != "" { 396 suffix = "_" + ctxt.InstallSuffix 397 } 398 switch ctxt.Compiler { 399 case "gccgo": 400 pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix 401 case "gc": 402 pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix 403 default: 404 // Save error for end of function. 405 pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler) 406 } 407 setPkga := func() { 408 switch ctxt.Compiler { 409 case "gccgo": 410 dir, elem := pathpkg.Split(p.ImportPath) 411 pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a" 412 case "gc": 413 pkga = pkgtargetroot + "/" + p.ImportPath + ".a" 414 } 415 } 416 setPkga() 417 418 binaryOnly := false 419 if IsLocalImport(path) { 420 pkga = "" // local imports have no installed path 421 if srcDir == "" { 422 return p, fmt.Errorf("import %q: import relative to unknown directory", path) 423 } 424 if !ctxt.isAbsPath(path) { 425 p.Dir = ctxt.joinPath(srcDir, path) 426 } 427 // Determine canonical import path, if any. 428 // Exclude results where the import path would include /testdata/. 429 inTestdata := func(sub string) bool { 430 return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || strings.HasPrefix(sub, "testdata/") || sub == "testdata" 431 } 432 if ctxt.GOROOT != "" { 433 root := ctxt.joinPath(ctxt.GOROOT, "src") 434 if sub, ok := ctxt.hasSubdir(root, p.Dir); ok && !inTestdata(sub) { 435 p.Goroot = true 436 p.ImportPath = sub 437 p.Root = ctxt.GOROOT 438 goto Found 439 } 440 } 441 all := ctxt.gopath() 442 for i, root := range all { 443 rootsrc := ctxt.joinPath(root, "src") 444 if sub, ok := ctxt.hasSubdir(rootsrc, p.Dir); ok && !inTestdata(sub) { 445 // We found a potential import path for dir, 446 // but check that using it wouldn't find something 447 // else first. 448 if ctxt.GOROOT != "" { 449 if dir := ctxt.joinPath(ctxt.GOROOT, "src", sub); ctxt.isDir(dir) { 450 p.ConflictDir = dir 451 goto Found 452 } 453 } 454 for _, earlyRoot := range all[:i] { 455 if dir := ctxt.joinPath(earlyRoot, "src", sub); ctxt.isDir(dir) { 456 p.ConflictDir = dir 457 goto Found 458 } 459 } 460 461 // sub would not name some other directory instead of this one. 462 // Record it. 463 p.ImportPath = sub 464 p.Root = root 465 goto Found 466 } 467 } 468 // It's okay that we didn't find a root containing dir. 469 // Keep going with the information we have. 470 } else { 471 if strings.HasPrefix(path, "/") { 472 return p, fmt.Errorf("import %q: cannot import absolute path", path) 473 } 474 475 // tried records the location of unsuccessful package lookups 476 var tried struct { 477 vendor []string 478 goroot string 479 gopath []string 480 } 481 gopath := ctxt.gopath() 482 483 // Vendor directories get first chance to satisfy import. 484 if mode&IgnoreVendor == 0 && srcDir != "" { 485 searchVendor := func(root string, isGoroot bool) bool { 486 sub, ok := ctxt.hasSubdir(root, srcDir) 487 if !ok || !strings.HasPrefix(sub, "src/") || strings.Contains(sub, "/testdata/") { 488 return false 489 } 490 for { 491 vendor := ctxt.joinPath(root, sub, "vendor") 492 if ctxt.isDir(vendor) { 493 dir := ctxt.joinPath(vendor, path) 494 if ctxt.isDir(dir) && hasGoFiles(ctxt, dir) { 495 p.Dir = dir 496 p.ImportPath = strings.TrimPrefix(pathpkg.Join(sub, "vendor", path), "src/") 497 p.Goroot = isGoroot 498 p.Root = root 499 setPkga() // p.ImportPath changed 500 return true 501 } 502 tried.vendor = append(tried.vendor, dir) 503 } 504 i := strings.LastIndex(sub, "/") 505 if i < 0 { 506 break 507 } 508 sub = sub[:i] 509 } 510 return false 511 } 512 if searchVendor(ctxt.GOROOT, true) { 513 goto Found 514 } 515 for _, root := range gopath { 516 if searchVendor(root, false) { 517 goto Found 518 } 519 } 520 } 521 522 // Determine directory from import path. 523 if ctxt.GOROOT != "" { 524 dir := ctxt.joinPath(ctxt.GOROOT, "src", path) 525 isDir := ctxt.isDir(dir) 526 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga)) 527 if isDir || binaryOnly { 528 p.Dir = dir 529 p.Goroot = true 530 p.Root = ctxt.GOROOT 531 goto Found 532 } 533 tried.goroot = dir 534 } 535 for _, root := range gopath { 536 dir := ctxt.joinPath(root, "src", path) 537 isDir := ctxt.isDir(dir) 538 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(root, pkga)) 539 if isDir || binaryOnly { 540 p.Dir = dir 541 p.Root = root 542 goto Found 543 } 544 tried.gopath = append(tried.gopath, dir) 545 } 546 547 // package was not found 548 var paths []string 549 format := "\t%s (vendor tree)" 550 for _, dir := range tried.vendor { 551 paths = append(paths, fmt.Sprintf(format, dir)) 552 format = "\t%s" 553 } 554 if tried.goroot != "" { 555 paths = append(paths, fmt.Sprintf("\t%s (from $GOROOT)", tried.goroot)) 556 } else { 557 paths = append(paths, "\t($GOROOT not set)") 558 } 559 format = "\t%s (from $GOPATH)" 560 for _, dir := range tried.gopath { 561 paths = append(paths, fmt.Sprintf(format, dir)) 562 format = "\t%s" 563 } 564 if len(tried.gopath) == 0 { 565 paths = append(paths, "\t($GOPATH not set)") 566 } 567 return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, strings.Join(paths, "\n")) 568 } 569 570 Found: 571 if p.Root != "" { 572 p.SrcRoot = ctxt.joinPath(p.Root, "src") 573 p.PkgRoot = ctxt.joinPath(p.Root, "pkg") 574 p.BinDir = ctxt.joinPath(p.Root, "bin") 575 if pkga != "" { 576 p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot) 577 p.PkgObj = ctxt.joinPath(p.Root, pkga) 578 } 579 } 580 581 if mode&FindOnly != 0 { 582 return p, pkgerr 583 } 584 if binaryOnly && (mode&AllowBinary) != 0 { 585 return p, pkgerr 586 } 587 588 dirs, err := ctxt.readDir(p.Dir) 589 if err != nil { 590 return p, err 591 } 592 593 var badGoError error 594 var Sfiles []string // files with ".S" (capital S) 595 var firstFile, firstCommentFile string 596 imported := make(map[string][]token.Position) 597 testImported := make(map[string][]token.Position) 598 xTestImported := make(map[string][]token.Position) 599 allTags := make(map[string]bool) 600 fset := token.NewFileSet() 601 for _, d := range dirs { 602 if d.IsDir() { 603 continue 604 } 605 606 name := d.Name() 607 ext := nameExt(name) 608 609 badFile := func(err error) { 610 if badGoError == nil { 611 badGoError = err 612 } 613 p.InvalidGoFiles = append(p.InvalidGoFiles, name) 614 } 615 616 match, data, filename, err := ctxt.matchFile(p.Dir, name, true, allTags, &p.BinaryOnly) 617 if err != nil { 618 badFile(err) 619 continue 620 } 621 if !match { 622 if ext == ".go" { 623 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) 624 } 625 continue 626 } 627 628 // Going to save the file. For non-Go files, can stop here. 629 switch ext { 630 case ".c": 631 p.CFiles = append(p.CFiles, name) 632 continue 633 case ".cc", ".cpp", ".cxx": 634 p.CXXFiles = append(p.CXXFiles, name) 635 continue 636 case ".m": 637 p.MFiles = append(p.MFiles, name) 638 continue 639 case ".h", ".hh", ".hpp", ".hxx": 640 p.HFiles = append(p.HFiles, name) 641 continue 642 case ".f", ".F", ".for", ".f90": 643 p.FFiles = append(p.FFiles, name) 644 continue 645 case ".s": 646 p.SFiles = append(p.SFiles, name) 647 continue 648 case ".S": 649 Sfiles = append(Sfiles, name) 650 continue 651 case ".swig": 652 p.SwigFiles = append(p.SwigFiles, name) 653 continue 654 case ".swigcxx": 655 p.SwigCXXFiles = append(p.SwigCXXFiles, name) 656 continue 657 case ".syso": 658 // binary objects to add to package archive 659 // Likely of the form foo_windows.syso, but 660 // the name was vetted above with goodOSArchFile. 661 p.SysoFiles = append(p.SysoFiles, name) 662 continue 663 } 664 665 pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments) 666 if err != nil { 667 badFile(err) 668 continue 669 } 670 671 pkg := pf.Name.Name 672 if pkg == "documentation" { 673 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) 674 continue 675 } 676 677 isTest := strings.HasSuffix(name, "_test.go") 678 isXTest := false 679 if isTest && strings.HasSuffix(pkg, "_test") { 680 isXTest = true 681 pkg = pkg[:len(pkg)-len("_test")] 682 } 683 684 if p.Name == "" { 685 p.Name = pkg 686 firstFile = name 687 } else if pkg != p.Name { 688 pkgerr = &MultiplePackageError{ 689 Dir: p.Dir, 690 Packages: []string{p.Name, pkg}, 691 Files: []string{firstFile, name}, 692 } 693 p.InvalidGoFiles = append(p.InvalidGoFiles, name) 694 } 695 if pf.Doc != nil && p.Doc == "" { 696 p.Doc = doc.Synopsis(pf.Doc.Text()) 697 } 698 699 if mode&ImportComment != 0 { 700 qcom, line := findImportComment(data) 701 if line != 0 { 702 com, err := strconv.Unquote(qcom) 703 if err != nil { 704 badFile(fmt.Errorf("%s:%d: cannot parse import comment", filename, line)) 705 } else if p.ImportComment == "" { 706 p.ImportComment = com 707 firstCommentFile = name 708 } else if p.ImportComment != com { 709 badFile(fmt.Errorf("found import comments %q (%s) and %q (%s) in %s", p.ImportComment, firstCommentFile, com, name, p.Dir)) 710 } 711 } 712 } 713 714 // Record imports and information about cgo. 715 isCgo := false 716 for _, decl := range pf.Decls { 717 d, ok := decl.(*ast.GenDecl) 718 if !ok { 719 continue 720 } 721 for _, dspec := range d.Specs { 722 spec, ok := dspec.(*ast.ImportSpec) 723 if !ok { 724 continue 725 } 726 quoted := spec.Path.Value 727 path, err := strconv.Unquote(quoted) 728 if err != nil { 729 log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted) 730 } 731 if isXTest { 732 xTestImported[path] = append(xTestImported[path], fset.Position(spec.Pos())) 733 } else if isTest { 734 testImported[path] = append(testImported[path], fset.Position(spec.Pos())) 735 } else { 736 imported[path] = append(imported[path], fset.Position(spec.Pos())) 737 } 738 if path == "C" { 739 if isTest { 740 badFile(fmt.Errorf("use of cgo in test %s not supported", filename)) 741 } else { 742 cg := spec.Doc 743 if cg == nil && len(d.Specs) == 1 { 744 cg = d.Doc 745 } 746 if cg != nil { 747 if err := ctxt.saveCgo(filename, p, cg); err != nil { 748 badFile(err) 749 } 750 } 751 isCgo = true 752 } 753 } 754 } 755 } 756 if isCgo { 757 allTags["cgo"] = true 758 if ctxt.CgoEnabled { 759 p.CgoFiles = append(p.CgoFiles, name) 760 } else { 761 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) 762 } 763 } else if isXTest { 764 p.XTestGoFiles = append(p.XTestGoFiles, name) 765 } else if isTest { 766 p.TestGoFiles = append(p.TestGoFiles, name) 767 } else { 768 p.GoFiles = append(p.GoFiles, name) 769 } 770 } 771 if badGoError != nil { 772 return p, badGoError 773 } 774 if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 { 775 return p, &NoGoError{p.Dir} 776 } 777 778 for tag := range allTags { 779 p.AllTags = append(p.AllTags, tag) 780 } 781 sort.Strings(p.AllTags) 782 783 p.Imports, p.ImportPos = cleanImports(imported) 784 p.TestImports, p.TestImportPos = cleanImports(testImported) 785 p.XTestImports, p.XTestImportPos = cleanImports(xTestImported) 786 787 // add the .S files only if we are using cgo 788 // (which means gcc will compile them). 789 // The standard assemblers expect .s files. 790 if len(p.CgoFiles) > 0 { 791 p.SFiles = append(p.SFiles, Sfiles...) 792 sort.Strings(p.SFiles) 793 } 794 795 return p, pkgerr 796 } 797 798 // hasGoFiles reports whether dir contains any files with names ending in .go. 799 // For a vendor check we must exclude directories that contain no .go files. 800 // Otherwise it is not possible to vendor just a/b/c and still import the 801 // non-vendored a/b. See golang.org/issue/13832. 802 func hasGoFiles(ctxt *Context, dir string) bool { 803 ents, _ := ctxt.readDir(dir) 804 for _, ent := range ents { 805 if !ent.IsDir() && strings.HasSuffix(ent.Name(), ".go") { 806 return true 807 } 808 } 809 return false 810 } 811 812 func findImportComment(data []byte) (s string, line int) { 813 // expect keyword package 814 word, data := parseWord(data) 815 if string(word) != "package" { 816 return "", 0 817 } 818 819 // expect package name 820 _, data = parseWord(data) 821 822 // now ready for import comment, a // or /* */ comment 823 // beginning and ending on the current line. 824 for len(data) > 0 && (data[0] == ' ' || data[0] == '\t' || data[0] == '\r') { 825 data = data[1:] 826 } 827 828 var comment []byte 829 switch { 830 case bytes.HasPrefix(data, slashSlash): 831 i := bytes.Index(data, newline) 832 if i < 0 { 833 i = len(data) 834 } 835 comment = data[2:i] 836 case bytes.HasPrefix(data, slashStar): 837 data = data[2:] 838 i := bytes.Index(data, starSlash) 839 if i < 0 { 840 // malformed comment 841 return "", 0 842 } 843 comment = data[:i] 844 if bytes.Contains(comment, newline) { 845 return "", 0 846 } 847 } 848 comment = bytes.TrimSpace(comment) 849 850 // split comment into `import`, `"pkg"` 851 word, arg := parseWord(comment) 852 if string(word) != "import" { 853 return "", 0 854 } 855 856 line = 1 + bytes.Count(data[:cap(data)-cap(arg)], newline) 857 return strings.TrimSpace(string(arg)), line 858 } 859 860 var ( 861 slashSlash = []byte("//") 862 slashStar = []byte("/*") 863 starSlash = []byte("*/") 864 newline = []byte("\n") 865 ) 866 867 // skipSpaceOrComment returns data with any leading spaces or comments removed. 868 func skipSpaceOrComment(data []byte) []byte { 869 for len(data) > 0 { 870 switch data[0] { 871 case ' ', '\t', '\r', '\n': 872 data = data[1:] 873 continue 874 case '/': 875 if bytes.HasPrefix(data, slashSlash) { 876 i := bytes.Index(data, newline) 877 if i < 0 { 878 return nil 879 } 880 data = data[i+1:] 881 continue 882 } 883 if bytes.HasPrefix(data, slashStar) { 884 data = data[2:] 885 i := bytes.Index(data, starSlash) 886 if i < 0 { 887 return nil 888 } 889 data = data[i+2:] 890 continue 891 } 892 } 893 break 894 } 895 return data 896 } 897 898 // parseWord skips any leading spaces or comments in data 899 // and then parses the beginning of data as an identifier or keyword, 900 // returning that word and what remains after the word. 901 func parseWord(data []byte) (word, rest []byte) { 902 data = skipSpaceOrComment(data) 903 904 // Parse past leading word characters. 905 rest = data 906 for { 907 r, size := utf8.DecodeRune(rest) 908 if unicode.IsLetter(r) || '0' <= r && r <= '9' || r == '_' { 909 rest = rest[size:] 910 continue 911 } 912 break 913 } 914 915 word = data[:len(data)-len(rest)] 916 if len(word) == 0 { 917 return nil, nil 918 } 919 920 return word, rest 921 } 922 923 // MatchFile reports whether the file with the given name in the given directory 924 // matches the context and would be included in a Package created by ImportDir 925 // of that directory. 926 // 927 // MatchFile considers the name of the file and may use ctxt.OpenFile to 928 // read some or all of the file's content. 929 func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) { 930 match, _, _, err = ctxt.matchFile(dir, name, false, nil, nil) 931 return 932 } 933 934 // matchFile determines whether the file with the given name in the given directory 935 // should be included in the package being constructed. 936 // It returns the data read from the file. 937 // If returnImports is true and name denotes a Go program, matchFile reads 938 // until the end of the imports (and returns that data) even though it only 939 // considers text until the first non-comment. 940 // If allTags is non-nil, matchFile records any encountered build tag 941 // by setting allTags[tag] = true. 942 func (ctxt *Context) matchFile(dir, name string, returnImports bool, allTags map[string]bool, binaryOnly *bool) (match bool, data []byte, filename string, err error) { 943 if strings.HasPrefix(name, "_") || 944 strings.HasPrefix(name, ".") { 945 return 946 } 947 948 i := strings.LastIndex(name, ".") 949 if i < 0 { 950 i = len(name) 951 } 952 ext := name[i:] 953 954 if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles { 955 return 956 } 957 958 switch ext { 959 case ".go", ".c", ".cc", ".cxx", ".cpp", ".m", ".s", ".h", ".hh", ".hpp", ".hxx", ".f", ".F", ".f90", ".S", ".swig", ".swigcxx": 960 // tentatively okay - read to make sure 961 case ".syso": 962 // binary, no reading 963 match = true 964 return 965 default: 966 // skip 967 return 968 } 969 970 filename = ctxt.joinPath(dir, name) 971 f, err := ctxt.openFile(filename) 972 if err != nil { 973 return 974 } 975 976 if strings.HasSuffix(filename, ".go") { 977 data, err = readImports(f, false, nil) 978 if strings.HasSuffix(filename, "_test.go") { 979 binaryOnly = nil // ignore //go:binary-only-package comments in _test.go files 980 } 981 } else { 982 binaryOnly = nil // ignore //go:binary-only-package comments in non-Go sources 983 data, err = readComments(f) 984 } 985 f.Close() 986 if err != nil { 987 err = fmt.Errorf("read %s: %v", filename, err) 988 return 989 } 990 991 // Look for +build comments to accept or reject the file. 992 if !ctxt.shouldBuild(data, allTags, binaryOnly) && !ctxt.UseAllFiles { 993 return 994 } 995 996 match = true 997 return 998 } 999 1000 func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) { 1001 all := make([]string, 0, len(m)) 1002 for path := range m { 1003 all = append(all, path) 1004 } 1005 sort.Strings(all) 1006 return all, m 1007 } 1008 1009 var slashslash = []byte("//") 1010 1011 // Special comment denoting a binary-only package. 1012 // See https://golang.org/design/2775-binary-only-packages 1013 // for more about the design of binary-only packages. 1014 var binaryOnlyComment = []byte("//go:binary-only-package") 1015 1016 // shouldBuild reports whether it is okay to use this file, 1017 // The rule is that in the file's leading run of // comments 1018 // and blank lines, which must be followed by a blank line 1019 // (to avoid including a Go package clause doc comment), 1020 // lines beginning with '// +build' are taken as build directives. 1021 // 1022 // The file is accepted only if each such line lists something 1023 // matching the file. For example: 1024 // 1025 // // +build windows linux 1026 // 1027 // marks the file as applicable only on Windows and Linux. 1028 // 1029 // If shouldBuild finds a //go:binary-only-package comment in a file that 1030 // should be built, it sets *binaryOnly to true. Otherwise it does 1031 // not change *binaryOnly. 1032 // 1033 func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool, binaryOnly *bool) bool { 1034 sawBinaryOnly := false 1035 1036 // Pass 1. Identify leading run of // comments and blank lines, 1037 // which must be followed by a blank line. 1038 end := 0 1039 p := content 1040 for len(p) > 0 { 1041 line := p 1042 if i := bytes.IndexByte(line, '\n'); i >= 0 { 1043 line, p = line[:i], p[i+1:] 1044 } else { 1045 p = p[len(p):] 1046 } 1047 line = bytes.TrimSpace(line) 1048 if len(line) == 0 { // Blank line 1049 end = len(content) - len(p) 1050 continue 1051 } 1052 if !bytes.HasPrefix(line, slashslash) { // Not comment line 1053 break 1054 } 1055 } 1056 content = content[:end] 1057 1058 // Pass 2. Process each line in the run. 1059 p = content 1060 allok := true 1061 for len(p) > 0 { 1062 line := p 1063 if i := bytes.IndexByte(line, '\n'); i >= 0 { 1064 line, p = line[:i], p[i+1:] 1065 } else { 1066 p = p[len(p):] 1067 } 1068 line = bytes.TrimSpace(line) 1069 if bytes.HasPrefix(line, slashslash) { 1070 if bytes.Equal(line, binaryOnlyComment) { 1071 sawBinaryOnly = true 1072 } 1073 line = bytes.TrimSpace(line[len(slashslash):]) 1074 if len(line) > 0 && line[0] == '+' { 1075 // Looks like a comment +line. 1076 f := strings.Fields(string(line)) 1077 if f[0] == "+build" { 1078 ok := false 1079 for _, tok := range f[1:] { 1080 if ctxt.match(tok, allTags) { 1081 ok = true 1082 } 1083 } 1084 if !ok { 1085 allok = false 1086 } 1087 } 1088 } 1089 } 1090 } 1091 1092 if binaryOnly != nil && sawBinaryOnly { 1093 *binaryOnly = true 1094 } 1095 1096 return allok 1097 } 1098 1099 // saveCgo saves the information from the #cgo lines in the import "C" comment. 1100 // These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives 1101 // that affect the way cgo's C code is built. 1102 func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error { 1103 text := cg.Text() 1104 for _, line := range strings.Split(text, "\n") { 1105 orig := line 1106 1107 // Line is 1108 // #cgo [GOOS/GOARCH...] LDFLAGS: stuff 1109 // 1110 line = strings.TrimSpace(line) 1111 if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') { 1112 continue 1113 } 1114 1115 // Split at colon. 1116 line = strings.TrimSpace(line[4:]) 1117 i := strings.Index(line, ":") 1118 if i < 0 { 1119 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) 1120 } 1121 line, argstr := line[:i], line[i+1:] 1122 1123 // Parse GOOS/GOARCH stuff. 1124 f := strings.Fields(line) 1125 if len(f) < 1 { 1126 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) 1127 } 1128 1129 cond, verb := f[:len(f)-1], f[len(f)-1] 1130 if len(cond) > 0 { 1131 ok := false 1132 for _, c := range cond { 1133 if ctxt.match(c, nil) { 1134 ok = true 1135 break 1136 } 1137 } 1138 if !ok { 1139 continue 1140 } 1141 } 1142 1143 args, err := splitQuoted(argstr) 1144 if err != nil { 1145 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) 1146 } 1147 var ok bool 1148 for i, arg := range args { 1149 if arg, ok = expandSrcDir(arg, di.Dir); !ok { 1150 return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg) 1151 } 1152 args[i] = arg 1153 } 1154 1155 switch verb { 1156 case "CFLAGS": 1157 di.CgoCFLAGS = append(di.CgoCFLAGS, args...) 1158 case "CPPFLAGS": 1159 di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...) 1160 case "CXXFLAGS": 1161 di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...) 1162 case "FFLAGS": 1163 di.CgoFFLAGS = append(di.CgoFFLAGS, args...) 1164 case "LDFLAGS": 1165 di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...) 1166 case "pkg-config": 1167 di.CgoPkgConfig = append(di.CgoPkgConfig, args...) 1168 default: 1169 return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig) 1170 } 1171 } 1172 return nil 1173 } 1174 1175 // expandSrcDir expands any occurrence of ${SRCDIR}, making sure 1176 // the result is safe for the shell. 1177 func expandSrcDir(str string, srcdir string) (string, bool) { 1178 // "\" delimited paths cause safeCgoName to fail 1179 // so convert native paths with a different delimiter 1180 // to "/" before starting (eg: on windows). 1181 srcdir = filepath.ToSlash(srcdir) 1182 1183 // Spaces are tolerated in ${SRCDIR}, but not anywhere else. 1184 chunks := strings.Split(str, "${SRCDIR}") 1185 if len(chunks) < 2 { 1186 return str, safeCgoName(str, false) 1187 } 1188 ok := true 1189 for _, chunk := range chunks { 1190 ok = ok && (chunk == "" || safeCgoName(chunk, false)) 1191 } 1192 ok = ok && (srcdir == "" || safeCgoName(srcdir, true)) 1193 res := strings.Join(chunks, srcdir) 1194 return res, ok && res != "" 1195 } 1196 1197 // NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN. 1198 // We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay. 1199 // See golang.org/issue/6038. 1200 // The @ is for OS X. See golang.org/issue/13720. 1201 const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@" 1202 const safeSpaces = " " 1203 1204 var safeBytes = []byte(safeSpaces + safeString) 1205 1206 func safeCgoName(s string, spaces bool) bool { 1207 if s == "" { 1208 return false 1209 } 1210 safe := safeBytes 1211 if !spaces { 1212 safe = safe[len(safeSpaces):] 1213 } 1214 for i := 0; i < len(s); i++ { 1215 if c := s[i]; c < utf8.RuneSelf && bytes.IndexByte(safe, c) < 0 { 1216 return false 1217 } 1218 } 1219 return true 1220 } 1221 1222 // splitQuoted splits the string s around each instance of one or more consecutive 1223 // white space characters while taking into account quotes and escaping, and 1224 // returns an array of substrings of s or an empty list if s contains only white space. 1225 // Single quotes and double quotes are recognized to prevent splitting within the 1226 // quoted region, and are removed from the resulting substrings. If a quote in s 1227 // isn't closed err will be set and r will have the unclosed argument as the 1228 // last element. The backslash is used for escaping. 1229 // 1230 // For example, the following string: 1231 // 1232 // a b:"c d" 'e''f' "g\"" 1233 // 1234 // Would be parsed as: 1235 // 1236 // []string{"a", "b:c d", "ef", `g"`} 1237 // 1238 func splitQuoted(s string) (r []string, err error) { 1239 var args []string 1240 arg := make([]rune, len(s)) 1241 escaped := false 1242 quoted := false 1243 quote := '\x00' 1244 i := 0 1245 for _, rune := range s { 1246 switch { 1247 case escaped: 1248 escaped = false 1249 case rune == '\\': 1250 escaped = true 1251 continue 1252 case quote != '\x00': 1253 if rune == quote { 1254 quote = '\x00' 1255 continue 1256 } 1257 case rune == '"' || rune == '\'': 1258 quoted = true 1259 quote = rune 1260 continue 1261 case unicode.IsSpace(rune): 1262 if quoted || i > 0 { 1263 quoted = false 1264 args = append(args, string(arg[:i])) 1265 i = 0 1266 } 1267 continue 1268 } 1269 arg[i] = rune 1270 i++ 1271 } 1272 if quoted || i > 0 { 1273 args = append(args, string(arg[:i])) 1274 } 1275 if quote != 0 { 1276 err = errors.New("unclosed quote") 1277 } else if escaped { 1278 err = errors.New("unfinished escaping") 1279 } 1280 return args, err 1281 } 1282 1283 // match reports whether the name is one of: 1284 // 1285 // $GOOS 1286 // $GOARCH 1287 // cgo (if cgo is enabled) 1288 // !cgo (if cgo is disabled) 1289 // ctxt.Compiler 1290 // !ctxt.Compiler 1291 // tag (if tag is listed in ctxt.BuildTags or ctxt.ReleaseTags) 1292 // !tag (if tag is not listed in ctxt.BuildTags or ctxt.ReleaseTags) 1293 // a comma-separated list of any of these 1294 // 1295 func (ctxt *Context) match(name string, allTags map[string]bool) bool { 1296 if name == "" { 1297 if allTags != nil { 1298 allTags[name] = true 1299 } 1300 return false 1301 } 1302 if i := strings.Index(name, ","); i >= 0 { 1303 // comma-separated list 1304 ok1 := ctxt.match(name[:i], allTags) 1305 ok2 := ctxt.match(name[i+1:], allTags) 1306 return ok1 && ok2 1307 } 1308 if strings.HasPrefix(name, "!!") { // bad syntax, reject always 1309 return false 1310 } 1311 if strings.HasPrefix(name, "!") { // negation 1312 return len(name) > 1 && !ctxt.match(name[1:], allTags) 1313 } 1314 1315 if allTags != nil { 1316 allTags[name] = true 1317 } 1318 1319 // Tags must be letters, digits, underscores or dots. 1320 // Unlike in Go identifiers, all digits are fine (e.g., "386"). 1321 for _, c := range name { 1322 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { 1323 return false 1324 } 1325 } 1326 1327 // special tags 1328 if ctxt.CgoEnabled && name == "cgo" { 1329 return true 1330 } 1331 if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler { 1332 return true 1333 } 1334 if ctxt.GOOS == "android" && name == "linux" { 1335 return true 1336 } 1337 1338 // other tags 1339 for _, tag := range ctxt.BuildTags { 1340 if tag == name { 1341 return true 1342 } 1343 } 1344 for _, tag := range ctxt.ReleaseTags { 1345 if tag == name { 1346 return true 1347 } 1348 } 1349 1350 return false 1351 } 1352 1353 // goodOSArchFile returns false if the name contains a $GOOS or $GOARCH 1354 // suffix which does not match the current system. 1355 // The recognized name formats are: 1356 // 1357 // name_$(GOOS).* 1358 // name_$(GOARCH).* 1359 // name_$(GOOS)_$(GOARCH).* 1360 // name_$(GOOS)_test.* 1361 // name_$(GOARCH)_test.* 1362 // name_$(GOOS)_$(GOARCH)_test.* 1363 // 1364 // An exception: if GOOS=android, then files with GOOS=linux are also matched. 1365 func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool { 1366 if dot := strings.Index(name, "."); dot != -1 { 1367 name = name[:dot] 1368 } 1369 1370 // Before Go 1.4, a file called "linux.go" would be equivalent to having a 1371 // build tag "linux" in that file. For Go 1.4 and beyond, we require this 1372 // auto-tagging to apply only to files with a non-empty prefix, so 1373 // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating 1374 // systems, such as android, to arrive without breaking existing code with 1375 // innocuous source code in "android.go". The easiest fix: cut everything 1376 // in the name before the initial _. 1377 i := strings.Index(name, "_") 1378 if i < 0 { 1379 return true 1380 } 1381 name = name[i:] // ignore everything before first _ 1382 1383 l := strings.Split(name, "_") 1384 if n := len(l); n > 0 && l[n-1] == "test" { 1385 l = l[:n-1] 1386 } 1387 n := len(l) 1388 if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] { 1389 if allTags != nil { 1390 allTags[l[n-2]] = true 1391 allTags[l[n-1]] = true 1392 } 1393 if l[n-1] != ctxt.GOARCH { 1394 return false 1395 } 1396 if ctxt.GOOS == "android" && l[n-2] == "linux" { 1397 return true 1398 } 1399 return l[n-2] == ctxt.GOOS 1400 } 1401 if n >= 1 && knownOS[l[n-1]] { 1402 if allTags != nil { 1403 allTags[l[n-1]] = true 1404 } 1405 if ctxt.GOOS == "android" && l[n-1] == "linux" { 1406 return true 1407 } 1408 return l[n-1] == ctxt.GOOS 1409 } 1410 if n >= 1 && knownArch[l[n-1]] { 1411 if allTags != nil { 1412 allTags[l[n-1]] = true 1413 } 1414 return l[n-1] == ctxt.GOARCH 1415 } 1416 return true 1417 } 1418 1419 var knownOS = make(map[string]bool) 1420 var knownArch = make(map[string]bool) 1421 1422 func init() { 1423 for _, v := range strings.Fields(goosList) { 1424 knownOS[v] = true 1425 } 1426 for _, v := range strings.Fields(goarchList) { 1427 knownArch[v] = true 1428 } 1429 } 1430 1431 // ToolDir is the directory containing build tools. 1432 var ToolDir = filepath.Join(runtime.GOROOT(), "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH) 1433 1434 // IsLocalImport reports whether the import path is 1435 // a local import path, like ".", "..", "./foo", or "../foo". 1436 func IsLocalImport(path string) bool { 1437 return path == "." || path == ".." || 1438 strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../") 1439 } 1440 1441 // ArchChar returns "?" and an error. 1442 // In earlier versions of Go, the returned string was used to derive 1443 // the compiler and linker tool names, the default object file suffix, 1444 // and the default linker output name. As of Go 1.5, those strings 1445 // no longer vary by architecture; they are compile, link, .o, and a.out, respectively. 1446 func ArchChar(goarch string) (string, error) { 1447 return "?", errors.New("architecture letter no longer used") 1448 }