depCheck.go 6.9 KB

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