depCheck.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "strings"
  6. "sync"
  7. alpm "github.com/Jguer/go-alpm"
  8. "github.com/Jguer/yay/v9/pkg/stringset"
  9. )
  10. func (dp *depPool) checkInnerConflict(name, conflict string, conflicts stringset.MapStringSet) {
  11. for _, pkg := range dp.Aur {
  12. if pkg.Name == name {
  13. continue
  14. }
  15. if satisfiesAur(conflict, pkg) {
  16. conflicts.Add(name, pkg.Name)
  17. }
  18. }
  19. for _, pkg := range dp.Repo {
  20. if pkg.Name() == name {
  21. continue
  22. }
  23. if satisfiesRepo(conflict, pkg) {
  24. conflicts.Add(name, pkg.Name())
  25. }
  26. }
  27. }
  28. func (dp *depPool) checkForwardConflict(name, conflict string, conflicts stringset.MapStringSet) {
  29. _ = dp.LocalDB.PkgCache().ForEach(func(pkg alpm.Package) error {
  30. if pkg.Name() == name || dp.hasPackage(pkg.Name()) {
  31. return nil
  32. }
  33. if satisfiesRepo(conflict, &pkg) {
  34. n := pkg.Name()
  35. if n != conflict {
  36. n += " (" + conflict + ")"
  37. }
  38. conflicts.Add(name, n)
  39. }
  40. return nil
  41. })
  42. }
  43. func (dp *depPool) checkReverseConflict(name, conflict string, conflicts stringset.MapStringSet) {
  44. for _, pkg := range dp.Aur {
  45. if pkg.Name == name {
  46. continue
  47. }
  48. if satisfiesAur(conflict, pkg) {
  49. if name != conflict {
  50. name += " (" + conflict + ")"
  51. }
  52. conflicts.Add(pkg.Name, name)
  53. }
  54. }
  55. for _, pkg := range dp.Repo {
  56. if pkg.Name() == name {
  57. continue
  58. }
  59. if satisfiesRepo(conflict, pkg) {
  60. if name != conflict {
  61. name += " (" + conflict + ")"
  62. }
  63. conflicts.Add(pkg.Name(), name)
  64. }
  65. }
  66. }
  67. func (dp *depPool) checkInnerConflicts(conflicts stringset.MapStringSet) {
  68. for _, pkg := range dp.Aur {
  69. for _, conflict := range pkg.Conflicts {
  70. dp.checkInnerConflict(pkg.Name, conflict, conflicts)
  71. }
  72. }
  73. for _, pkg := range dp.Repo {
  74. _ = pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
  75. dp.checkInnerConflict(pkg.Name(), conflict.String(), conflicts)
  76. return nil
  77. })
  78. }
  79. }
  80. func (dp *depPool) checkForwardConflicts(conflicts stringset.MapStringSet) {
  81. for _, pkg := range dp.Aur {
  82. for _, conflict := range pkg.Conflicts {
  83. dp.checkForwardConflict(pkg.Name, conflict, conflicts)
  84. }
  85. }
  86. for _, pkg := range dp.Repo {
  87. _ = pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
  88. dp.checkForwardConflict(pkg.Name(), conflict.String(), conflicts)
  89. return nil
  90. })
  91. }
  92. }
  93. func (dp *depPool) checkReverseConflicts(conflicts stringset.MapStringSet) {
  94. _ = dp.LocalDB.PkgCache().ForEach(func(pkg alpm.Package) error {
  95. if dp.hasPackage(pkg.Name()) {
  96. return nil
  97. }
  98. _ = pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
  99. dp.checkReverseConflict(pkg.Name(), conflict.String(), conflicts)
  100. return nil
  101. })
  102. return nil
  103. })
  104. }
  105. func (dp *depPool) CheckConflicts() (stringset.MapStringSet, error) {
  106. var wg sync.WaitGroup
  107. innerConflicts := make(stringset.MapStringSet)
  108. conflicts := make(stringset.MapStringSet)
  109. wg.Add(2)
  110. fmt.Println(bold(cyan("::") + bold(" Checking for conflicts...")))
  111. go func() {
  112. dp.checkForwardConflicts(conflicts)
  113. dp.checkReverseConflicts(conflicts)
  114. wg.Done()
  115. }()
  116. fmt.Println(bold(cyan("::") + bold(" Checking for inner conflicts...")))
  117. go func() {
  118. dp.checkInnerConflicts(innerConflicts)
  119. wg.Done()
  120. }()
  121. wg.Wait()
  122. if len(innerConflicts) != 0 {
  123. fmt.Println()
  124. fmt.Println(bold(red(arrow)), bold("Inner conflicts found:"))
  125. for name, pkgs := range innerConflicts {
  126. str := red(bold(smallArrow)) + " " + name + ":"
  127. for pkg := range pkgs {
  128. str += " " + cyan(pkg) + ","
  129. }
  130. str = strings.TrimSuffix(str, ",")
  131. fmt.Println(str)
  132. }
  133. }
  134. if len(conflicts) != 0 {
  135. fmt.Println()
  136. fmt.Println(bold(red(arrow)), bold("Package conflicts found:"))
  137. for name, pkgs := range conflicts {
  138. str := red(bold(smallArrow)) + " Installing " + cyan(name) + " will remove:"
  139. for pkg := range pkgs {
  140. str += " " + cyan(pkg) + ","
  141. }
  142. str = strings.TrimSuffix(str, ",")
  143. fmt.Println(str)
  144. }
  145. }
  146. // Add the inner conflicts to the conflicts
  147. // These are used to decide what to pass --ask to (if set) or don't pass --noconfirm to
  148. // As we have no idea what the order is yet we add every inner conflict to the slice
  149. for name, pkgs := range innerConflicts {
  150. conflicts[name] = make(stringset.StringSet)
  151. for pkg := range pkgs {
  152. conflicts[pkg] = make(stringset.StringSet)
  153. }
  154. }
  155. if len(conflicts) > 0 {
  156. if !config.UseAsk {
  157. if config.NoConfirm {
  158. return nil, fmt.Errorf("package conflicts can not be resolved with noconfirm, aborting")
  159. }
  160. fmt.Fprintln(os.Stderr)
  161. fmt.Fprintln(os.Stderr, bold(red(arrow)), bold("Conflicting packages will have to be confirmed manually"))
  162. fmt.Fprintln(os.Stderr)
  163. }
  164. }
  165. return conflicts, nil
  166. }
  167. type missing struct {
  168. Good stringset.StringSet
  169. Missing map[string][][]string
  170. }
  171. func (dp *depPool) _checkMissing(dep string, stack []string, missing *missing) {
  172. if missing.Good.Get(dep) {
  173. return
  174. }
  175. if trees, ok := missing.Missing[dep]; ok {
  176. for _, tree := range trees {
  177. if stringSliceEqual(tree, stack) {
  178. return
  179. }
  180. }
  181. missing.Missing[dep] = append(missing.Missing[dep], stack)
  182. return
  183. }
  184. aurPkg := dp.findSatisfierAur(dep)
  185. if aurPkg != nil {
  186. missing.Good.Set(dep)
  187. for _, deps := range [3][]string{aurPkg.Depends, aurPkg.MakeDepends, aurPkg.CheckDepends} {
  188. for _, aurDep := range deps {
  189. if _, err := dp.LocalDB.PkgCache().FindSatisfier(aurDep); err == nil {
  190. missing.Good.Set(aurDep)
  191. continue
  192. }
  193. dp._checkMissing(aurDep, append(stack, aurPkg.Name), missing)
  194. }
  195. }
  196. return
  197. }
  198. repoPkg := dp.findSatisfierRepo(dep)
  199. if repoPkg != nil {
  200. missing.Good.Set(dep)
  201. _ = repoPkg.Depends().ForEach(func(repoDep alpm.Depend) error {
  202. if _, err := dp.LocalDB.PkgCache().FindSatisfier(repoDep.String()); err == nil {
  203. missing.Good.Set(repoDep.String())
  204. return nil
  205. }
  206. dp._checkMissing(repoDep.String(), append(stack, repoPkg.Name()), missing)
  207. return nil
  208. })
  209. return
  210. }
  211. missing.Missing[dep] = [][]string{stack}
  212. }
  213. func (dp *depPool) CheckMissing() error {
  214. missing := &missing{
  215. make(stringset.StringSet),
  216. make(map[string][][]string),
  217. }
  218. for _, target := range dp.Targets {
  219. dp._checkMissing(target.DepString(), make([]string, 0), missing)
  220. }
  221. if len(missing.Missing) == 0 {
  222. return nil
  223. }
  224. fmt.Println(bold(red(arrow+" Error: ")) + "Could not find all required packages:")
  225. for dep, trees := range missing.Missing {
  226. for _, tree := range trees {
  227. fmt.Print(" ", cyan(dep))
  228. if len(tree) == 0 {
  229. fmt.Print(" (Target")
  230. } else {
  231. fmt.Print(" (Wanted by: ")
  232. for n := 0; n < len(tree)-1; n++ {
  233. fmt.Print(cyan(tree[n]), " -> ")
  234. }
  235. fmt.Print(cyan(tree[len(tree)-1]))
  236. }
  237. fmt.Println(")")
  238. }
  239. }
  240. return fmt.Errorf("")
  241. }