github.com/goplus/igop@v0.25.0/load/list.go (about) 1 /* 2 * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package load 18 19 import ( 20 "bytes" 21 "errors" 22 "fmt" 23 "os" 24 "os/exec" 25 "path/filepath" 26 "sort" 27 "strings" 28 ) 29 30 var ( 31 BuildMod string // BuildMod can be set readonly, vendor, or mod. 32 ) 33 34 // ListDriver implement (*igop.Context).Lookup use go list 35 type ListDriver struct { 36 init bool 37 root string 38 pkgs map[string]string // path -> dir 39 } 40 41 // Lookup implement (*igop.Context).Lookup 42 func (d *ListDriver) Lookup(root string, path string) (dir string, found bool) { 43 if !d.init || d.root != root { 44 d.init = true 45 d.root = root 46 err := d.Parse(root) 47 if err != nil { 48 fmt.Fprintln(os.Stderr, err) 49 } 50 } 51 dir, found = d.pkgs[path] 52 if found { 53 return 54 } 55 var list []string 56 for k := range d.pkgs { 57 if strings.HasPrefix(path, k+"/") { 58 list = append(list, k) 59 } 60 } 61 switch len(list) { 62 case 0: 63 case 1: 64 v := list[0] 65 dir, found = filepath.Join(d.pkgs[v], path[len(v+"/"):]), true 66 default: 67 // check path/v2 68 sort.Slice(list, func(i, j int) bool { 69 return list[i] > list[j] 70 }) 71 v := list[0] 72 dir, found = filepath.Join(d.pkgs[v], path[len(v+"/"):]), true 73 } 74 return 75 } 76 77 // Parse parse deps by go list 78 func (d *ListDriver) Parse(root string) error { 79 args := []string{"list", "-deps", "-e", "-f={{.ImportPath}}={{.Dir}}"} 80 if BuildMod != "" { 81 args = append(args, "-mod", BuildMod) 82 } 83 data, err := runGoCommand(root, args...) 84 if err != nil { 85 return err 86 } 87 d.pkgs = make(map[string]string) 88 for _, line := range strings.Split(string(data), "\n") { 89 pos := strings.Index(line, "=") 90 if pos != -1 { 91 d.pkgs[line[:pos]] = line[pos+1:] 92 } 93 } 94 return nil 95 } 96 97 func runGoCommand(dir string, args ...string) (ret []byte, err error) { 98 var stdout, stderr bytes.Buffer 99 cmd := exec.Command("go", args...) 100 cmd.Stdout = &stdout 101 cmd.Stderr = &stderr 102 cmd.Dir = dir 103 err = cmd.Run() 104 if err == nil { 105 ret = stdout.Bytes() 106 } else if stderr.Len() > 0 { 107 err = errors.New(stderr.String()) 108 } 109 return 110 }