Browse Source

Merge pull request #269 from Morganamilo/conflicts

 More conflict checking
Morgana 7 years ago
parent
commit
ac02498177
2 changed files with 375 additions and 51 deletions
  1. 373 0
      conflicts.go
  2. 2 51
      install.go

+ 373 - 0
conflicts.go

@@ -0,0 +1,373 @@
+package main
+
+import (
+	"fmt"
+	"strings"
+	"sync"
+
+	alpm "github.com/jguer/go-alpm"
+	gopkg "github.com/mikkeloscar/gopkgbuild"
+)
+
+// Checks a single conflict against every other to be installed package's
+// name and its provides.
+func checkInnerConflict(name string, conflict string, conflicts map[string]stringSet, dc *depCatagories) {
+	add := func(h map[string]stringSet, n string, v string) {
+		_, ok := h[n]
+		if !ok {
+			h[n] = make(stringSet)
+		}
+		h[n].set(v)
+	}
+
+	deps, err := gopkg.ParseDeps([]string{conflict})
+	if err != nil {
+		return
+	}
+	dep := deps[0]
+
+	for _, pkg := range dc.Aur {
+		if name == pkg.Name {
+			continue
+		}
+
+		version, err := gopkg.NewCompleteVersion(pkg.Version)
+		if err != nil {
+			return
+		}
+		if dep.Name == pkg.Name && version.Satisfies(dep) {
+			add(conflicts, name, pkg.Name)
+			continue
+		}
+
+		for _, provide := range pkg.Provides {
+			// Provides are not versioned unless explicitly defined as
+			// such. If a conflict is versioned but a provide is
+			// not it can not conflict.
+			if (dep.MaxVer != nil || dep.MinVer != nil) && !strings.ContainsAny(provide, "><=") {
+				continue
+			}
+
+			var version *gopkg.CompleteVersion
+			var err error
+
+			pname, pversion := splitNameFromDep(provide)
+
+			if dep.Name != pname {
+				continue
+			}
+
+			if pversion != "" {
+				version, err = gopkg.NewCompleteVersion(provide)
+				if err != nil {
+					return
+				}
+			}
+
+			if version != nil && version.Satisfies(dep) {
+				add(conflicts, name, pkg.Name)
+				break
+			}
+
+		}
+	}
+
+	for _, pkg := range dc.Repo {
+		if name == pkg.Name() {
+			continue
+		}
+
+		version, err := gopkg.NewCompleteVersion(pkg.Version())
+		if err != nil {
+			return
+		}
+
+		if dep.Name == pkg.Name() && version.Satisfies(dep) {
+			add(conflicts, name, pkg.Name())
+			continue
+		}
+
+		pkg.Provides().ForEach(func(provide alpm.Depend) error {
+			// Provides are not versioned unless explicitly defined as
+			// such. If a conflict is versioned but a provide is
+			// not it can not conflict.
+			if (dep.MaxVer != nil || dep.MinVer != nil) && provide.Mod == alpm.DepModAny {
+				return nil
+			}
+
+			if dep.Name != pkg.Name() {
+				return nil
+			}
+
+			if provide.Mod == alpm.DepModAny {
+				add(conflicts, name, pkg.Name())
+				return fmt.Errorf("")
+			}
+
+			version, err := gopkg.NewCompleteVersion(provide.Version)
+			if err != nil {
+				return nil
+			}
+
+			if  version.Satisfies(dep) {
+				add(conflicts, name, pkg.Name())
+				return fmt.Errorf("")
+			}
+
+			return nil
+		})
+	}
+}
+
+// Checks every to be installed package's conflicts against every other to be
+// installed package and its provides.
+func checkForInnerConflicts(dc *depCatagories) (map[string]stringSet) {
+	conflicts := make(map[string]stringSet)
+
+	for _, pkg := range dc.Aur {
+		for _, cpkg := range pkg.Conflicts {
+			checkInnerConflict(pkg.Name, cpkg, conflicts, dc)
+		}
+	}
+
+	for _, pkg := range dc.Repo {
+		pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
+			checkInnerConflict(pkg.Name(), conflict.String(), conflicts, dc)
+			return nil
+		})
+	}
+
+	return conflicts
+}
+
+// Checks a provide or packagename from a to be installed package
+// against every already installed package's conflicts
+func checkReverseConflict(name string, provide string, conflicts map[string]stringSet) error {
+	add := func(h map[string]stringSet, n string, v string) {
+		_, ok := h[n]
+		if !ok {
+			h[n] = make(stringSet)
+		}
+		h[n].set(v)
+	}
+
+	var version *gopkg.CompleteVersion
+	var err error
+
+	localDb, err := alpmHandle.LocalDb()
+	if err != nil {
+		return err
+	}
+
+	pname, pversion := splitNameFromDep(provide)
+	if pversion != "" {
+		version, err = gopkg.NewCompleteVersion(pversion)
+		if err != nil {
+			return nil
+		}
+	}
+
+
+	localDb.PkgCache().ForEach(func(pkg alpm.Package) error {
+		if name == pkg.Name() {
+			return nil
+		}
+
+		pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
+			deps, err := gopkg.ParseDeps([]string{conflict.String()})
+			if err != nil {
+				return nil
+			}
+
+			dep := deps[0]
+			// Provides are not versioned unless explicitly defined as
+			// such. If a conflict is versioned but a provide is
+			// not it can not conflict.
+			if (dep.MaxVer != nil || dep.MinVer != nil) && version == nil {
+				return nil
+			}
+
+			if dep.Name != pname {
+				return nil
+			}
+
+			if version == nil ||  version.Satisfies(dep) {
+				// Todo
+				add(conflicts, name, pkg.Name() + " (" + provide + ")")
+				return fmt.Errorf("")
+			}
+
+			return nil
+		})
+
+		return nil
+	})
+
+	return nil
+}
+
+// Checks the conflict of a to be installed package against the package name and
+// provides of every installed package.
+func checkConflict(name string, conflict string, conflicts map[string]stringSet) error {
+	add := func(h map[string]stringSet, n string, v string) {
+		_, ok := h[n]
+		if !ok {
+			h[n] = make(stringSet)
+		}
+		h[n].set(v)
+	}
+
+	localDb, err := alpmHandle.LocalDb()
+	if err != nil {
+		return err
+	}
+
+	deps, err := gopkg.ParseDeps([]string{conflict})
+	if err != nil {
+		return nil
+	}
+
+	dep := deps[0]
+
+	localDb.PkgCache().ForEach(func(pkg alpm.Package) error {
+		if name == pkg.Name() {
+			return nil
+		}
+
+		version, err := gopkg.NewCompleteVersion(pkg.Version())
+		if err != nil {
+			return nil
+		}
+
+		if dep.Name == pkg.Name() && version.Satisfies(dep) {
+			add(conflicts, name, pkg.Name())
+			return nil
+		}
+
+		pkg.Provides().ForEach(func(provide alpm.Depend) error {
+			if dep.Name != provide.Name {
+				return nil
+			}
+
+			// Provides arent version unless explicitly defined as
+			// such. If a conflict is versioned but a provide is
+			// not it can not conflict.
+			if (dep.MaxVer != nil || dep.MinVer != nil) && provide.Mod == alpm.DepModAny {
+				return nil
+			}
+
+			if provide.Mod == alpm.DepModAny {
+				add(conflicts, name, pkg.Name() + " (" + provide.Name + ")")
+				return fmt.Errorf("")
+			}
+
+			version, err := gopkg.NewCompleteVersion(provide.Version)
+			if err != nil {
+				return nil
+			}
+
+			if version.Satisfies(dep) {
+				add(conflicts, name, pkg.Name() + " (" + provide.Name + ")")
+				return fmt.Errorf("")
+			}
+
+			return nil
+		})
+
+		return nil
+	})
+
+	return nil
+}
+
+// Checks every to be installed package's conflicts against the names and
+// provides of every already installed package and checks every to be installed
+// package's name and provides against every already installed package.
+func checkForConflicts(dc *depCatagories) (map[string]stringSet, error)  {
+	conflicts := make(map[string]stringSet)
+
+	for _, pkg := range dc.Aur {
+		for _, cpkg := range pkg.Conflicts {
+			checkConflict(pkg.Name, cpkg, conflicts)
+		}
+	}
+
+	for _, pkg := range dc.Repo {
+		pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
+			checkConflict(pkg.Name(), conflict.String(), conflicts)
+			return nil
+		})
+	}
+
+	for _, pkg := range dc.Aur {
+		checkReverseConflict(pkg.Name, pkg.Name, conflicts)
+		for _, ppkg := range pkg.Provides {
+			checkReverseConflict(pkg.Name, ppkg, conflicts)
+		}
+	}
+
+	for _, pkg := range dc.Repo {
+		checkReverseConflict(pkg.Name(), pkg.Name(), conflicts)
+		pkg.Provides().ForEach(func(provide alpm.Depend) error {
+			checkReverseConflict(pkg.Name(), provide.String(), conflicts)
+			return nil
+		})
+	}
+
+	return conflicts, nil
+}
+
+// Combiles checkForConflicts() and checkForInnerConflicts() in parallel and
+// does some printing.
+func checkForAllConflicts(dc *depCatagories) error {
+	var err error
+	var conflicts map[string]stringSet
+	var innerConflicts map[string]stringSet
+	var wg sync.WaitGroup
+	wg.Add(2)
+
+	fmt.Println(bold(cyan("::")+ " Checking for conflicts..."))
+	go func() {
+		conflicts, err = checkForConflicts(dc)
+		wg.Done()
+	}()
+
+	fmt.Println(bold(cyan("::")+ " Checking for inner conflicts..."))
+	go func() {
+		innerConflicts = checkForInnerConflicts(dc)
+		wg.Done()
+	}()
+
+	wg.Wait()
+	if len(innerConflicts) != 0 {
+		fmt.Println(
+			red("\nInner conflicts found:"))
+		for name, pkgs := range innerConflicts {
+			str := "\t" + name + ":"
+			for pkg := range pkgs {
+				str += " " + magenta(pkg)
+			}
+
+			fmt.Println(str)
+		}
+
+		return fmt.Errorf("Aborting")
+	}
+
+	if len(conflicts) != 0 {
+		fmt.Println(
+			red("\nPackage conflicts found:"))
+		for name, pkgs := range conflicts {
+			str := "\tInstalling " + magenta(name) + " will remove:"
+			for pkg := range pkgs {
+				str += " " + magenta(pkg)
+			}
+
+			fmt.Println(str)
+		}
+
+		fmt.Println()
+	}
+
+	return nil
+}

