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 }