local_install.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. // Experimental code for install local with dependency refactoring
  2. // Not at feature parity with install.go
  3. package main
  4. import (
  5. "context"
  6. "fmt"
  7. "os"
  8. "path/filepath"
  9. "github.com/Jguer/yay/v11/pkg/db"
  10. "github.com/Jguer/yay/v11/pkg/dep"
  11. "github.com/Jguer/yay/v11/pkg/download"
  12. "github.com/Jguer/yay/v11/pkg/metadata"
  13. "github.com/Jguer/yay/v11/pkg/settings"
  14. "github.com/Jguer/yay/v11/pkg/settings/exe"
  15. "github.com/Jguer/yay/v11/pkg/settings/parser"
  16. "github.com/Jguer/yay/v11/pkg/text"
  17. "github.com/leonelquinteros/gotext"
  18. "github.com/pkg/errors"
  19. gosrc "github.com/Morganamilo/go-srcinfo"
  20. mapset "github.com/deckarep/golang-set/v2"
  21. )
  22. var ErrInstallRepoPkgs = errors.New(gotext.Get("error installing repo packages"))
  23. func installLocalPKGBUILD(
  24. ctx context.Context,
  25. cmdArgs *parser.Arguments,
  26. dbExecutor db.Executor,
  27. ) error {
  28. aurCache, err := metadata.NewAURCache(filepath.Join(config.BuildDir, "aur.json"))
  29. if err != nil {
  30. return errors.Wrap(err, gotext.Get("failed to retrieve aur Cache"))
  31. }
  32. wd, err := os.Getwd()
  33. if err != nil {
  34. return errors.Wrap(err, gotext.Get("failed to retrieve working directory"))
  35. }
  36. if len(cmdArgs.Targets) > 1 {
  37. return errors.New(gotext.Get("only one target is allowed"))
  38. }
  39. if len(cmdArgs.Targets) == 1 {
  40. wd = cmdArgs.Targets[0]
  41. }
  42. pkgbuild, err := gosrc.ParseFile(filepath.Join(wd, ".SRCINFO"))
  43. if err != nil {
  44. return errors.Wrap(err, gotext.Get("failed to parse .SRCINFO"))
  45. }
  46. grapher := dep.NewGrapher(dbExecutor, aurCache, false, settings.NoConfirm, os.Stdout)
  47. graph, err := grapher.GraphFromSrcInfo(wd, pkgbuild)
  48. if err != nil {
  49. return err
  50. }
  51. topoSorted := graph.TopoSortedLayerMap()
  52. fmt.Println(topoSorted, len(topoSorted))
  53. preparer := &Preparer{dbExecutor: dbExecutor, cmdBuilder: config.Runtime.CmdBuilder}
  54. installer := &Installer{dbExecutor: dbExecutor}
  55. if err := preparer.PrepareWorkspace(ctx, topoSorted); err != nil {
  56. return err
  57. }
  58. return installer.Install(ctx, cmdArgs, topoSorted)
  59. }
  60. type Preparer struct {
  61. dbExecutor db.Executor
  62. cmdBuilder exe.ICmdBuilder
  63. aurBases []string
  64. pkgBuildDirs []string
  65. }
  66. func (preper *Preparer) PrepareWorkspace(ctx context.Context, targets []map[string]*dep.InstallInfo,
  67. ) error {
  68. for _, layer := range targets {
  69. for pkgBase, info := range layer {
  70. if info.Source == dep.AUR {
  71. preper.aurBases = append(preper.aurBases, pkgBase)
  72. preper.pkgBuildDirs = append(preper.pkgBuildDirs, filepath.Join(config.BuildDir, pkgBase))
  73. } else if info.Source == dep.SrcInfo {
  74. preper.pkgBuildDirs = append(preper.pkgBuildDirs, *info.SrcinfoPath)
  75. }
  76. }
  77. }
  78. if _, errA := download.AURPKGBUILDRepos(ctx,
  79. preper.cmdBuilder, preper.aurBases, config.AURURL, config.BuildDir, false); errA != nil {
  80. return errA
  81. }
  82. if errP := downloadPKGBUILDSourceFanout(ctx, config.Runtime.CmdBuilder,
  83. preper.pkgBuildDirs, false, config.MaxConcurrentDownloads); errP != nil {
  84. text.Errorln(errP)
  85. }
  86. return nil
  87. }
  88. type Installer struct {
  89. dbExecutor db.Executor
  90. }
  91. func (installer *Installer) Install(ctx context.Context, cmdArgs *parser.Arguments, targets []map[string]*dep.InstallInfo) error {
  92. // Reorganize targets into layers of dependencies
  93. for i := len(targets) - 1; i >= 0; i-- {
  94. err := installer.handleLayer(ctx, cmdArgs, targets[i])
  95. if err != nil {
  96. // rollback
  97. return err
  98. }
  99. }
  100. return nil
  101. }
  102. type MapBySourceAndType map[dep.Source]map[dep.Reason][]string
  103. func (m *MapBySourceAndType) String() string {
  104. var s string
  105. for source, reasons := range *m {
  106. s += fmt.Sprintf("%s: [", source)
  107. for reason, names := range reasons {
  108. s += fmt.Sprintf(" %d: [%v] ", reason, names)
  109. }
  110. s += "], "
  111. }
  112. return s
  113. }
  114. func (installer *Installer) handleLayer(ctx context.Context, cmdArgs *parser.Arguments, layer map[string]*dep.InstallInfo) error {
  115. // Install layer
  116. depByTypeAndReason := make(MapBySourceAndType)
  117. for name, info := range layer {
  118. if _, ok := depByTypeAndReason[info.Source]; !ok {
  119. depByTypeAndReason[info.Source] = make(map[dep.Reason][]string)
  120. }
  121. depByTypeAndReason[info.Source][info.Reason] = append(depByTypeAndReason[info.Source][info.Reason], name)
  122. }
  123. fmt.Printf("%v\n", depByTypeAndReason)
  124. syncDeps, syncExp := make([]string, 0), make([]string, 0)
  125. repoTargets := make([]string, 0)
  126. aurDeps, aurExp := mapset.NewSet[string](), mapset.NewSet[string]()
  127. for source, reasons := range depByTypeAndReason {
  128. switch source {
  129. case dep.AUR:
  130. for reason, names := range reasons {
  131. for _, name := range names {
  132. switch reason {
  133. case dep.Explicit:
  134. if cmdArgs.ExistsArg("asdeps", "asdep") {
  135. aurDeps.Add(name)
  136. } else {
  137. aurExp.Add(name)
  138. }
  139. case dep.CheckDep:
  140. fallthrough
  141. case dep.MakeDep:
  142. fallthrough
  143. case dep.Dep:
  144. aurDeps.Add(name)
  145. }
  146. }
  147. }
  148. case dep.Sync:
  149. for reason, names := range reasons {
  150. switch reason {
  151. case dep.Explicit:
  152. if cmdArgs.ExistsArg("asdeps", "asdep") {
  153. syncDeps = append(syncDeps, names...)
  154. } else {
  155. syncExp = append(syncExp, names...)
  156. }
  157. case dep.CheckDep:
  158. fallthrough
  159. case dep.MakeDep:
  160. fallthrough
  161. case dep.Dep:
  162. syncDeps = append(syncDeps, names...)
  163. }
  164. repoTargets = append(repoTargets, names...)
  165. }
  166. }
  167. }
  168. fmt.Println(syncDeps, syncExp)
  169. errShow := installer.installSyncPackages(ctx, cmdArgs, repoTargets, syncDeps, syncExp)
  170. if errShow != nil {
  171. return ErrInstallRepoPkgs
  172. }
  173. return nil
  174. }
  175. func (*Installer) installAURPackages(ctx context.Context, cmdArgs *parser.Arguments, aurBaseDeps, aurBaseExp mapset.Set[string], pkgBuildDirs map[string]string, installIncompatible bool) error {
  176. deps, exp := make([]string, 0, aurBaseDeps.Cardinality()), make([]string, 0, aurBaseExp.Cardinality())
  177. for _, base := range aurBaseDeps.Union(aurBaseExp).ToSlice() {
  178. dir := pkgBuildDirs[base]
  179. args := []string{"--nobuild", "-fC"}
  180. if installIncompatible {
  181. args = append(args, "--ignorearch")
  182. }
  183. // pkgver bump
  184. if err := config.Runtime.CmdBuilder.Show(
  185. config.Runtime.CmdBuilder.BuildMakepkgCmd(ctx, dir, args...)); err != nil {
  186. return errors.New(gotext.Get("error making: %s", base))
  187. }
  188. pkgdests, _, errList := parsePackageList(ctx, dir)
  189. if errList != nil {
  190. return errList
  191. }
  192. args = []string{"-cf", "--noconfirm", "--noextract", "--noprepare", "--holdver"}
  193. if installIncompatible {
  194. args = append(args, "--ignorearch")
  195. }
  196. if errMake := config.Runtime.CmdBuilder.Show(
  197. config.Runtime.CmdBuilder.BuildMakepkgCmd(ctx,
  198. dir, args...)); errMake != nil {
  199. return errors.New(gotext.Get("error making: %s", base))
  200. }
  201. for suffix, optional := range map[string]bool{"": false, "-debug": true} {
  202. newDeps, newExp, err := getNewTargets(cmdArgs, pkgdests, base+suffix, aurBaseDeps.Contains(base), optional)
  203. if err != nil {
  204. return err
  205. }
  206. deps = append(deps, newDeps...)
  207. exp = append(exp, newExp...)
  208. }
  209. }
  210. if err := doInstall(ctx, cmdArgs, deps, exp); err != nil {
  211. return errors.New(fmt.Sprintf(gotext.Get("error installing:")+" %v %v", deps, exp))
  212. }
  213. return nil
  214. }
  215. func getNewTargets(cmdArgs *parser.Arguments, pkgdests map[string]string, name string, isDep, optional bool,
  216. ) (deps, exp []string, err error) {
  217. for pkgName, pkgDest := range pkgdests {
  218. if _, errStat := os.Stat(pkgDest); os.IsNotExist(errStat) {
  219. if optional {
  220. continue
  221. }
  222. return deps, exp, errors.New(
  223. gotext.Get(
  224. "the PKGDEST for %s is listed by makepkg but does not exist: %s",
  225. pkgName, pkgDest))
  226. }
  227. switch {
  228. case cmdArgs.ExistsArg("asdeps", "asdep"):
  229. deps = append(deps, name)
  230. case cmdArgs.ExistsArg("asexplicit", "asexp"):
  231. exp = append(exp, name)
  232. case isDep:
  233. deps = append(deps, name)
  234. default:
  235. exp = append(exp, name)
  236. }
  237. }
  238. return deps, exp, nil
  239. }
  240. func (*Installer) installSyncPackages(ctx context.Context, cmdArgs *parser.Arguments,
  241. repoTargets, // all repo targets
  242. syncDeps, // repo targets that are deps
  243. syncExp []string, // repo targets that are exp
  244. ) error {
  245. arguments := cmdArgs.Copy()
  246. arguments.DelArg("asdeps", "asdep")
  247. arguments.DelArg("asexplicit", "asexp")
  248. arguments.DelArg("i", "install")
  249. arguments.Op = "S"
  250. arguments.ClearTargets()
  251. arguments.AddTarget(repoTargets...)
  252. errShow := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
  253. arguments, config.Runtime.Mode, settings.NoConfirm))
  254. if errD := asdeps(ctx, cmdArgs, syncDeps); errD != nil {
  255. return errD
  256. }
  257. if errE := asexp(ctx, cmdArgs, syncExp); errE != nil {
  258. return errE
  259. }
  260. return errShow
  261. }