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  }