+ 2 - 51
install.go

@@ -120,7 +120,7 @@ func install(parser *arguments) error {
 		fmt.Println()
 
 		if !parser.existsArg("gendb") {
-			err = checkForConflicts(dc)
+			err = checkForAllConflicts(dc)
 			if err != nil {
 				return err
 			}
@@ -452,56 +452,6 @@ func cleanBuilds(pkgs []*rpc.Pkg) {
 	}
 }
 
-func checkForConflicts(dc *depCatagories) error {
-	localDb, err := alpmHandle.LocalDb()
-	if err != nil {
-		return err
-	}
-	toRemove := make(map[string]stringSet)
-
-	for _, pkg := range dc.Aur {
-		for _, cpkg := range pkg.Conflicts {
-			if _, err := localDb.PkgByName(cpkg); err == nil {
-				_, ok := toRemove[pkg.Name]
-				if !ok {
-					toRemove[pkg.Name] = make(stringSet)
-				}
-				toRemove[pkg.Name].set(cpkg)
-			}
-		}
-	}
-
-	for _, pkg := range dc.Repo {
-		pkg.Conflicts().ForEach(func(conf alpm.Depend) error {
-			if _, err := localDb.PkgByName(conf.Name); err == nil {
-				_, ok := toRemove[pkg.Name()]
-				if !ok {
-					toRemove[pkg.Name()] = make(stringSet)
-				}
-				toRemove[pkg.Name()].set(conf.Name)
-			}
-			return nil
-		})
-	}
-
-	if len(toRemove) != 0 {
-		fmt.Println(
-			red("Package conflicts found:"))
-		for name, pkgs := range toRemove {
-			str := "\tInstalling " + magenta(name) + " will remove"
-			for pkg := range pkgs {
-				str += " " + magenta(pkg)
-			}
-
-			fmt.Println(str)
-		}
-
-		fmt.Println()
-	}
-
-	return nil
-}
-
 func editPkgBuilds(pkgs []*rpc.Pkg) error {
 	pkgbuilds := make([]string, 0, len(pkgs))
 	for _, pkg := range pkgs {
@@ -728,6 +678,7 @@ func clean(pkgs []*rpc.Pkg) {
 }
 
 func completeFileName(dir, name string) (string, error) {
+
 	files, err := ioutil.ReadDir(dir)
 	if err != nil {
 		return "", err