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.
28 // A Context specifies the supporting context for a build.
30 GOARCH string // target architecture
31 GOOS string // target operating system
32 GOROOT string // Go root
33 GOPATH string // Go path
34 CgoEnabled bool // whether cgo can be used
35 BuildTags []string // additional tags to recognize in +build lines
36 InstallTag string // package install directory suffix
37 UseAllFiles bool // use files regardless of +build lines, file names
38 Compiler string // compiler to assume when computing target paths
40 // By default, Import uses the operating system's file system calls
41 // to read directories and files. To read from other sources,
42 // callers can set the following functions. They all have default
43 // behaviors that use the local file system, so clients need only set
44 // the functions whose behaviors they wish to change.
46 // JoinPath joins the sequence of path fragments into a single path.
47 // If JoinPath is nil, Import uses filepath.Join.
48 JoinPath func(elem ...string) string
50 // SplitPathList splits the path list into a slice of individual paths.
51 // If SplitPathList is nil, Import uses filepath.SplitList.
52 SplitPathList func(list string) []string
54 // IsAbsPath reports whether path is an absolute path.
55 // If IsAbsPath is nil, Import uses filepath.IsAbs.
56 IsAbsPath func(path string) bool
58 // IsDir reports whether the path names a directory.
59 // If IsDir is nil, Import calls os.Stat and uses the result's IsDir method.
60 IsDir func(path string) bool
62 // HasSubdir reports whether dir is a subdirectory of
63 // (perhaps multiple levels below) root.
64 // If so, HasSubdir sets rel to a slash-separated path that
65 // can be joined to root to produce a path equivalent to dir.
66 // If HasSubdir is nil, Import uses an implementation built on
67 // filepath.EvalSymlinks.
68 HasSubdir func(root, dir string) (rel string, ok bool)
70 // ReadDir returns a slice of os.FileInfo, sorted by Name,
71 // describing the content of the named directory.
72 // If ReadDir is nil, Import uses ioutil.ReadDir.
73 ReadDir func(dir string) (fi []os.FileInfo, err error)
75 // OpenFile opens a file (not a directory) for reading.
76 // If OpenFile is nil, Import uses os.Open.
77 OpenFile func(path string) (r io.ReadCloser, err error)
80 // joinPath calls ctxt.JoinPath (if not nil) or else filepath.Join.
81 func (ctxt *Context) joinPath(elem ...string) string {
82 if f := ctxt.JoinPath; f != nil {
85 return filepath.Join(elem...)
88 // splitPathList calls ctxt.SplitPathList (if not nil) or else filepath.SplitList.
89 func (ctxt *Context) splitPathList(s string) []string {
90 if f := ctxt.SplitPathList; f != nil {
93 return filepath.SplitList(s)
96 // isAbsPath calls ctxt.IsAbsSPath (if not nil) or else filepath.IsAbs.
97 func (ctxt *Context) isAbsPath(path string) bool {
98 if f := ctxt.IsAbsPath; f != nil {
101 return filepath.IsAbs(path)
104 // isDir calls ctxt.IsDir (if not nil) or else uses os.Stat.
105 func (ctxt *Context) isDir(path string) bool {
106 if f := ctxt.IsDir; f != nil {
109 fi, err := os.Stat(path)
110 return err == nil && fi.IsDir()
113 // hasSubdir calls ctxt.HasSubdir (if not nil) or else uses
114 // the local file system to answer the question.
115 func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) {
116 if f := ctxt.HasSubdir; f != nil {
120 if p, err := filepath.EvalSymlinks(root); err == nil {
123 if p, err := filepath.EvalSymlinks(dir); err == nil {
126 const sep = string(filepath.Separator)
127 root = filepath.Clean(root)
128 if !strings.HasSuffix(root, sep) {
131 dir = filepath.Clean(dir)
132 if !strings.HasPrefix(dir, root) {
135 return filepath.ToSlash(dir[len(root):]), true
138 // readDir calls ctxt.ReadDir (if not nil) or else ioutil.ReadDir.
139 func (ctxt *Context) readDir(path string) ([]os.FileInfo, error) {
140 if f := ctxt.ReadDir; f != nil {
143 return ioutil.ReadDir(path)
146 // openFile calls ctxt.OpenFile (if not nil) or else os.Open.
147 func (ctxt *Context) openFile(path string) (io.ReadCloser, error) {
148 if fn := ctxt.OpenFile; fn != nil {
152 f, err := os.Open(path)
154 return nil, err // nil interface
159 // isFile determines whether path is a file by trying to open it.
160 // It reuses openFile instead of adding another function to the
162 func (ctxt *Context) isFile(path string) bool {
163 f, err := ctxt.openFile(path)
171 // gopath returns the list of Go path directories.
172 func (ctxt *Context) gopath() []string {
174 for _, p := range ctxt.splitPathList(ctxt.GOPATH) {
175 if p == "" || p == ctxt.GOROOT {
176 // Empty paths are uninteresting.
177 // If the path is the GOROOT, ignore it.
178 // People sometimes set GOPATH=$GOROOT, which is useless
179 // but would cause us to find packages with import paths
181 // Do not get confused by this common mistake.
189 // SrcDirs returns a list of package source root directories.
190 // It draws from the current Go root and Go path but omits directories
191 // that do not exist.
192 func (ctxt *Context) SrcDirs() []string {
194 if ctxt.GOROOT != "" {
195 dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg")
197 all = append(all, dir)
200 for _, p := range ctxt.gopath() {
201 dir := ctxt.joinPath(p, "src")
203 all = append(all, dir)
209 // Default is the default Context for builds.
210 // It uses the GOARCH, GOOS, GOROOT, and GOPATH environment variables
211 // if set, or else the compiled code's GOARCH, GOOS, and GOROOT.
212 var Default Context = defaultContext()
214 var cgoEnabled = map[string]bool{
216 "darwin/amd64": true,
218 "freebsd/amd64": true,
223 "netbsd/amd64": true,
225 "openbsd/amd64": true,
227 "windows/amd64": true,
230 func defaultContext() Context {
233 c.GOARCH = envOr("GOARCH", runtime.GOARCH)
234 c.GOOS = envOr("GOOS", runtime.GOOS)
235 c.GOROOT = runtime.GOROOT()
236 c.GOPATH = envOr("GOPATH", "")
237 c.Compiler = runtime.Compiler
239 switch os.Getenv("CGO_ENABLED") {
245 c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]
251 func envOr(name, def string) string {
259 // An ImportMode controls the behavior of the Import method.
263 // If FindOnly is set, Import stops after locating the directory
264 // that should contain the sources for a package. It does not
265 // read any files in the directory.
266 FindOnly ImportMode = 1 << iota
268 // If AllowBinary is set, Import can be satisfied by a compiled
269 // package object without corresponding sources.
273 // A Package describes the Go package found in a directory.
274 type Package struct {
275 Dir string // directory containing package sources
276 Name string // package name
277 Doc string // documentation synopsis
278 ImportPath string // import path of package ("" if unknown)
279 Root string // root of Go tree where this package lives
280 SrcRoot string // package source root directory ("" if unknown)
281 PkgRoot string // package install root directory ("" if unknown)
282 BinDir string // command install directory ("" if unknown)
283 Goroot bool // package found in Go root
284 PkgObj string // installed .a file
287 GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
288 CgoFiles []string // .go source files that import "C"
289 CFiles []string // .c source files
290 HFiles []string // .h source files
291 SFiles []string // .s source files
292 SysoFiles []string // .syso system object files to add to archive
293 SwigFiles []string // .swig files
294 SwigCXXFiles []string // .swigcxx files
297 CgoPkgConfig []string // Cgo pkg-config directives
298 CgoCFLAGS []string // Cgo CFLAGS directives
299 CgoLDFLAGS []string // Cgo LDFLAGS directives
301 // Dependency information
302 Imports []string // imports from GoFiles, CgoFiles
303 ImportPos map[string][]token.Position // line information for Imports
306 TestGoFiles []string // _test.go files in package
307 TestImports []string // imports from TestGoFiles
308 TestImportPos map[string][]token.Position // line information for TestImports
309 XTestGoFiles []string // _test.go files outside package
310 XTestImports []string // imports from XTestGoFiles
311 XTestImportPos map[string][]token.Position // line information for XTestImports
314 // IsCommand reports whether the package is considered a
315 // command to be installed (not just a library).
316 // Packages named "main" are treated as commands.
317 func (p *Package) IsCommand() bool {
318 return p.Name == "main"
321 // ImportDir is like Import but processes the Go package found in
322 // the named directory.
323 func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) {
324 return ctxt.Import(".", dir, mode)
327 // NoGoError is the error used by Import to describe a directory
328 // containing no Go source files.
329 type NoGoError struct {
333 func (e *NoGoError) Error() string {
334 return "no Go source files in " + e.Dir
337 // Import returns details about the Go package named by the import path,
338 // interpreting local import paths relative to the srcDir directory.
339 // If the path is a local import path naming a package that can be imported
340 // using a standard import path, the returned package will set p.ImportPath
343 // In the directory containing the package, .go, .c, .h, and .s files are
344 // considered part of the package except for:
346 // - .go files in package documentation
347 // - files starting with _ or . (likely editor temporary files)
348 // - files with build constraints not satisfied by the context
350 // If an error occurs, Import returns a non-nil error and a non-nil
351 // *Package containing partial information.
353 func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) {
358 return p, fmt.Errorf("import %q: invalid import path", path)
363 switch ctxt.Compiler {
365 dir, elem := pathpkg.Split(p.ImportPath)
366 pkga = "pkg/gccgo/" + dir + "lib" + elem + ".a"
369 if ctxt.InstallTag != "" {
370 tag = "_" + ctxt.InstallTag
372 pkga = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + tag + "/" + p.ImportPath + ".a"
374 // Save error for end of function.
375 pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler)
379 if IsLocalImport(path) {
380 pkga = "" // local imports have no installed path
382 return p, fmt.Errorf("import %q: import relative to unknown directory", path)
384 if !ctxt.isAbsPath(path) {
385 p.Dir = ctxt.joinPath(srcDir, path)
387 // Determine canonical import path, if any.
388 if ctxt.GOROOT != "" {
389 root := ctxt.joinPath(ctxt.GOROOT, "src", "pkg")
390 if sub, ok := ctxt.hasSubdir(root, p.Dir); ok {
398 for i, root := range all {
399 rootsrc := ctxt.joinPath(root, "src")
400 if sub, ok := ctxt.hasSubdir(rootsrc, p.Dir); ok {
401 // We found a potential import path for dir,
402 // but check that using it wouldn't find something
404 if ctxt.GOROOT != "" {
405 if dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", sub); ctxt.isDir(dir) {
409 for _, earlyRoot := range all[:i] {
410 if dir := ctxt.joinPath(earlyRoot, "src", sub); ctxt.isDir(dir) {
415 // sub would not name some other directory instead of this one.
422 // It's okay that we didn't find a root containing dir.
423 // Keep going with the information we have.
425 if strings.HasPrefix(path, "/") {
426 return p, fmt.Errorf("import %q: cannot import absolute path", path)
429 // tried records the location of unsucsessful package lookups
435 // Determine directory from import path.
436 if ctxt.GOROOT != "" {
437 dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", path)
438 isDir := ctxt.isDir(dir)
439 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
440 if isDir || binaryOnly {
448 for _, root := range ctxt.gopath() {
449 dir := ctxt.joinPath(root, "src", path)
450 isDir := ctxt.isDir(dir)
451 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(root, pkga))
452 if isDir || binaryOnly {
457 tried.gopath = append(tried.gopath, dir)
460 // package was not found
462 if tried.goroot != "" {
463 paths = append(paths, fmt.Sprintf("\t%s (from $GOROOT)", tried.goroot))
465 paths = append(paths, "\t($GOROOT not set)")
468 var format = "\t%s (from $GOPATH)"
469 for ; i < len(tried.gopath); i++ {
473 paths = append(paths, fmt.Sprintf(format, tried.gopath[i]))
476 paths = append(paths, "\t($GOPATH not set)")
478 return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, strings.Join(paths, "\n"))
484 p.SrcRoot = ctxt.joinPath(p.Root, "src", "pkg")
486 p.SrcRoot = ctxt.joinPath(p.Root, "src")
488 p.PkgRoot = ctxt.joinPath(p.Root, "pkg")
489 p.BinDir = ctxt.joinPath(p.Root, "bin")
491 p.PkgObj = ctxt.joinPath(p.Root, pkga)
495 if mode&FindOnly != 0 {
498 if binaryOnly && (mode&AllowBinary) != 0 {
502 dirs, err := ctxt.readDir(p.Dir)
507 var Sfiles []string // files with ".S" (capital S)
509 imported := make(map[string][]token.Position)
510 testImported := make(map[string][]token.Position)
511 xTestImported := make(map[string][]token.Position)
512 fset := token.NewFileSet()
513 for _, d := range dirs {
518 if strings.HasPrefix(name, "_") ||
519 strings.HasPrefix(name, ".") {
522 if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) {
526 i := strings.LastIndex(name, ".")
532 case ".go", ".c", ".s", ".h", ".S", ".swig", ".swigcxx":
533 // tentatively okay - read to make sure
535 // binary objects to add to package archive
536 // Likely of the form foo_windows.syso, but
537 // the name was vetted above with goodOSArchFile.
538 p.SysoFiles = append(p.SysoFiles, name)
545 filename := ctxt.joinPath(p.Dir, name)
546 f, err := ctxt.openFile(filename)
552 if strings.HasSuffix(filename, ".go") {
553 data, err = readImports(f, false)
555 data, err = readComments(f)
559 return p, fmt.Errorf("read %s: %v", filename, err)
562 // Look for +build comments to accept or reject the file.
563 if !ctxt.UseAllFiles && !ctxt.shouldBuild(data) {
567 // Going to save the file. For non-Go files, can stop here.
570 p.CFiles = append(p.CFiles, name)
573 p.HFiles = append(p.HFiles, name)
576 p.SFiles = append(p.SFiles, name)
579 Sfiles = append(Sfiles, name)
582 p.SwigFiles = append(p.SwigFiles, name)
585 p.SwigCXXFiles = append(p.SwigCXXFiles, name)
589 pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments)
595 if pkg == "documentation" {
599 isTest := strings.HasSuffix(name, "_test.go")
601 if isTest && strings.HasSuffix(pkg, "_test") {
603 pkg = pkg[:len(pkg)-len("_test")]
609 } else if pkg != p.Name {
610 return p, fmt.Errorf("found packages %s (%s) and %s (%s) in %s", p.Name, firstFile, pkg, name, p.Dir)
612 if pf.Doc != nil && p.Doc == "" {
613 p.Doc = doc.Synopsis(pf.Doc.Text())
616 // Record imports and information about cgo.
618 for _, decl := range pf.Decls {
619 d, ok := decl.(*ast.GenDecl)
623 for _, dspec := range d.Specs {
624 spec, ok := dspec.(*ast.ImportSpec)
628 quoted := spec.Path.Value
629 path, err := strconv.Unquote(quoted)
631 log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted)
634 xTestImported[path] = append(xTestImported[path], fset.Position(spec.Pos()))
636 testImported[path] = append(testImported[path], fset.Position(spec.Pos()))
638 imported[path] = append(imported[path], fset.Position(spec.Pos()))
642 return p, fmt.Errorf("use of cgo in test %s not supported", filename)
645 if cg == nil && len(d.Specs) == 1 {
649 if err := ctxt.saveCgo(filename, p, cg); err != nil {
659 p.CgoFiles = append(p.CgoFiles, name)
662 p.XTestGoFiles = append(p.XTestGoFiles, name)
664 p.TestGoFiles = append(p.TestGoFiles, name)
666 p.GoFiles = append(p.GoFiles, name)
670 return p, &NoGoError{p.Dir}
673 p.Imports, p.ImportPos = cleanImports(imported)
674 p.TestImports, p.TestImportPos = cleanImports(testImported)
675 p.XTestImports, p.XTestImportPos = cleanImports(xTestImported)
677 // add the .S files only if we are using cgo
678 // (which means gcc will compile them).
679 // The standard assemblers expect .s files.
680 if len(p.CgoFiles) > 0 {
681 p.SFiles = append(p.SFiles, Sfiles...)
682 sort.Strings(p.SFiles)
688 func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) {
689 all := make([]string, 0, len(m))
690 for path := range m {
691 all = append(all, path)
697 // Import is shorthand for Default.Import.
698 func Import(path, srcDir string, mode ImportMode) (*Package, error) {
699 return Default.Import(path, srcDir, mode)
702 // ImportDir is shorthand for Default.ImportDir.
703 func ImportDir(dir string, mode ImportMode) (*Package, error) {
704 return Default.ImportDir(dir, mode)
707 var slashslash = []byte("//")
709 // shouldBuild reports whether it is okay to use this file,
710 // The rule is that in the file's leading run of // comments
711 // and blank lines, which must be followed by a blank line
712 // (to avoid including a Go package clause doc comment),
713 // lines beginning with '// +build' are taken as build directives.
715 // The file is accepted only if each such line lists something
716 // matching the file. For example:
718 // // +build windows linux
720 // marks the file as applicable only on Windows and Linux.
722 func (ctxt *Context) shouldBuild(content []byte) bool {
723 // Pass 1. Identify leading run of // comments and blank lines,
724 // which must be followed by a blank line.
729 if i := bytes.IndexByte(line, '\n'); i >= 0 {
730 line, p = line[:i], p[i+1:]
734 line = bytes.TrimSpace(line)
735 if len(line) == 0 { // Blank line
736 end = len(content) - len(p)
739 if !bytes.HasPrefix(line, slashslash) { // Not comment line
743 content = content[:end]
745 // Pass 2. Process each line in the run.
749 if i := bytes.IndexByte(line, '\n'); i >= 0 {
750 line, p = line[:i], p[i+1:]
754 line = bytes.TrimSpace(line)
755 if bytes.HasPrefix(line, slashslash) {
756 line = bytes.TrimSpace(line[len(slashslash):])
757 if len(line) > 0 && line[0] == '+' {
758 // Looks like a comment +line.
759 f := strings.Fields(string(line))
760 if f[0] == "+build" {
762 for _, tok := range f[1:] {
769 return false // this one doesn't match
775 return true // everything matches
778 // saveCgo saves the information from the #cgo lines in the import "C" comment.
779 // These lines set CFLAGS and LDFLAGS and pkg-config directives that affect
780 // the way cgo's C code is built.
782 // TODO(rsc): This duplicates code in cgo.
783 // Once the dust settles, remove this code from cgo.
784 func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error {
786 for _, line := range strings.Split(text, "\n") {
790 // #cgo [GOOS/GOARCH...] LDFLAGS: stuff
792 line = strings.TrimSpace(line)
793 if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') {
798 line = strings.TrimSpace(line[4:])
799 i := strings.Index(line, ":")
801 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
803 line, argstr := line[:i], line[i+1:]
805 // Parse GOOS/GOARCH stuff.
806 f := strings.Fields(line)
808 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
811 cond, verb := f[:len(f)-1], f[len(f)-1]
814 for _, c := range cond {
825 args, err := splitQuoted(argstr)
827 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
829 for _, arg := range args {
831 return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
837 di.CgoCFLAGS = append(di.CgoCFLAGS, args...)
839 di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...)
841 di.CgoPkgConfig = append(di.CgoPkgConfig, args...)
843 return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig)
849 var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:")
851 func safeName(s string) bool {
855 for i := 0; i < len(s); i++ {
856 if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 {
863 // splitQuoted splits the string s around each instance of one or more consecutive
864 // white space characters while taking into account quotes and escaping, and
865 // returns an array of substrings of s or an empty list if s contains only white space.
866 // Single quotes and double quotes are recognized to prevent splitting within the
867 // quoted region, and are removed from the resulting substrings. If a quote in s
868 // isn't closed err will be set and r will have the unclosed argument as the
869 // last element. The backslash is used for escaping.
871 // For example, the following string:
873 // a b:"c d" 'e''f' "g\""
875 // Would be parsed as:
877 // []string{"a", "b:c d", "ef", `g"`}
879 func splitQuoted(s string) (r []string, err error) {
881 arg := make([]rune, len(s))
886 for _, rune := range s {
893 case quote != '\x00':
898 case rune == '"' || rune == '\'':
902 case unicode.IsSpace(rune):
905 args = append(args, string(arg[:i]))
914 args = append(args, string(arg[:i]))
917 err = errors.New("unclosed quote")
919 err = errors.New("unfinished escaping")
924 // match returns true if the name is one of:
928 // cgo (if cgo is enabled)
929 // !cgo (if cgo is disabled)
932 // tag (if tag is listed in ctxt.BuildTags)
933 // !tag (if tag is not listed in ctxt.BuildTags)
934 // a comma-separated list of any of these
936 func (ctxt *Context) match(name string) bool {
940 if i := strings.Index(name, ","); i >= 0 {
941 // comma-separated list
942 return ctxt.match(name[:i]) && ctxt.match(name[i+1:])
944 if strings.HasPrefix(name, "!!") { // bad syntax, reject always
947 if strings.HasPrefix(name, "!") { // negation
948 return len(name) > 1 && !ctxt.match(name[1:])
951 // Tags must be letters, digits, underscores.
952 // Unlike in Go identifiers, all digits are fine (e.g., "386").
953 for _, c := range name {
954 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' {
960 if ctxt.CgoEnabled && name == "cgo" {
963 if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler {
968 for _, tag := range ctxt.BuildTags {
977 // goodOSArchFile returns false if the name contains a $GOOS or $GOARCH
978 // suffix which does not match the current system.
979 // The recognized name formats are:
983 // name_$(GOOS)_$(GOARCH).*
984 // name_$(GOOS)_test.*
985 // name_$(GOARCH)_test.*
986 // name_$(GOOS)_$(GOARCH)_test.*
988 func (ctxt *Context) goodOSArchFile(name string) bool {
989 if dot := strings.Index(name, "."); dot != -1 {
992 l := strings.Split(name, "_")
993 if n := len(l); n > 0 && l[n-1] == "test" {
997 if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
998 return l[n-2] == ctxt.GOOS && l[n-1] == ctxt.GOARCH
1000 if n >= 1 && knownOS[l[n-1]] {
1001 return l[n-1] == ctxt.GOOS
1003 if n >= 1 && knownArch[l[n-1]] {
1004 return l[n-1] == ctxt.GOARCH
1009 var knownOS = make(map[string]bool)
1010 var knownArch = make(map[string]bool)
1013 for _, v := range strings.Fields(goosList) {
1016 for _, v := range strings.Fields(goarchList) {
1021 // ToolDir is the directory containing build tools.
1022 var ToolDir = filepath.Join(runtime.GOROOT(), "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
1024 // IsLocalImport reports whether the import path is
1025 // a local import path, like ".", "..", "./foo", or "../foo".
1026 func IsLocalImport(path string) bool {
1027 return path == "." || path == ".." ||
1028 strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
1031 // ArchChar returns the architecture character for the given goarch.
1032 // For example, ArchChar("amd64") returns "6".
1033 func ArchChar(goarch string) (string, error) {
1042 return "", errors.New("unsupported GOARCH " + goarch)