github.com/sandwichdev/go-internals@v0.0.0-20210605002614-12311ac6b2c5/goroot/gc.go (about) 1 // Copyright 2018 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 // +build gc 6 7 package goroot 8 9 import ( 10 "os" 11 "path/filepath" 12 "strings" 13 "sync" 14 15 exec "github.com/SandwichDev/go-internals/execabs" 16 ) 17 18 // IsStandardPackage reports whether path is a standard package, 19 // given goroot and compiler. 20 func IsStandardPackage(goroot, compiler, path string) bool { 21 switch compiler { 22 case "gc": 23 dir := filepath.Join(goroot, "src", path) 24 _, err := os.Stat(dir) 25 return err == nil 26 case "gccgo": 27 return gccgoSearch.isStandard(path) 28 default: 29 panic("unknown compiler " + compiler) 30 } 31 } 32 33 // gccgoSearch holds the gccgo search directories. 34 type gccgoDirs struct { 35 once sync.Once 36 dirs []string 37 } 38 39 // gccgoSearch is used to check whether a gccgo package exists in the 40 // standard library. 41 var gccgoSearch gccgoDirs 42 43 // init finds the gccgo search directories. If this fails it leaves dirs == nil. 44 func (gd *gccgoDirs) init() { 45 gccgo := os.Getenv("GCCGO") 46 if gccgo == "" { 47 gccgo = "gccgo" 48 } 49 bin, err := exec.LookPath(gccgo) 50 if err != nil { 51 return 52 } 53 54 allDirs, err := exec.Command(bin, "-print-search-dirs").Output() 55 if err != nil { 56 return 57 } 58 versionB, err := exec.Command(bin, "-dumpversion").Output() 59 if err != nil { 60 return 61 } 62 version := strings.TrimSpace(string(versionB)) 63 machineB, err := exec.Command(bin, "-dumpmachine").Output() 64 if err != nil { 65 return 66 } 67 machine := strings.TrimSpace(string(machineB)) 68 69 dirsEntries := strings.Split(string(allDirs), "\n") 70 const prefix = "libraries: =" 71 var dirs []string 72 for _, dirEntry := range dirsEntries { 73 if strings.HasPrefix(dirEntry, prefix) { 74 dirs = filepath.SplitList(strings.TrimPrefix(dirEntry, prefix)) 75 break 76 } 77 } 78 if len(dirs) == 0 { 79 return 80 } 81 82 var lastDirs []string 83 for _, dir := range dirs { 84 goDir := filepath.Join(dir, "go", version) 85 if fi, err := os.Stat(goDir); err == nil && fi.IsDir() { 86 gd.dirs = append(gd.dirs, goDir) 87 goDir = filepath.Join(goDir, machine) 88 if fi, err = os.Stat(goDir); err == nil && fi.IsDir() { 89 gd.dirs = append(gd.dirs, goDir) 90 } 91 } 92 if fi, err := os.Stat(dir); err == nil && fi.IsDir() { 93 lastDirs = append(lastDirs, dir) 94 } 95 } 96 gd.dirs = append(gd.dirs, lastDirs...) 97 } 98 99 // isStandard reports whether path is a standard library for gccgo. 100 func (gd *gccgoDirs) isStandard(path string) bool { 101 // Quick check: if the first path component has a '.', it's not 102 // in the standard library. This skips most GOPATH directories. 103 i := strings.Index(path, "/") 104 if i < 0 { 105 i = len(path) 106 } 107 if strings.Contains(path[:i], ".") { 108 return false 109 } 110 111 if path == "unsafe" { 112 // Special case. 113 return true 114 } 115 116 gd.once.Do(gd.init) 117 if gd.dirs == nil { 118 // We couldn't find the gccgo search directories. 119 // Best guess, since the first component did not contain 120 // '.', is that this is a standard library package. 121 return true 122 } 123 124 for _, dir := range gd.dirs { 125 full := filepath.Join(dir, path) + ".gox" 126 if fi, err := os.Stat(full); err == nil && !fi.IsDir() { 127 return true 128 } 129 } 130 131 return false 132 }