Skip to content

Commit

Permalink
refactor: the same traversing tripartite dependency logics are moved …
Browse files Browse the repository at this point in the history
…into the resolver

Signed-off-by: zongz <zongzhe1024@163.com>
  • Loading branch information
zong-zhe committed Nov 15, 2024
1 parent d591035 commit ca697c6
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 153 deletions.
38 changes: 6 additions & 32 deletions pkg/client/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ package client

import (
"fmt"
"path/filepath"

"github.com/dominikbraun/graph"
"golang.org/x/mod/module"
"kcl-lang.io/kpm/pkg/downloader"
pkg "kcl-lang.io/kpm/pkg/package"
"kcl-lang.io/kpm/pkg/resolver"
)
Expand Down Expand Up @@ -156,37 +154,13 @@ func (c *KpmClient) Graph(opts ...GraphOption) (*DepGraph, error) {
}
depResolver.ResolveFuncs = append(depResolver.ResolveFuncs, resolverFunc)

for _, depName := range modDeps.Keys() {
dep, ok := modDeps.Get(depName)
if !ok {
return nil, fmt.Errorf("failed to get dependency %s", depName)
}

// Check if the dependency is a local path and it is not an absolute path.
// If it is not an absolute path, transform the path to an absolute path.
var depSource *downloader.Source
if dep.Source.IsLocalPath() && !filepath.IsAbs(dep.Source.Local.Path) {
depSource = &downloader.Source{
Local: &downloader.Local{
Path: filepath.Join(kMod.HomePath, dep.Source.Local.Path),
},
}
} else {
depSource = &dep.Source
}
err := depResolver.Resolve(
resolver.WithEnableCache(true),
resolver.WithResolveKclMod(kMod),
)

err := resolverFunc(&dep, kMod)
if err != nil {
return nil, err
}

err = depResolver.Resolve(
resolver.WithEnableCache(true),
resolver.WithSource(depSource),
)
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}

return dGraph, nil
Expand Down
70 changes: 23 additions & 47 deletions pkg/client/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package client

import (
"fmt"
"path/filepath"

"kcl-lang.io/kpm/pkg/downloader"
pkg "kcl-lang.io/kpm/pkg/package"
"kcl-lang.io/kpm/pkg/resolver"
)
Expand Down Expand Up @@ -34,16 +32,16 @@ func (c *KpmClient) Update(options ...UpdateOption) (*pkg.KclPkg, error) {
}
}

