132 lines
3.0 KiB
Go
132 lines
3.0 KiB
Go
// Copyright 2018 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// +build gc
|
|
|
|
package goroot
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// IsStandardPackage reports whether path is a standard package,
|
|
// given goroot and compiler.
|
|
func IsStandardPackage(goroot, compiler, path string) bool {
|
|
switch compiler {
|
|
case "gc":
|
|
dir := filepath.Join(goroot, "src", path)
|
|
_, err := os.Stat(dir)
|
|
return err == nil
|
|
case "gccgo":
|
|
return gccgoSearch.isStandard(path)
|
|
default:
|
|
panic("unknown compiler " + compiler)
|
|
}
|
|
}
|
|
|
|
// gccgoSearch holds the gccgo search directories.
|
|
type gccgoDirs struct {
|
|
once sync.Once
|
|
dirs []string
|
|
}
|
|
|
|
// gccgoSearch is used to check whether a gccgo package exists in the
|
|
// standard library.
|
|
var gccgoSearch gccgoDirs
|
|
|
|
// init finds the gccgo search directories. If this fails it leaves dirs == nil.
|
|
func (gd *gccgoDirs) init() {
|
|
gccgo := os.Getenv("GCCGO")
|
|
if gccgo == "" {
|
|
gccgo = "gccgo"
|
|
}
|
|
bin, err := exec.LookPath(gccgo)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
allDirs, err := exec.Command(bin, "-print-search-dirs").Output()
|
|
if err != nil {
|
|
return
|
|
}
|
|
versionB, err := exec.Command(bin, "-dumpversion").Output()
|
|
if err != nil {
|
|
return
|
|
}
|
|
version := strings.TrimSpace(string(versionB))
|
|
machineB, err := exec.Command(bin, "-dumpmachine").Output()
|
|
if err != nil {
|
|
return
|
|
}
|
|
machine := strings.TrimSpace(string(machineB))
|
|
|
|
dirsEntries := strings.Split(string(allDirs), "\n")
|
|
const prefix = "libraries: ="
|
|
var dirs []string
|
|
for _, dirEntry := range dirsEntries {
|
|
if strings.HasPrefix(dirEntry, prefix) {
|
|
dirs = filepath.SplitList(strings.TrimPrefix(dirEntry, prefix))
|
|
break
|
|
}
|
|
}
|
|
if len(dirs) == 0 {
|
|
return
|
|
}
|
|
|
|
var lastDirs []string
|
|
for _, dir := range dirs {
|
|
goDir := filepath.Join(dir, "go", version)
|
|
if fi, err := os.Stat(goDir); err == nil && fi.IsDir() {
|
|
gd.dirs = append(gd.dirs, goDir)
|
|
goDir = filepath.Join(goDir, machine)
|
|
if fi, err = os.Stat(goDir); err == nil && fi.IsDir() {
|
|
gd.dirs = append(gd.dirs, goDir)
|
|
}
|
|
}
|
|
if fi, err := os.Stat(dir); err == nil && fi.IsDir() {
|
|
lastDirs = append(lastDirs, dir)
|
|
}
|
|
}
|
|
gd.dirs = append(gd.dirs, lastDirs...)
|
|
}
|
|
|
|
// isStandard reports whether path is a standard library for gccgo.
|
|
func (gd *gccgoDirs) isStandard(path string) bool {
|
|
// Quick check: if the first path component has a '.', it's not
|
|
// in the standard library. This skips most GOPATH directories.
|
|
i := strings.Index(path, "/")
|
|
if i < 0 {
|
|
i = len(path)
|
|
}
|
|
if strings.Contains(path[:i], ".") {
|
|
return false
|
|
}
|
|
|
|
if path == "unsafe" {
|
|
// Special case.
|
|
return true
|
|
}
|
|
|
|
gd.once.Do(gd.init)
|
|
if gd.dirs == nil {
|
|
// We couldn't find the gccgo search directories.
|
|
// Best guess, since the first component did not contain
|
|
// '.', is that this is a standard library package.
|
|
return true
|
|
}
|
|
|
|
for _, dir := range gd.dirs {
|
|
full := filepath.Join(dir, path) + ".gox"
|
|
if fi, err := os.Stat(full); err == nil && !fi.IsDir() {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|