github.com/xmplusdev/xmcore@v1.8.11-0.20240412132628-5518b55526af/infra/vformat/main.go (about) 1 package main 2 3 import ( 4 "flag" 5 "fmt" 6 "go/build" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "runtime" 11 "strings" 12 ) 13 14 var directory = flag.String("pwd", "", "Working directory of Xray vformat.") 15 16 // envFile returns the name of the Go environment configuration file. 17 // Copy from https://github.com/golang/go/blob/c4f2a9788a7be04daf931ac54382fbe2cb754938/src/cmd/go/internal/cfg/cfg.go#L150-L166 18 func envFile() (string, error) { 19 if file := os.Getenv("GOENV"); file != "" { 20 if file == "off" { 21 return "", fmt.Errorf("GOENV=off") 22 } 23 return file, nil 24 } 25 dir, err := os.UserConfigDir() 26 if err != nil { 27 return "", err 28 } 29 if dir == "" { 30 return "", fmt.Errorf("missing user-config dir") 31 } 32 return filepath.Join(dir, "go", "env"), nil 33 } 34 35 // GetRuntimeEnv returns the value of runtime environment variable, 36 // that is set by running following command: `go env -w key=value`. 37 func GetRuntimeEnv(key string) (string, error) { 38 file, err := envFile() 39 if err != nil { 40 return "", err 41 } 42 if file == "" { 43 return "", fmt.Errorf("missing runtime env file") 44 } 45 var data []byte 46 var runtimeEnv string 47 data, readErr := os.ReadFile(file) 48 if readErr != nil { 49 return "", readErr 50 } 51 envStrings := strings.Split(string(data), "\n") 52 for _, envItem := range envStrings { 53 envItem = strings.TrimSuffix(envItem, "\r") 54 envKeyValue := strings.Split(envItem, "=") 55 if len(envKeyValue) == 2 && strings.TrimSpace(envKeyValue[0]) == key { 56 runtimeEnv = strings.TrimSpace(envKeyValue[1]) 57 } 58 } 59 return runtimeEnv, nil 60 } 61 62 // GetGOBIN returns GOBIN environment variable as a string. It will NOT be empty. 63 func GetGOBIN() string { 64 // The one set by user explicitly by `export GOBIN=/path` or `env GOBIN=/path command` 65 GOBIN := os.Getenv("GOBIN") 66 if GOBIN == "" { 67 var err error 68 // The one set by user by running `go env -w GOBIN=/path` 69 GOBIN, err = GetRuntimeEnv("GOBIN") 70 if err != nil { 71 // The default one that Golang uses 72 return filepath.Join(build.Default.GOPATH, "bin") 73 } 74 if GOBIN == "" { 75 return filepath.Join(build.Default.GOPATH, "bin") 76 } 77 return GOBIN 78 } 79 return GOBIN 80 } 81 82 func Run(binary string, args []string) ([]byte, error) { 83 cmd := exec.Command(binary, args...) 84 cmd.Env = append(cmd.Env, os.Environ()...) 85 output, cmdErr := cmd.CombinedOutput() 86 if cmdErr != nil { 87 return nil, cmdErr 88 } 89 return output, nil 90 } 91 92 func RunMany(binary string, args, files []string) { 93 fmt.Println("Processing...") 94 95 maxTasks := make(chan struct{}, runtime.NumCPU()) 96 for _, file := range files { 97 maxTasks <- struct{}{} 98 go func(file string) { 99 output, err := Run(binary, append(args, file)) 100 if err != nil { 101 fmt.Println(err) 102 } else if len(output) > 0 { 103 fmt.Println(string(output)) 104 } 105 <-maxTasks 106 }(file) 107 } 108 } 109 110 func main() { 111 flag.Usage = func() { 112 fmt.Fprintf(flag.CommandLine.Output(), "Usage of vformat:\n") 113 flag.PrintDefaults() 114 } 115 flag.Parse() 116 117 if !filepath.IsAbs(*directory) { 118 pwd, wdErr := os.Getwd() 119 if wdErr != nil { 120 fmt.Println("Can not get current working directory.") 121 os.Exit(1) 122 } 123 *directory = filepath.Join(pwd, *directory) 124 } 125 126 pwd := *directory 127 GOBIN := GetGOBIN() 128 binPath := os.Getenv("PATH") 129 pathSlice := []string{pwd, GOBIN, binPath} 130 binPath = strings.Join(pathSlice, string(os.PathListSeparator)) 131 os.Setenv("PATH", binPath) 132 133 suffix := "" 134 if runtime.GOOS == "windows" { 135 suffix = ".exe" 136 } 137 gofmt := "gofumpt" + suffix 138 goimports := "gci" + suffix 139 140 if gofmtPath, err := exec.LookPath(gofmt); err != nil { 141 fmt.Println("Can not find", gofmt, "in system path or current working directory.") 142 os.Exit(1) 143 } else { 144 gofmt = gofmtPath 145 } 146 147 if goimportsPath, err := exec.LookPath(goimports); err != nil { 148 fmt.Println("Can not find", goimports, "in system path or current working directory.") 149 os.Exit(1) 150 } else { 151 goimports = goimportsPath 152 } 153 154 rawFilesSlice := make([]string, 0, 1000) 155 walkErr := filepath.Walk(pwd, func(path string, info os.FileInfo, err error) error { 156 if err != nil { 157 fmt.Println(err) 158 return err 159 } 160 161 if info.IsDir() { 162 return nil 163 } 164 165 dir := filepath.Dir(path) 166 filename := filepath.Base(path) 167 if strings.HasSuffix(filename, ".go") && 168 !strings.HasSuffix(filename, ".pb.go") && 169 !strings.Contains(dir, filepath.Join("testing", "mocks")) && 170 !strings.Contains(path, filepath.Join("main", "distro", "all", "all.go")) { 171 rawFilesSlice = append(rawFilesSlice, path) 172 } 173 174 return nil 175 }) 176 if walkErr != nil { 177 fmt.Println(walkErr) 178 os.Exit(1) 179 } 180 181 gofmtArgs := []string{ 182 "-s", "-l", "-e", "-w", 183 } 184 185 goimportsArgs := []string{ 186 "write", 187 } 188 189 RunMany(gofmt, gofmtArgs, rawFilesSlice) 190 RunMany(goimports, goimportsArgs, rawFilesSlice) 191 fmt.Println("Do NOT forget to commit file changes.") 192 }