Files
gocp/tool_analyze_dependencies.go

132 lines
3.1 KiB
Go
Raw Normal View History

package main
import (
"go/ast"
"go/token"
"path/filepath"
"strings"
)
// Dependency analysis types
type DependencyInfo struct {
Package string `json:"package"`
Dir string `json:"dir"`
Dependencies []string `json:"dependencies"`
Dependents []string `json:"dependents,omitempty"`
Cycles [][]string `json:"cycles,omitempty"`
}
func analyzeDependencies(dir string) ([]DependencyInfo, error) {
depMap := make(map[string]*DependencyInfo)
// First pass: collect all packages and their imports
err := walkGoFiles(dir, func(path string, src []byte, file *ast.File, fset *token.FileSet) error {
pkgDir := filepath.Dir(path)
if _, exists := depMap[pkgDir]; !exists {
depMap[pkgDir] = &DependencyInfo{
Package: file.Name.Name,
Dir: pkgDir,
Dependencies: []string{},
}
}
// Add imports
for _, imp := range file.Imports {
importPath := strings.Trim(imp.Path.Value, `"`)
if !contains(depMap[pkgDir].Dependencies, importPath) {
depMap[pkgDir].Dependencies = append(depMap[pkgDir].Dependencies, importPath)
}
}
return nil
})
if err != nil {
return nil, err
}
// Build dependency graph and find cycles
var deps []DependencyInfo
for _, dep := range depMap {
// Find internal dependencies
for _, imp := range dep.Dependencies {
// Check if this is an internal package
for otherDir, otherDep := range depMap {
if strings.HasSuffix(imp, otherDep.Package) && otherDir != dep.Dir {
otherDep.Dependents = append(otherDep.Dependents, dep.Package)
}
}
}
deps = append(deps, *dep)
}
// Simple cycle detection (could be enhanced)
for i := range deps {
deps[i].Cycles = findCycles(&deps[i], depMap)
}
return deps, nil
}
func contains(slice []string, str string) bool {
for _, s := range slice {
if s == str {
return true
}
}
return false
}
func findCycles(dep *DependencyInfo, depMap map[string]*DependencyInfo) [][]string {
// Simple DFS-based cycle detection
var cycles [][]string
visited := make(map[string]bool)
recStack := make(map[string]bool)
path := []string{}
var dfs func(pkg string) bool
dfs = func(pkg string) bool {
visited[pkg] = true
recStack[pkg] = true
path = append(path, pkg)
// Find dependencies for this package
for _, d := range depMap {
if d.Package == pkg {
for _, imp := range d.Dependencies {
for _, otherDep := range depMap {
if strings.HasSuffix(imp, otherDep.Package) {
if !visited[otherDep.Package] {
if dfs(otherDep.Package) {
return true
}
} else if recStack[otherDep.Package] {
// Found a cycle
cycleStart := -1
for i, p := range path {
if p == otherDep.Package {
cycleStart = i
break
}
}
if cycleStart >= 0 {
cycle := append([]string{}, path[cycleStart:]...)
cycles = append(cycles, cycle)
}
return true
}
}
}
}
break
}
}
path = path[:len(path)-1]
recStack[pkg] = false
return false
}
dfs(dep.Package)
return cycles
}