github.com/bir3/gocompiler@v0.9.2202/gocompiler.go (about)

     1  // Copyright 2022 Bergur Ragnarsson.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gocompiler //syncpackage:
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"fmt"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  
    15          "github.com/bir3/gocompiler/extra" //syncimport: "$pkg/extra"
    16          "github.com/bir3/gocompiler/src/cmd/asm" //syncimport: "$pkg/src/$cmd/asm"
    17          "github.com/bir3/gocompiler/src/cmd/cgo" //syncimport: "$pkg/src/$cmd/cgo"
    18          "github.com/bir3/gocompiler/src/cmd/compile" //syncimport: "$pkg/src/$cmd/compile"
    19          "github.com/bir3/gocompiler/src/cmd/gocmd" //syncimport: "$pkg/src/$cmd/gocmd"
    20          "github.com/bir3/gocompiler/src/cmd/gofmt" //syncimport: "$pkg/src/$cmd/gofmt"
    21          "github.com/bir3/gocompiler/src/cmd/link" //syncimport: "$pkg/src/$cmd/link"
    22          "github.com/bir3/gocompiler/vfs" //syncimport: "$pkg/vfs"
    23  )
    24  
    25  type Info struct {
    26  	GoVersion string
    27  	CacheDir  string
    28  	GOROOT    string
    29  }
    30  
    31  func GetInfo() (Info, error) {
    32  	info := Info{}
    33  	info.GoVersion = GoVersion()
    34  	d, err := cacheDir()
    35  	if err != nil {
    36  		return Info{}, err
    37  	}
    38  	info.CacheDir = d
    39  	info.GOROOT, err = vfs.PrivateGOROOT()
    40  	if err != nil {
    41  		return Info{}, err
    42  	}
    43  	return info, nil
    44  }
    45  
    46  func IsRunToolchainRequest() bool {
    47  	return os.Getenv("BIR3_GOCOMPILER_TOOL") != ""
    48  }
    49  
    50  // adding extra executables : 44 MB -> 51 MB
    51  func RunToolchain() {
    52  	switch os.Getenv("BIR3_GOCOMPILER_TOOL") {
    53  	case "go":
    54  		gocmd.Main()
    55  	case "compile":
    56  		compile.Main()
    57  	case "asm":
    58  		asm.Main()
    59  	case "link":
    60  		link.Main()
    61  	case "cgo":
    62  		cgo.Main()
    63  	case "gofmt":
    64  		gofmt.Main()
    65  	case "debug-info":
    66  		info, err := GetInfo()
    67  		if err != nil {
    68  			fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
    69  		}
    70  		fmt.Printf("Embedded Go compiler github.com/bir3/gocompiler\n")
    71  		fmt.Printf("go-version    : %s\n", info.GoVersion)
    72  		fmt.Printf("cache-dir     : %s\n", info.CacheDir)
    73  		fmt.Printf("GOROOT/stdlib : %s\n", info.GOROOT)
    74  	default:
    75  		fmt.Fprintf(os.Stderr, "ERROR: unknown BIR3_GOCOMPILER_TOOL=%s\n", os.Getenv("BIR3_GOCOMPILER_TOOL"))
    76  		os.Exit(3)
    77  	}
    78  }
    79  
    80  type Result struct {
    81  	Stdout string
    82  	Stderr string
    83  }
    84  
    85  func cacheDir() (string, error) {
    86  	d, err := os.UserCacheDir()
    87  	if err != nil {
    88  		return "", err
    89  	}
    90  	d = filepath.Join(d, "bir3-gocompiler")
    91  
    92  	if !filepath.IsAbs(d) {
    93  		return "", fmt.Errorf("not absolute path: %s", d)
    94  	}
    95  
    96  	info, err := os.Stat(d)
    97  	if err == nil {
    98  		if info.IsDir() {
    99  			return d, nil
   100  		}
   101  		return "", fmt.Errorf("not a folder: %s", d)
   102  	}
   103  	// this runs only once
   104  	err = extra.MkdirAllRace(d, 0777)
   105  	if err != nil {
   106  		return "", nil
   107  	}
   108  	readme := `
   109  cache for github.com/bir3/gocompiler
   110  = Go compiler as a package
   111  = private Go build cache to avoid interfering with the standard Go toolchain build cache
   112  `
   113  	os.WriteFile(filepath.Join(d, "README-bir3"), []byte(readme), 0666)
   114  	return d, nil
   115  }
   116  
   117  func Command(env []string, args ...string) (*exec.Cmd, error) {
   118  	SharedExe, err := os.Executable()
   119  
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	if len(args) < 2 {
   124  		return nil, errors.New("too few arguments")
   125  	}
   126  	err = vfs.SetupStdlib() // no-op if already done
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  	privateCacheDir, err := cacheDir()
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	cmd := exec.Command(SharedExe, args[1:]...)
   135  
   136  	cmd.Env = make([]string, len(env), len(env)+10)
   137  	copy(cmd.Env, env)
   138  
   139  	goroot, err := vfs.PrivateGOROOT()
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	cmd.Env = append(cmd.Env, fmt.Sprintf("BIR3_GOCOMPILER_TOOL=%s", args[0]))
   144  	cmd.Env = append(cmd.Env, fmt.Sprintf("GOCACHE=%s", privateCacheDir))
   145  	cmd.Env = append(cmd.Env, fmt.Sprintf("GOROOT=%s", goroot))
   146  	return cmd, nil
   147  }
   148  
   149  func RunWithEnv(env []string, args ...string) (Result, error) {
   150  	var result Result
   151  
   152  	cmd, err := Command(env, args...)
   153  	if err != nil {
   154  		return result, err
   155  	}
   156  	var out bytes.Buffer
   157  	var outerr bytes.Buffer
   158  
   159  	cmd.Stdout = &out
   160  	cmd.Stderr = &outerr
   161  
   162  	err = cmd.Run()
   163  	result.Stdout = out.String()
   164  	result.Stderr = outerr.String()
   165  
   166  	if err != nil {
   167  		return result, err
   168  	}
   169  	return result, nil
   170  }
   171  
   172  func Run(args ...string) (Result, error) {
   173  	return RunWithEnv(os.Environ(), args...)
   174  }
   175  
   176  func GoVersion() string {
   177  	return "go1.22.0"
   178  }