kpkg := opts.kpkg
if kpkg == nil {
kMod := opts.kpkg
if kMod == nil {
return nil, fmt.Errorf("kcl package is nil")
}

modDeps := kpkg.ModFile.Dependencies.Deps
modDeps := kMod.ModFile.Dependencies.Deps
if modDeps == nil {
return nil, fmt.Errorf("kcl.mod dependencies is nil")
}
lockDeps := kpkg.Dependencies.Deps
lockDeps := kMod.Dependencies.Deps
if lockDeps == nil {
return nil, fmt.Errorf("kcl.mod.lock dependencies is nil")
}
Expand All @@ -58,72 +56,50 @@ func (c *KpmClient) Update(options ...UpdateOption) (*pkg.KclPkg, error) {
}
// ResolveFunc is the function for resolving each dependency when traversing the dependency graph.
resolverFunc := func(dep *pkg.Dependency, parentPkg *pkg.KclPkg) error {
selectedModDep := dep
// Check if the dependency exists in the mod file.
if existDep, exist := modDeps.Get(dep.Name); exist {
// if the dependency exists in the mod file,
// check the version and select the greater one.
if less, err := existDep.VersionLessThan(dep); less && err == nil {
kpkg.ModFile.Dependencies.Deps.Set(dep.Name, *dep)
if less, err := dep.VersionLessThan(&existDep); less && err == nil {
selectedModDep = &existDep
}
// if the dependency does not exist in the mod file,
// the dependency is a indirect dependency.
// it will be added to the kcl.mod.lock file not the kcl.mod file.
}

selectedDep := dep
// Check if the dependency exists in the lock file.
if existDep, exist := lockDeps.Get(dep.Name); exist {
// If the dependency exists in the lock file,
// check the version and select the greater one.
if less, err := existDep.VersionLessThan(dep); less && err == nil {
kpkg.Dependencies.Deps.Set(dep.Name, *dep)
if less, err := dep.VersionLessThan(&existDep); less && err == nil {
selectedDep = &existDep
}
} else {
// if the dependency does not exist in the lock file,
// the dependency is a new dependency and will be added to the lock file.
kpkg.Dependencies.Deps.Set(dep.Name, *dep)
}
selectedDep.LocalFullPath = dep.LocalFullPath

kMod.ModFile.Dependencies.Deps.Set(dep.Name, *selectedModDep)
kMod.Dependencies.Deps.Set(dep.Name, *selectedDep)

return nil
}
depResolver.ResolveFuncs = append(depResolver.ResolveFuncs, resolverFunc)

// Iterate all the dependencies of the package in kcl.mod and resolve each dependency.
for _, depName := range modDeps.Keys() {
dep, ok := modDeps.Get(depName)
if !ok {
return nil, fmt.Errorf("failed to get dependency %s", depName)
}
err := depResolver.Resolve(
resolver.WithResolveKclMod(kMod),
resolver.WithEnableCache(true),
)

// Check if the dependency is a local path and it is not an absolute path.
// If it is not an absolute path, transform the path to an absolute path.
var depSource *downloader.Source
if dep.Source.IsLocalPath() && !filepath.IsAbs(dep.Source.Local.Path) {
depSource = &downloader.Source{
Local: &downloader.Local{
Path: filepath.Join(kpkg.HomePath, dep.Source.Local.Path),
},
}
} else {
depSource = &dep.Source
}

err := resolverFunc(&dep, kpkg)
if err != nil {
return nil, err
}

err = depResolver.Resolve(
resolver.WithEnableCache(true),
resolver.WithSource(depSource),
)
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}

err := kpkg.UpdateModAndLockFile()
err = kMod.UpdateModAndLockFile()
if err != nil {
return nil, err
}

return kpkg, nil
return kMod, nil
}
125 changes: 52 additions & 73 deletions pkg/resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,21 @@ type ResolveOption func(*ResolveOptions) error
type resolveFunc func(dep *pkg.Dependency, parentPkg *pkg.KclPkg) error

type ResolveOptions struct {
// Source is the source of the package to be pulled.
// Including git, oci, local.
Source *downloader.Source
// kMod is the module to be resolved.
kMod *pkg.KclPkg
// EnableCache is the flag to enable the cache during the resolving the remote package.
EnableCache bool
// CachePath is the path of the cache.
CachePath string
}

func WithResolveKclMod(kMod *pkg.KclPkg) ResolveOption {
return func(opts *ResolveOptions) error {
opts.kMod = kMod
return nil
}
}

// WithEnableCache sets the flag to enable the cache during the resolving the remote package.
func WithEnableCache(enableCache bool) ResolveOption {
return func(opts *ResolveOptions) error {
Expand All @@ -46,26 +52,6 @@ func WithCachePath(cachePath string) ResolveOption {
}
}

// WithSource sets the source of the package to be resolved.
func WithSource(source *downloader.Source) ResolveOption {
return func(opts *ResolveOptions) error {
opts.Source = source
return nil
}
}

// WithResolveSourceUrl sets the source of the package to be resolved by the source url.
func WithSourceUrl(sourceUrl string) ResolveOption {
return func(opts *ResolveOptions) error {
source, err := downloader.NewSourceFromStr(sourceUrl)
if err != nil {
return err
}
opts.Source = source
return nil
}
}

// DepsResolver is the resolver for resolving dependencies.
type DepsResolver struct {
DefaultCachePath string
Expand All @@ -84,7 +70,6 @@ func (dr *DepsResolver) Resolve(options ...ResolveOption) error {
return err
}
}

// visitorSelectorFunc selects the visitor for the source.
// For remote source, it will use the RemoteVisitor and enable the cache.
// For local source, it will use the PkgVisitor.
Expand Down Expand Up @@ -128,70 +113,64 @@ func (dr *DepsResolver) Resolve(options ...ResolveOption) error {
}
}

// visitFunc is the function for visiting the package.
// It will traverse the dependency graph and visit each dependency by source.
visitFunc := func(kclPkg *pkg.KclPkg) error {
// Traverse the all dependencies of the package.
for _, depKey := range kclPkg.ModFile.Deps.Keys() {
dep, ok := kclPkg.ModFile.Deps.Get(depKey)
if !ok {
break
}
kMod := opts.kMod
if kMod == nil {
return fmt.Errorf("kcl module is nil")
}

// Get the dependency source.
var depSource downloader.Source
// If the dependency source is a local path and the path is not absolute, transform the path to absolute path.
if dep.Source.IsLocalPath() && !filepath.IsAbs(dep.Source.Path) {
depSource = downloader.Source{
Local: &downloader.Local{
Path: filepath.Join(kclPkg.HomePath, dep.Source.Path),
},
}
} else {
depSource = dep.Source
}
modDeps := kMod.ModFile.Dependencies.Deps
if modDeps == nil {
return fmt.Errorf("kcl.mod dependencies is nil")
}

// Get the visitor for the dependency source.
visitor, err := visitorSelectorFunc(&depSource)
if err != nil {
return err
}
for _, depName := range modDeps.Keys() {
dep, ok := modDeps.Get(depName)
if !ok {
return fmt.Errorf("failed to get dependency %s", depName)
}

// Visit this dependency and current package as the parent package.
err = visitor.Visit(&depSource,
func(childPkg *pkg.KclPkg) error {
for _, resolveFunc := range dr.ResolveFuncs {
err := resolveFunc(&dep, kclPkg)
if err != nil {
return err
}
}
return nil
// Check if the dependency is a local path and it is not an absolute path.
// If it is not an absolute path, transform the path to an absolute path.
var depSource *downloader.Source
if dep.Source.IsLocalPath() && !filepath.IsAbs(dep.Source.Local.Path) {
depSource = &downloader.Source{
Local: &downloader.Local{
Path: filepath.Join(kMod.HomePath, dep.Source.Local.Path),
},
)

if err != nil {
return err
}
} else {
depSource = &dep.Source
}

// Recursively resolve the dependencies of the dependency.
depVisitor, err := visitorSelectorFunc(depSource)
if err != nil {
return err
}

err = depVisitor.Visit(depSource, func(kclMod *pkg.KclPkg) error {
dep.FromKclPkg(kclMod)
for _, resolveFunc := range dr.ResolveFuncs {
err := resolveFunc(&dep, kMod)
if err != nil {
return err
}
}
err = dr.Resolve(
WithSource(&depSource),
WithResolveKclMod(kclMod),
WithEnableCache(opts.EnableCache),
WithCachePath(opts.CachePath),
)
if err != nil {
return err
}
}

return nil
}
return nil
})

visitor, err := visitorSelectorFunc(opts.Source)
if err != nil {
return err
if err != nil {
return err
}
}

return visitor.Visit(opts.Source, visitFunc)
return nil
}
10 changes: 9 additions & 1 deletion pkg/resolver/resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,17 @@ func TestResolver(t *testing.T) {
}},
}

kMod, err := pkg.LoadKclPkgWithOpts(
pkg.WithPath(pkgPath),
)

if err != nil {
t.Fatal(err)
}

err = resolver.Resolve(
WithEnableCache(true),
WithSourceUrl(pkgPath),
WithResolveKclMod(kMod),
)

if err != nil {
Expand Down

0 comments on commit ca697c6

Please sign in to comment.