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  		info, err := os.Stat(dir)
    24  		return err == nil && info.IsDir()
    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) + ".gox"
   125  		if fi, err := os.Stat(full); err == nil && !fi.IsDir() {
   126  			return true
   127  		}
   128  	}
   129  
   130  	return false
   131  }
   132  

View as plain text