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