github.com/rgonomic/rgo@v0.2.2-0.20220708095818-4747f0905d6e/internal/mod/module.go (about) 1 // Copyright ©2019 The rgonomic Authors. All rights reserved. 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 mod 6 7 import ( 8 "bytes" 9 "encoding/json" 10 "errors" 11 "fmt" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "strings" 16 ) 17 18 // Module returns module information for the given directory path. 19 // If the path is in the standard library, the Info Path field will 20 // be GOROOT and Version will be the Go version. 21 func Module(path string) (Info, error) { 22 args := []string{"list", "-json"} 23 if path == "." { 24 args = append(args, "-m") 25 } else { 26 args = append(args, path) 27 } 28 cmd := exec.Command("go", args...) 29 var buf, errbuf bytes.Buffer 30 cmd.Stdout = &buf 31 cmd.Stderr = &errbuf 32 err := cmd.Run() 33 if err != nil { 34 stderr := strings.TrimSpace(errbuf.String()) 35 if stderr == "" { 36 return Info{}, err 37 } 38 return Info{}, fmt.Errorf("%s: %w", stderr, err) 39 } 40 var m struct { 41 Module Info 42 Standard bool 43 } 44 err = json.Unmarshal(buf.Bytes(), &m) 45 if err != nil { 46 return Info{}, err 47 } 48 if !m.Standard { 49 return m.Module, nil 50 } 51 52 buf.Reset() 53 errbuf.Reset() 54 cmd = exec.Command("go", "env", "GOROOT") 55 cmd.Stdout = &buf 56 cmd.Stderr = &errbuf 57 err = cmd.Run() 58 if err != nil { 59 stderr := strings.TrimSpace(errbuf.String()) 60 if stderr == "" { 61 return Info{}, err 62 } 63 return Info{}, fmt.Errorf("%s: %w", stderr, err) 64 } 65 root := strings.TrimSpace(buf.String()) 66 info := Info{ 67 Path: filepath.Join(root, "src", path), 68 Dir: root, 69 } 70 71 buf.Reset() 72 errbuf.Reset() 73 cmd = exec.Command("go", "version") 74 cmd.Stdout = &buf 75 cmd.Stderr = &errbuf 76 err = cmd.Run() 77 if err != nil { 78 stderr := strings.TrimSpace(errbuf.String()) 79 if stderr == "" { 80 return Info{}, err 81 } 82 return Info{}, fmt.Errorf("%s: %w", stderr, err) 83 } 84 version := strings.Fields(strings.TrimPrefix(buf.String(), "go version go"))[0] 85 info.Version = "v" + version 86 87 return info, nil 88 } 89 90 // Info holds module information. It is a subset of the information 91 // returned by "go list -json" in the Module struct. See go help list 92 // for details. 93 type Info struct { 94 Path string // module path 95 Version string // module version 96 Dir string // directory holding files for this module, if any 97 GoMod string // path to go.mod file for this module, if any 98 } 99 100 // Root returns the root directory of the module in the given path and 101 // whether a go.mod file can be found. It returns an error if the go 102 // tool is not running is module-aware mode. 103 func Root(path string) (root string, ok bool, err error) { 104 cmd := exec.Command("go", "env", "GOMOD") 105 cmd.Dir = path 106 var buf bytes.Buffer 107 cmd.Stdout = &buf 108 err = cmd.Run() 109 if err != nil { 110 return "", false, err 111 } 112 gomod := strings.TrimSpace(buf.String()) 113 if gomod == "" { 114 return "", false, errors.New("go tool not running in module mode") 115 } 116 if gomod == os.DevNull { 117 return path, false, nil 118 } 119 return filepath.Dir(gomod), true, nil 120 }