mirror of https://github.com/tinygo-org/tinygo.git
Browse Source
This makes it possible to query these environment variables from anywhere, which might be useful. More importantly, it puts them in a central location from where they can be queried, useful for a `go env` subcommand.pull/628/head
Ayke van Laethem
5 years ago
committed by
Ron Evans
7 changed files with 214 additions and 173 deletions
@ -0,0 +1,189 @@ |
|||
// Package goenv returns environment variables that are used in various parts of
|
|||
// the compiler. You can query it manually with the `tinygo env` subcommand.
|
|||
package goenv |
|||
|
|||
import ( |
|||
"fmt" |
|||
"os" |
|||
"os/exec" |
|||
"os/user" |
|||
"path/filepath" |
|||
"runtime" |
|||
) |
|||
|
|||
// Keys is a slice of all available environment variable keys.
|
|||
var Keys = []string{ |
|||
"GOOS", |
|||
"GOARCH", |
|||
"GOROOT", |
|||
"GOPATH", |
|||
"GOCACHE", |
|||
"TINYGOROOT", |
|||
} |
|||
|
|||
// TINYGOROOT is the path to the final location for checking tinygo files. If
|
|||
// unset (by a -X ldflag), then sourceDir() will fallback to the original build
|
|||
// directory.
|
|||
var TINYGOROOT string |
|||
|
|||
// Get returns a single environment variable, possibly calculating it on-demand.
|
|||
// The empty string is returned for unknown environment variables.
|
|||
func Get(name string) string { |
|||
switch name { |
|||
case "GOOS": |
|||
if dir := os.Getenv("GOOS"); dir != "" { |
|||
return dir |
|||
} |
|||
return runtime.GOOS |
|||
case "GOARCH": |
|||
if dir := os.Getenv("GOARCH"); dir != "" { |
|||
return dir |
|||
} |
|||
return runtime.GOARCH |
|||
case "GOROOT": |
|||
return getGoroot() |
|||
case "GOPATH": |
|||
if dir := os.Getenv("GOPATH"); dir != "" { |
|||
return dir |
|||
} |
|||
|
|||
// fallback
|
|||
home := getHomeDir() |
|||
return filepath.Join(home, "go") |
|||
case "GOCACHE": |
|||
// Get the cache directory, usually ~/.cache/tinygo
|
|||
dir, err := os.UserCacheDir() |
|||
if err != nil { |
|||
panic("could not find cache dir: " + err.Error()) |
|||
} |
|||
return filepath.Join(dir, "tinygo") |
|||
case "TINYGOROOT": |
|||
return sourceDir() |
|||
default: |
|||
return "" |
|||
} |
|||
} |
|||
|
|||
// Return the TINYGOROOT, or exit with an error.
|
|||
func sourceDir() string { |
|||
// Use $TINYGOROOT as root, if available.
|
|||
root := os.Getenv("TINYGOROOT") |
|||
if root != "" { |
|||
if !isSourceDir(root) { |
|||
fmt.Fprintln(os.Stderr, "error: $TINYGOROOT was not set to the correct root") |
|||
os.Exit(1) |
|||
} |
|||
return root |
|||
} |
|||
|
|||
if TINYGOROOT != "" { |
|||
if !isSourceDir(TINYGOROOT) { |
|||
fmt.Fprintln(os.Stderr, "error: TINYGOROOT was not set to the correct root") |
|||
os.Exit(1) |
|||
} |
|||
return TINYGOROOT |
|||
} |
|||
|
|||
// Find root from executable path.
|
|||
path, err := os.Executable() |
|||
if err != nil { |
|||
// Very unlikely. Bail out if it happens.
|
|||
panic("could not get executable path: " + err.Error()) |
|||
} |
|||
root = filepath.Dir(filepath.Dir(path)) |
|||
if isSourceDir(root) { |
|||
return root |
|||
} |
|||
|
|||
// Fallback: use the original directory from where it was built
|
|||
// https://stackoverflow.com/a/32163888/559350
|
|||
_, path, _, _ = runtime.Caller(0) |
|||
root = filepath.Dir(filepath.Dir(path)) |
|||
if isSourceDir(root) { |
|||
return root |
|||
} |
|||
|
|||
fmt.Fprintln(os.Stderr, "error: could not autodetect root directory, set the TINYGOROOT environment variable to override") |
|||
os.Exit(1) |
|||
panic("unreachable") |
|||
} |
|||
|
|||
// isSourceDir returns true if the directory looks like a TinyGo source directory.
|
|||
func isSourceDir(root string) bool { |
|||
_, err := os.Stat(filepath.Join(root, "src/runtime/internal/sys/zversion.go")) |
|||
if err != nil { |
|||
return false |
|||
} |
|||
_, err = os.Stat(filepath.Join(root, "src/device/arm/arm.go")) |
|||
return err == nil |
|||
} |
|||
|
|||
func getHomeDir() string { |
|||
u, err := user.Current() |
|||
if err != nil { |
|||
panic("cannot get current user: " + err.Error()) |
|||
} |
|||
if u.HomeDir == "" { |
|||
// This is very unlikely, so panic here.
|
|||
// Not the nicest solution, however.
|
|||
panic("could not find home directory") |
|||
} |
|||
return u.HomeDir |
|||
} |
|||
|
|||
// getGoroot returns an appropriate GOROOT from various sources. If it can't be
|
|||
// found, it returns an empty string.
|
|||
func getGoroot() string { |
|||
goroot := os.Getenv("GOROOT") |
|||
if goroot != "" { |
|||
// An explicitly set GOROOT always has preference.
|
|||
return goroot |
|||
} |
|||
|
|||
// Check for the location of the 'go' binary and base GOROOT on that.
|
|||
binpath, err := exec.LookPath("go") |
|||
if err == nil { |
|||
binpath, err = filepath.EvalSymlinks(binpath) |
|||
if err == nil { |
|||
goroot := filepath.Dir(filepath.Dir(binpath)) |
|||
if isGoroot(goroot) { |
|||
return goroot |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Check what GOROOT was at compile time.
|
|||
if isGoroot(runtime.GOROOT()) { |
|||
return runtime.GOROOT() |
|||
} |
|||
|
|||
// Check for some standard locations, as a last resort.
|
|||
var candidates []string |
|||
switch runtime.GOOS { |
|||
case "linux": |
|||
candidates = []string{ |
|||
"/usr/local/go", // manually installed
|
|||
"/usr/lib/go", // from the distribution
|
|||
} |
|||
case "darwin": |
|||
candidates = []string{ |
|||
"/usr/local/go", // manually installed
|
|||
"/usr/local/opt/go/libexec", // from Homebrew
|
|||
} |
|||
} |
|||
|
|||
for _, candidate := range candidates { |
|||
if isGoroot(candidate) { |
|||
return candidate |
|||
} |
|||
} |
|||
|
|||
// Can't find GOROOT...
|
|||
return "" |
|||
} |
|||
|
|||
// isGoroot checks whether the given path looks like a GOROOT.
|
|||
func isGoroot(goroot string) bool { |
|||
_, err := os.Stat(filepath.Join(goroot, "src", "runtime", "internal", "sys", "zversion.go")) |
|||
return err == nil |
|||
} |
Loading…
Reference in new issue