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