Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 51 additions & 17 deletions lang/golang/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type GoParser struct {
files map[string][]byte
exclues []*regexp.Regexp
cgoPkgs map[string]bool // CGO packages
workDirs map[string]bool // directories that are in go.work scope
}

type moduleInfo struct {
Expand Down Expand Up @@ -97,10 +98,33 @@ func newGoParser(name string, homePageDir string, opts Options) *GoParser {
}

func (p *GoParser) collectGoMods(startDir string) error {
hasGoWork := false
var workFiles []string
err := filepath.Walk(startDir, func(path string, info fs.FileInfo, err error) error {
if err != nil || !strings.HasSuffix(path, "go.work") {
return nil
}
workFiles = append(workFiles, path)
return nil
})
if err != nil {
return err
}

p.workDirs = make(map[string]bool)
for _, workPath := range workFiles {
wf, err := parseGoWork(workPath)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to parse go.work file %s: %v\n", workPath, err)
continue
}
for _, useDir := range wf.UseDirs {
p.workDirs[useDir] = true
}
}
fmt.Printf("go work effective dirs: %v\n", p.workDirs)
deps := map[string]string{}
var cgoPkgs map[string]bool
err := filepath.Walk(startDir, func(path string, info fs.FileInfo, err error) error {
err = filepath.Walk(startDir, func(path string, info fs.FileInfo, err error) error {
if err != nil || !strings.HasSuffix(path, "go.mod") {
return nil
}
Expand All @@ -117,7 +141,7 @@ func (p *GoParser) collectGoMods(startDir string) error {
p.repo.Modules[name] = newModule(name, rel)
p.modules = append(p.modules, newModuleInfo(name, rel, name))

deps, hasGoWork, cgoPkgs, err = getDeps(filepath.Dir(path), hasGoWork)
deps, cgoPkgs, err = getDeps(filepath.Dir(path), p.workDirs)
if err != nil {
return err
}
Expand Down Expand Up @@ -161,31 +185,40 @@ type dep struct {
CgoFiles []string `json:"CgoFiles"`
}

func getDeps(dir string, goWork bool) (a map[string]string, hasGoWork bool, cgoPkgs map[string]bool, err error) {
func getDeps(dir string, workDirs map[string]bool) (a map[string]string, cgoPkgs map[string]bool, err error) {
cgoPkgs = make(map[string]bool)
// run go mod tidy first to ensure all dependencies are resolved
absDir, err := filepath.Abs(dir)
if err != nil {
return nil, cgoPkgs, fmt.Errorf("failed to get absolute path: %w", err)
}

inWorkSpace := false
for workDir := range workDirs {
if absDir == workDir || strings.HasPrefix(absDir, workDir+string(filepath.Separator)) {
inWorkSpace = true
break
}
}

cmd := exec.Command("go", "mod", "tidy", "-e")
cmd.Dir = dir
cmd.Env = append(os.Environ(), "GONOSUMDB=*")
cmd.Env = append(os.Environ(), "GONOSUMDB=*", "GOTOOLCHAIN=local")
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Fprintf(os.Stderr, "failed to execute 'go mod tidy', err: %v, output: %s, remove go.sum file reexecute\n", err, string(output))
os.Remove(filepath.Join(dir, "go.sum"))
cmd = exec.Command("go", "mod", "tidy", "-e")
cmd.Dir = dir
cmd.Env = append(os.Environ(), "GOSUMDB=off")
cmd.Env = append(os.Environ(), "GOSUMDB=off", "GOTOOLCHAIN=local")
output, err = cmd.CombinedOutput()
if err != nil {
return nil, hasGoWork, cgoPkgs, fmt.Errorf("failed to execute 'go mod tidy', err: %v, output: %s", err, string(output))
return nil, cgoPkgs, fmt.Errorf("failed to execute 'go mod tidy', err: %v, output: %s", err, string(output))
}
}
if hasNoDeps(filepath.Join(dir, "go.mod")) {
return map[string]string{}, hasGoWork, cgoPkgs, nil
return map[string]string{}, cgoPkgs, nil
}
// -mod=mod to use go mod when go mod is inconsistent with go vendor
// if go.work exist, it's no need to set -mod=mod
if _, err = os.Stat(filepath.Join(dir, "go.work")); err == nil || goWork {
hasGoWork = true
if inWorkSpace {
cmd = exec.Command("go", "list", "-e", "-json", "all")
} else {
cmd = exec.Command("go", "list", "-e", "-json", "-mod=mod", "all")
Expand All @@ -194,12 +227,12 @@ func getDeps(dir string, goWork bool) (a map[string]string, hasGoWork bool, cgoP
cmd.Env = append(os.Environ(), "GOSUMDB=off")
output, err = cmd.CombinedOutput()
if err != nil {
return nil, hasGoWork, cgoPkgs, fmt.Errorf("failed to execute 'go list -json all', err: %v, output: %s, cmd string: %s, dir: %s", err, string(output), cmd.String(), dir)
return nil, cgoPkgs, fmt.Errorf("failed to execute 'go list -json all', err: %v, output: %s, cmd string: %s, dir: %s", err, string(output), cmd.String(), dir)
}
// ignore content until first open
index := strings.Index(string(output), "{")
if index == -1 {
return nil, hasGoWork, cgoPkgs, fmt.Errorf("failed to find '{' in output, output: %s", string(output))
return nil, cgoPkgs, fmt.Errorf("failed to find '{' in output, output: %s", string(output))
}
if index > 0 {
log.Info("go list skip prefix, output: %s", string(output[:index]))
Expand All @@ -213,7 +246,7 @@ func getDeps(dir string, goWork bool) (a map[string]string, hasGoWork bool, cgoP
if err.Error() == "EOF" {
break
}
return nil, hasGoWork, cgoPkgs, fmt.Errorf("failed to decode json: %v, output: %s", err, string(output))
return nil, cgoPkgs, fmt.Errorf("failed to decode json: %v, output: %s", err, string(output))
}
module := mod.Module
// golang internal package, ignore it.
Expand All @@ -239,7 +272,7 @@ func getDeps(dir string, goWork bool) (a map[string]string, hasGoWork bool, cgoP
}
}
}
return deps, hasGoWork, cgoPkgs, nil
return deps, cgoPkgs, nil
}

// ParseRepo parse the entiry repo from homePageDir recursively until end
Expand All @@ -263,6 +296,7 @@ func (p *GoParser) ParseModule(mod *Module, dir string) (err error) {
// run go mod tidy before parse
cmd := exec.Command("go", "mod", "tidy")
cmd.Dir = dir
cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
buf := bytes.NewBuffer(nil)
cmd.Stderr = buf
cmd.Stdout = buf
Expand Down
55 changes: 55 additions & 0 deletions lang/golang/parser/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strings"
"sync"
Expand Down Expand Up @@ -319,3 +320,57 @@ func getCommitHash(dir string) (string, error) {
}
return strings.TrimSpace(string(output)), nil
}

type workFile struct {
Dir string
UseDirs []string
Replaces []workReplace
}

type workReplace struct {
OldPath string
NewPath string
}

func parseGoWork(workFilePath string) (*workFile, error) {
content, err := os.ReadFile(workFilePath)
if err != nil {
return nil, fmt.Errorf("failed to read go.work file: %w", err)
}

wf, err := modfile.ParseWork(workFilePath, content, nil)
if err != nil {
return nil, fmt.Errorf("failed to parse go.work file: %w", err)
}

workDir := filepath.Dir(workFilePath)
result := &workFile{
Dir: workDir,
UseDirs: make([]string, 0, len(wf.Use)),
Replaces: make([]workReplace, 0, len(wf.Replace)),
}

for _, use := range wf.Use {
if use.Path == "" {
continue
}
usePath := use.Path
if !filepath.IsAbs(usePath) {
usePath = filepath.Join(workDir, usePath)
}
absPath, err := filepath.Abs(usePath)
if err != nil {
continue
}
result.UseDirs = append(result.UseDirs, absPath)
}

for _, rep := range wf.Replace {
result.Replaces = append(result.Replaces, workReplace{
OldPath: rep.Old.Path,
NewPath: rep.New.Path,
})
}

return result, nil
}
Loading