diff --git a/common/common.go b/common/common.go index b67f18709..be06b3a39 100644 --- a/common/common.go +++ b/common/common.go @@ -2,7 +2,16 @@ // See each sub-package for detail. package common -import "v2ray.com/core/common/errors" +import ( + "fmt" + "go/build" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "v2ray.com/core/common/errors" +) //go:generate errorgen @@ -28,3 +37,114 @@ func Must2(v interface{}, err error) interface{} { func Error2(v interface{}, err error) error { return err } + +// envFile returns the name of the Go environment configuration file. +// Copy from https://github.com/golang/go/blob/c4f2a9788a7be04daf931ac54382fbe2cb754938/src/cmd/go/internal/cfg/cfg.go#L150-L166 +func envFile() (string, error) { + if file := os.Getenv("GOENV"); file != "" { + if file == "off" { + return "", fmt.Errorf("GOENV=off") + } + return file, nil + } + dir, err := os.UserConfigDir() + if err != nil { + return "", err + } + if dir == "" { + return "", fmt.Errorf("missing user-config dir") + } + return filepath.Join(dir, "go", "env"), nil +} + +// GetRuntimeEnv returns the value of runtime environment variable, +// that is set by running following command: `go env -w key=value`. +func GetRuntimeEnv(key string) (string, error) { + file, err := envFile() + if err != nil { + return "", err + } + if file == "" { + return "", fmt.Errorf("missing runtime env file") + } + var data []byte + var runtimeEnv string + data, readErr := ioutil.ReadFile(file) + if readErr != nil { + return "", readErr + } + envStrings := strings.Split(string(data), "\n") + for _, envItem := range envStrings { + envItem = strings.TrimSuffix(envItem, "\r") + envKeyValue := strings.Split(envItem, "=") + if strings.EqualFold(strings.TrimSpace(envKeyValue[0]), key) { + runtimeEnv = strings.TrimSpace(envKeyValue[1]) + } + } + return runtimeEnv, nil +} + +// GetGOBIN returns GOBIN environment variable as a string. It will NOT be empty. +func GetGOBIN() string { + // The one set by user explicitly by `export GOBIN=/path` or `env GOBIN=/path command` + GOBIN := os.Getenv("GOBIN") + if GOBIN == "" { + var err error + // The one set by user by running `go env -w GOBIN=/path` + GOBIN, err = GetRuntimeEnv("GOBIN") + if err != nil { + // The default one that Golang uses + return filepath.Join(build.Default.GOPATH, "bin") + } + if GOBIN == "" { + return filepath.Join(build.Default.GOPATH, "bin") + } + return GOBIN + } + return GOBIN +} + +// GetGOPATH returns GOPATH environment variable as a string. It will NOT be empty. +func GetGOPATH() string { + // The one set by user explicitly by `export GOPATH=/path` or `env GOPATH=/path command` + GOPATH := os.Getenv("GOPATH") + if GOPATH == "" { + var err error + // The one set by user by running `go env -w GOPATH=/path` + GOPATH, err = GetRuntimeEnv("GOPATH") + if err != nil { + // The default one that Golang uses + return build.Default.GOPATH + } + if GOPATH == "" { + return build.Default.GOPATH + } + return GOPATH + } + return GOPATH +} + +// GetModuleName returns the value of module in `go.mod` file. +func GetModuleName(path string) (string, error) { + gomodPath := filepath.Join(path, "go.mod") + gomodBytes, err := ioutil.ReadFile(gomodPath) + if err != nil { + return "", err + } + gomodContent := string(gomodBytes) + moduleIdx := strings.Index(gomodContent, "module") + 6 + newLineIdx := strings.Index(gomodContent, "\n") + + var moduleName string + if moduleIdx >= 0 { + if newLineIdx >= 0 { + moduleName = strings.TrimSpace(gomodContent[moduleIdx:newLineIdx]) + moduleName = strings.TrimSuffix(moduleName, "\r") + } else { + moduleName = strings.TrimSpace(gomodContent[moduleIdx:]) + } + } else { + return "", fmt.Errorf("can not get the value of `module` in path `%s`", gomodPath) + } + return moduleName, nil +}