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