depCheck.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. package dep
  2. import (
  3. "fmt"
  4. "os"
  5. "strings"
  6. "sync"
  7. "github.com/leonelquinteros/gotext"
  8. "github.com/Jguer/yay/v10/pkg/stringset"
  9. "github.com/Jguer/yay/v10/pkg/text"
  10. )
  11. func (dp *Pool) checkInnerConflict(name, conflict string, conflicts stringset.MapStringSet) {
  12. for _, pkg := range dp.Aur {
  13. if pkg.Name == name {
  14. continue
  15. }
  16. if satisfiesAur(conflict, pkg) {
  17. conflicts.Add(name, pkg.Name)
  18. }
  19. }
  20. for _, pkg := range dp.Repo {
  21. if pkg.Name() == name {
  22. continue
  23. }
  24. if satisfiesRepo(conflict, pkg, dp.AlpmExecutor) {
  25. conflicts.Add(name, pkg.Name())
  26. }
  27. }
  28. }
  29. func (dp *Pool) checkForwardConflict(name, conflict string, conflicts stringset.MapStringSet) {
  30. for _, pkg := range dp.AlpmExecutor.LocalPackages() {
  31. if pkg.Name() == name || dp.hasPackage(pkg.Name()) {
  32. continue
  33. }
  34. if satisfiesRepo(conflict, pkg, dp.AlpmExecutor) {
  35. n := pkg.Name()
  36. if n != conflict {
  37. n += " (" + conflict + ")"
  38. }
  39. conflicts.Add(name, n)
  40. }
  41. }
  42. }
  43. func (dp *Pool) 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, dp.AlpmExecutor) {
  60. if name != conflict {
  61. name += " (" + conflict + ")"
  62. }
  63. conflicts.Add(pkg.Name(), name)
  64. }
  65. }
  66. }
  67. func (dp *Pool) 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. for _, conflict := range dp.AlpmExecutor.PackageConflicts(pkg) {
  75. dp.checkInnerConflict(pkg.Name(), conflict.String(), conflicts)
  76. }
  77. }
  78. }
  79. func (dp *Pool) checkForwardConflicts(conflicts stringset.MapStringSet) {
  80. for _, pkg := range dp.Aur {
  81. for _, conflict := range pkg.Conflicts {
  82. dp.checkForwardConflict(pkg.Name, conflict, conflicts)
  83. }
  84. }
  85. for _, pkg := range dp.Repo {
  86. for _, conflict := range dp.AlpmExecutor.PackageConflicts(pkg) {
  87. dp.checkForwardConflict(pkg.Name(), conflict.String(), conflicts)
  88. }
  89. }
  90. }
  91. func (dp *Pool) checkReverseConflicts(conflicts stringset.MapStringSet) {
  92. for _, pkg := range dp.AlpmExecutor.LocalPackages() {
  93. if dp.hasPackage(pkg.Name()) {
  94. continue
  95. }
  96. for _, conflict := range dp.AlpmExecutor.PackageConflicts(pkg) {
  97. dp.checkReverseConflict(pkg.Name(), conflict.String(), conflicts)
  98. }
  99. }
  100. }
  101. func (dp *Pool) CheckConflicts(useAsk, noConfirm, noDeps bool) (stringset.MapStringSet, error) {
  102. conflicts := make(stringset.MapStringSet)
  103. if noDeps {
  104. return conflicts, nil
  105. }
  106. var wg sync.WaitGroup
  107. innerConflicts := make(stringset.MapStringSet)
  108. wg.Add(2)
  109. text.OperationInfoln(gotext.Get("Checking for conflicts..."))
  110. go func() {
  111. dp.checkForwardConflicts(conflicts)
  112. dp.checkReverseConflicts(conflicts)
  113. wg.Done()
  114. }()
  115. text.OperationInfoln(gotext.Get("Checking for inner conflicts..."))
  116. go func() {
  117. dp.checkInnerConflicts(innerConflicts)
  118. wg.Done()
  119. }()
  120. wg.Wait()
  121. if len(innerConflicts) != 0 {
  122. text.Errorln(gotext.Get("\nInner conflicts found:"))
  123. for name, pkgs := range innerConflicts {
  124. str := text.SprintError(name + ":")
  125. for pkg := range pkgs {
  126. str += " " + text.Cyan(pkg) + ","
  127. }
  128. str = strings.TrimSuffix(str, ",")
  129. fmt.Println(str)
  130. }
  131. }
  132. if len(conflicts) != 0 {
  133. text.Errorln(gotext.Get("\nPackage conflicts found:"))
  134. for name, pkgs := range conflicts {
  135. str := text.SprintError(gotext.Get("Installing %s will remove:", text.Cyan(name)))
  136. for pkg := range pkgs {
  137. str += " " + text.Cyan(pkg) + ","
  138. }
  139. str = strings.TrimSuffix(str, ",")
  140. fmt.Println(str)
  141. }
  142. }
  143. // Add the inner conflicts to the conflicts
  144. // These are used to decide what to pass --ask to (if set) or don't pass --noconfirm to
  145. // As we have no idea what the order is yet we add every inner conflict to the slice
  146. for name, pkgs := range innerConflicts {
  147. conflicts[name] = make(stringset.StringSet)
  148. for pkg := range pkgs {
  149. conflicts[pkg] = make(stringset.StringSet)
  150. }
  151. }
  152. if len(conflicts) > 0 {
  153. if !useAsk {
  154. if noConfirm {
  155. return nil, fmt.Errorf(gotext.Get("package conflicts can not be resolved with noconfirm, aborting"))
  156. }
  157. text.Errorln(gotext.Get("Conflicting packages will have to be confirmed manually"))
  158. }
  159. }
  160. return conflicts, nil
  161. }
  162. type missing struct {
  163. Good stringset.StringSet
  164. Missing map[string][][]string
  165. }
  166. func (dp *Pool) _checkMissing(dep string, stack []string, missing *missing, noDeps, noCheckDeps bool) {
  167. if missing.Good.Get(dep) {
  168. return
  169. }
  170. if trees, ok := missing.Missing[dep]; ok {
  171. for _, tree := range trees {
  172. if stringSliceEqual(tree, stack) {
  173. return
  174. }
  175. }
  176. missing.Missing[dep] = append(missing.Missing[dep], stack)
  177. return
  178. }
  179. if aurPkg := dp.findSatisfierAur(dep); aurPkg != nil {
  180. missing.Good.Set(dep)
  181. combinedDepList := ComputeCombinedDepList(aurPkg, noDeps, noCheckDeps)
  182. for _, deps := range combinedDepList {
  183. for _, aurDep := range deps {
  184. if dp.AlpmExecutor.LocalSatisfierExists(aurDep) {
  185. missing.Good.Set(aurDep)
  186. continue
  187. }
  188. dp._checkMissing(aurDep, append(stack, aurPkg.Name), missing, noDeps, noCheckDeps)
  189. }
  190. }
  191. return
  192. }
  193. if repoPkg := dp.findSatisfierRepo(dep); repoPkg != nil {
  194. missing.Good.Set(dep)
  195. if noDeps {
  196. return
  197. }
  198. for _, dep := range dp.AlpmExecutor.PackageDepends(repoPkg) {
  199. if dp.AlpmExecutor.LocalSatisfierExists(dep.String()) {
  200. missing.Good.Set(dep.String())
  201. continue
  202. }
  203. dp._checkMissing(dep.String(), append(stack, repoPkg.Name()), missing, noDeps, noCheckDeps)
  204. }
  205. return
  206. }
  207. missing.Missing[dep] = [][]string{stack}
  208. }
  209. func stringSliceEqual(a, b []string) bool {
  210. if a == nil && b == nil {
  211. return true
  212. }
  213. if a == nil || b == nil {
  214. return false
  215. }
  216. if len(a) != len(b) {
  217. return false
  218. }
  219. for i := 0; i < len(a); i++ {
  220. if a[i] != b[i] {
  221. return false
  222. }
  223. }
  224. return true
  225. }
  226. func (dp *Pool) CheckMissing(noDeps, noCheckDeps bool) error {
  227. missing := &missing{
  228. make(stringset.StringSet),
  229. make(map[string][][]string),
  230. }
  231. for _, target := range dp.Targets {
  232. dp._checkMissing(target.DepString(), make([]string, 0), missing, noDeps, noCheckDeps)
  233. }
  234. if len(missing.Missing) == 0 {
  235. return nil
  236. }
  237. text.Errorln(gotext.Get("Could not find all required packages:"))
  238. for dep, trees := range missing.Missing {
  239. for _, tree := range trees {
  240. fmt.Fprintf(os.Stderr, "\t%s", text.Cyan(dep))
  241. if len(tree) == 0 {
  242. fmt.Fprint(os.Stderr, gotext.Get(" (Target"))
  243. } else {
  244. fmt.Fprint(os.Stderr, gotext.Get(" (Wanted by: "))
  245. for n := 0; n < len(tree)-1; n++ {
  246. fmt.Fprint(os.Stderr, text.Cyan(tree[n]), " -> ")
  247. }
  248. fmt.Fprint(os.Stderr, text.Cyan(tree[len(tree)-1]))
  249. }
  250. fmt.Fprintln(os.Stderr, ")")
  251. }
  252. }
  253. return fmt.Errorf("")
  254. }