Source file src/internal/goroot/gc.go

     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  
     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  		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.
    41  type 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.
    48  var gccgoSearch gccgoDirs
    49  
    50  // init finds the gccgo search directories. If this fails it leaves dirs == nil.
    51  func (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.
   107  func (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  

View as plain text