소스 검색

feat(dep): add provider resolution on first layer (#2147)

add provider resolution on first layer
Jo 2 년 전
부모
커밋
966bfb74ee
5개의 변경된 파일201개의 추가작업 그리고 18개의 파일을 삭제
  1. 5 1
      pkg/db/mock/repo.go
  2. 48 15
      pkg/dep/dep_graph.go
  3. 114 0
      pkg/dep/dep_graph_test.go
  4. 3 0
      pkg/dep/testdata/android-sdk.json
  5. 31 2
      pkg/topo/dep.go

+ 5 - 1
pkg/db/mock/repo.go

@@ -38,6 +38,7 @@ type Package struct {
 	PVersion      string
 	PReason       alpm.PkgReason
 	PDepends      alpm.IDependList
+	PProvides     alpm.IDependList
 }
 
 func (p *Package) Base() string {
@@ -172,7 +173,10 @@ func (p *Package) Packager() string {
 
 // Provides returns DependList of packages provides by package.
 func (p *Package) Provides() alpm.IDependList {
-	return alpm.DependList{}
+	if p.PProvides == nil {
+		return alpm.DependList{}
+	}
+	return p.PProvides
 }
 
 // Origin returns package origin.

+ 48 - 15
pkg/dep/dep_graph.go

@@ -138,17 +138,6 @@ func (g *Grapher) GraphFromTargets(ctx context.Context,
 		case "": // unspecified db
 			if pkg := g.dbExecutor.SyncSatisfier(target.Name); pkg != nil {
 				dbName := pkg.DB().Name()
-				graph.AddNode(pkg.Name())
-				g.ValidateAndSetNodeInfo(graph, pkg.Name(), &topo.NodeInfo[*InstallInfo]{
-					Color:      colorMap[Explicit],
-					Background: bgColorMap[Sync],
-					Value: &InstallInfo{
-						Source:     Sync,
-						Reason:     Explicit,
-						Version:    pkg.Version(),
-						SyncDBName: &dbName,
-					},
-				})
 
 				g.GraphSyncPkg(ctx, graph, pkg, &InstallInfo{
 					Source:     Sync,
@@ -302,6 +291,12 @@ func (g *Grapher) GraphSyncPkg(ctx context.Context,
 	}
 
 	graph.AddNode(pkg.Name())
+	_ = pkg.Provides().ForEach(func(p *alpm.Depend) error {
+		g.logger.Debugln(pkg.Name() + " provides: " + p.String())
+		graph.Provides(p.Name, p, pkg.Name())
+		return nil
+	})
+
 	g.ValidateAndSetNodeInfo(graph, pkg.Name(), &topo.NodeInfo[*InstallInfo]{
 		Color:      colorMap[Explicit],
 		Background: bgColorMap[Sync],
@@ -322,6 +317,16 @@ func (g *Grapher) GraphAURTarget(ctx context.Context,
 	exists := graph.Exists(pkg.Name)
 
 	graph.AddNode(pkg.Name)
+
+	for i := range pkg.Provides {
+		depName, mod, version := splitDep(pkg.Provides[i])
+		graph.Provides(depName, &alpm.Depend{
+			Name:    depName,
+			Version: version,
+			Mod:     aurDepModToAlpmDep(mod),
+		}, pkg.Name)
+	}
+
 	g.ValidateAndSetNodeInfo(graph, pkg.Name, &topo.NodeInfo[*InstallInfo]{
 		Color:      colorMap[instalInfo.Reason],
 		Background: bgColorMap[AUR],
@@ -515,15 +520,27 @@ func (g *Grapher) addNodes(
 	// Check if in graph already
 	for _, depString := range targetsToFind.ToSlice() {
 		depName, _, _ := splitDep(depString)
-		if !graph.Exists(depName) {
+		if !graph.Exists(depName) && !graph.ProvidesExists(depName) {
 			continue
 		}
 
-		if err := graph.DependOn(depName, parentPkgName); err != nil {
-			g.logger.Warnln(depString, parentPkgName, err)
+		if graph.Exists(depName) {
+			if err := graph.DependOn(depName, parentPkgName); err != nil {
+				g.logger.Warnln(depString, parentPkgName, err)
+			}
+
+			targetsToFind.Remove(depString)
 		}
 
-		targetsToFind.Remove(depString)
+		if p := graph.GetProviderNode(depName); p != nil {
+			if provideSatisfies(p.String(), depString, p.Version) {
+				if err := graph.DependOn(p.Provider, parentPkgName); err != nil {
+					g.logger.Warnln(p.Provider, parentPkgName, err)
+				}
+
+				targetsToFind.Remove(depString)
+			}
+		}
 	}
 
 	// Check installed
@@ -745,3 +762,19 @@ func archStringToString(alpmArches []string, archString []gosrc.ArchString) []st
 
 	return pkgs
 }
+
+func aurDepModToAlpmDep(mod string) alpm.DepMod {
+	switch mod {
+	case "=":
+		return alpm.DepModEq
+	case ">=":
+		return alpm.DepModGE
+	case "<=":
+		return alpm.DepModLE
+	case ">":
+		return alpm.DepModGT
+	case "<":
+		return alpm.DepModLT
+	}
+	return alpm.DepModAny
+}

+ 114 - 0
pkg/dep/dep_graph_test.go

@@ -9,6 +9,7 @@ import (
 	"testing"
 
 	aurc "github.com/Jguer/aur"
+	alpm "github.com/Jguer/go-alpm/v2"
 	"github.com/stretchr/testify/require"
 
 	"github.com/Jguer/yay/v12/pkg/db"
@@ -205,3 +206,116 @@ func TestGrapher_GraphFromTargets_jellyfin(t *testing.T) {
 		})
 	}
 }
+
+func TestGrapher_GraphProvides_androidsdk(t *testing.T) {
+	mockDB := &mock.DBExecutor{
+		SyncPackageFn: func(string) mock.IPackage { return nil },
+		SyncSatisfierFn: func(s string) mock.IPackage {
+			switch s {
+			case "android-sdk":
+				return nil
+			case "jdk11-openjdk":
+				return &mock.Package{
+					PName:    "jdk11-openjdk",
+					PVersion: "11.0.12.u7-1",
+					PDB:      mock.NewDB("community"),
+					PProvides: mock.DependList{
+						Depends: []alpm.Depend{
+							{Name: "java-environment", Version: "11", Mod: alpm.DepModEq},
+							{Name: "java-environment-openjdk", Version: "11", Mod: alpm.DepModEq},
+							{Name: "jdk11-openjdk", Version: "11.0.19.u7-1", Mod: alpm.DepModEq},
+						},
+					},
+				}
+			case "java-environment":
+				panic("not supposed to be called")
+			}
+			panic("implement me " + s)
+		},
+		PackagesFromGroupFn: func(string) []mock.IPackage { return nil },
+		LocalSatisfierExistsFn: func(s string) bool {
+			switch s {
+			case "java-environment":
+				return false
+			}
+
+			switch s {
+			case "libxtst", "fontconfig", "freetype2", "lib32-gcc-libs", "lib32-glibc", "libx11", "libxext", "libxrender", "zlib", "gcc-libs":
+				return true
+			}
+
+			panic("implement me " + s)
+		},
+	}
+
+	mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
+		if query.Needles[0] == "android-sdk" {
+			jfinFn := getFromFile(t, "testdata/android-sdk.json")
+			return jfinFn(ctx, query)
+		}
+
+		panic(fmt.Sprintf("implement me %v", query.Needles))
+	}}
+
+	type fields struct {
+		dbExecutor  db.Executor
+		aurCache    aurc.QueryClient
+		noDeps      bool
+		noCheckDeps bool
+	}
+	type args struct {
+		targets []string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		want    []map[string]*InstallInfo
+		wantErr bool
+	}{
+		{
+			name: "explicit dep",
+			fields: fields{
+				dbExecutor:  mockDB,
+				aurCache:    mockAUR,
+				noDeps:      false,
+				noCheckDeps: false,
+			},
+			args: args{
+				targets: []string{"android-sdk", "jdk11-openjdk"},
+			},
+			want: []map[string]*InstallInfo{
+				{
+					"android-sdk": {
+						Source:  AUR,
+						Reason:  Explicit,
+						Version: "26.1.1-2",
+						AURBase: ptrString("android-sdk"),
+					},
+				},
+				{
+					"jdk11-openjdk": {
+						Source:     Sync,
+						Reason:     Explicit,
+						Version:    "11.0.12.u7-1",
+						SyncDBName: ptrString("community"),
+					},
+				},
+			},
+			wantErr: false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			g := NewGrapher(tt.fields.dbExecutor,
+				tt.fields.aurCache, false, true,
+				tt.fields.noDeps, tt.fields.noCheckDeps, false,
+				text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
+			got, err := g.GraphFromTargets(context.Background(), nil, tt.args.targets)
+			require.NoError(t, err)
+			layers := got.TopoSortedLayerMap(nil)
+			require.EqualValues(t, tt.want, layers, layers)
+		})
+	}
+}

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 3 - 0
pkg/dep/testdata/android-sdk.json


+ 31 - 2
pkg/topo/dep.go

@@ -4,12 +4,15 @@ import (
 	"fmt"
 	"strings"
 
+	"github.com/Jguer/go-alpm/v2"
+
 	"github.com/Jguer/yay/v12/pkg/text"
 )
 
 type (
-	NodeSet[T comparable] map[T]bool
-	DepMap[T comparable]  map[T]NodeSet[T]
+	NodeSet[T comparable]     map[T]bool
+	ProvidesMap[T comparable] map[T]*DependencyInfo[T]
+	DepMap[T comparable]      map[T]NodeSet[T]
 )
 
 func (n NodeSet[T]) Slice() []T {
@@ -28,6 +31,11 @@ type NodeInfo[V any] struct {
 	Value      V
 }
 
+type DependencyInfo[T comparable] struct {
+	Provider T
+	alpm.Depend
+}
+
 type CheckFn[T comparable, V any] func(T, V) error
 
 type Graph[T comparable, V any] struct {
@@ -36,6 +44,9 @@ type Graph[T comparable, V any] struct {
 	// node info map
 	nodeInfo map[T]*NodeInfo[V]
 
+	// `provides` tracks provides -> node.
+	provides ProvidesMap[T]
+
 	// `dependencies` tracks child -> parents.
 	dependencies DepMap[T]
 	// `dependents` tracks parent -> children.
@@ -48,6 +59,7 @@ func New[T comparable, V any]() *Graph[T, V] {
 		dependencies: make(DepMap[T]),
 		dependents:   make(DepMap[T]),
 		nodeInfo:     make(map[T]*NodeInfo[V]),
+		provides:     make(ProvidesMap[T]),
 	}
 }
 
@@ -65,6 +77,23 @@ func (g *Graph[T, V]) AddNode(node T) {
 	g.nodes[node] = true
 }
 
+func (g *Graph[T, V]) ProvidesExists(provides T) bool {
+	_, ok := g.provides[provides]
+
+	return ok
+}
+
+func (g *Graph[T, V]) GetProviderNode(provides T) *DependencyInfo[T] {
+	return g.provides[provides]
+}
+
+func (g *Graph[T, V]) Provides(provides T, depInfo *alpm.Depend, node T) {
+	g.provides[provides] = &DependencyInfo[T]{
+		Provider: node,
+		Depend:   *depInfo,
+	}
+}
+
 func (g *Graph[T, V]) ForEach(f CheckFn[T, V]) error {
 	for node := range g.nodes {
 		if err := f(node, g.nodeInfo[node].Value); err != nil {