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  }