install.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "os/exec"
  7. "strings"
  8. "strconv"
  9. alpm "github.com/jguer/go-alpm"
  10. rpc "github.com/mikkeloscar/aur"
  11. gopkg "github.com/mikkeloscar/gopkgbuild"
  12. )
  13. // Install handles package installs
  14. func install(parser *arguments) error {
  15. aur, repo, err := packageSlices(parser.targets.toSlice())
  16. if err != nil {
  17. return err
  18. }
  19. srcinfos := make(map[string]*gopkg.PKGBUILD)
  20. var dc *depCatagories
  21. //fmt.Println(greenFg(arrow), greenFg("Resolving Dependencies"))
  22. requestTargets := aur
  23. //remotenames: names of all non repo packages on the system
  24. _, _, _, remoteNames, err := filterPackages()
  25. if err != nil {
  26. return err
  27. }
  28. //cache as a stringset. maybe make it return a string set in the first
  29. //place
  30. remoteNamesCache := make(stringSet)
  31. for _, name := range remoteNames {
  32. remoteNamesCache.set(name)
  33. }
  34. //if we are doing -u also request every non repo package on the system
  35. if parser.existsArg("u", "sysupgrade") {
  36. requestTargets = append(requestTargets, remoteNames...)
  37. }
  38. fmt.Println(boldCyanFg("::"), boldFg("Querying AUR..."))
  39. dt , err := getDepTree(requestTargets)
  40. if err != nil {
  41. return err
  42. }
  43. //only error if direct targets or deps are missing
  44. missing := make(stringSet)
  45. for missingName := range dt.Missing {
  46. if !remoteNamesCache.get(missingName) {
  47. missing.set(missingName)
  48. }
  49. }
  50. if len(missing) > 0 {
  51. printMissing(missing)
  52. return fmt.Errorf("Could not find all required packages")
  53. }
  54. //create the arguments to pass for the repo install
  55. arguments := parser.copy()
  56. arguments.delArg("u", "sysupgrade")
  57. arguments.delArg("y", "refresh")
  58. arguments.op = "S"
  59. arguments.targets = make(stringSet)
  60. if parser.existsArg("u", "sysupgrade") {
  61. repoUp, aurUp, err := upgradePkgs(dt)
  62. if err != nil {
  63. return err
  64. }
  65. for pkg := range aurUp {
  66. parser.addTarget(pkg)
  67. }
  68. for pkg := range repoUp {
  69. arguments.addTarget(pkg)
  70. }
  71. //discard stuff thats
  72. //not a target and
  73. //not an upgrade and
  74. //is installed
  75. for pkg := range dt.Aur {
  76. if !parser.targets.get(pkg) && remoteNamesCache.get(pkg) {
  77. delete(dt.Aur, pkg)
  78. }
  79. }
  80. }
  81. for _, pkg := range repo {
  82. arguments.addTarget(pkg)
  83. }
  84. hasAur := len(dt.Aur) != 0
  85. if hasAur {
  86. dc, err = getDepCatagories(parser.formatTargets(), dt)
  87. if err != nil {
  88. return err
  89. }
  90. //printDownloadsFromRepo("Repo", dc.Repo)
  91. //printDownloadsFromRepo("Repo Make", dc.RepoMake)
  92. //printDownloadsFromAur("AUR", dc.Aur)
  93. //printDownloadsFromAur("AUR Make", dc.AurMake)
  94. //fmt.Println(dc.MakeOnly)
  95. //fmt.Println(dc.AurSet)
  96. printDepCatagories(dc)
  97. fmt.Println()
  98. }
  99. if len(arguments.targets) > 0 {
  100. err := passToPacman(arguments)
  101. if err != nil {
  102. fmt.Println("Error installing repo packages.")
  103. }
  104. }
  105. if hasAur {
  106. if !parser.existsArg("gendb") {
  107. err = checkForConflicts(dc)
  108. if err != nil {
  109. return err
  110. }
  111. }
  112. askCleanBuilds(dc.Aur, dc.Bases)
  113. fmt.Println()
  114. if !continueTask("Proceed with install?", "nN") {
  115. return fmt.Errorf("Aborting due to user")
  116. }
  117. // if !continueTask("Proceed with download?", "nN") {
  118. // return fmt.Errorf("Aborting due to user")
  119. // }
  120. err = dowloadPkgBuilds(dc.Aur, dc.Bases)
  121. if err != nil {
  122. return err
  123. }
  124. err = askEditPkgBuilds(dc.Aur, dc.Bases)
  125. if err != nil {
  126. return err
  127. }
  128. /*if len(dc.Repo) > 0 {
  129. arguments := parser.copy()
  130. arguments.delArg("u", "sysupgrade")
  131. arguments.delArg("y", "refresh")
  132. arguments.op = "S"
  133. arguments.targets = make(stringSet)
  134. arguments.addArg("needed", "asdeps")
  135. for _, pkg := range dc.Repo {
  136. arguments.addTarget(pkg.Name())
  137. }
  138. oldConfirm := config.NoConfirm
  139. config.NoConfirm = true
  140. passToPacman(arguments)
  141. config.NoConfirm = oldConfirm
  142. if err != nil {
  143. return err
  144. }
  145. }*/
  146. if arguments.existsArg("gendb") {
  147. fmt.Println("GenDB finished. No packages were installed")
  148. return nil
  149. }
  150. // if !continueTask("Proceed with install?", "nN") {
  151. // return fmt.Errorf("Aborting due to user")
  152. // }
  153. err = downloadPkgBuildsSources(dc.Aur)
  154. if err != nil {
  155. return err
  156. }
  157. err = parsesrcinfos(dc.Aur, srcinfos)
  158. if err != nil {
  159. return err
  160. }
  161. err = buildInstallPkgBuilds(dc.Aur, srcinfos, parser.targets, parser, dc.Bases)
  162. if err != nil {
  163. return err
  164. }
  165. if len(dc.MakeOnly) > 0 {
  166. if continueTask("Remove make dependencies?", "yY") {
  167. return nil
  168. }
  169. removeArguments := makeArguments()
  170. removeArguments.addArg("R", "u")
  171. for pkg := range dc.MakeOnly {
  172. removeArguments.addTarget(pkg)
  173. }
  174. oldValue := config.NoConfirm
  175. config.NoConfirm = true
  176. passToPacman(removeArguments)
  177. config.NoConfirm = oldValue
  178. }
  179. if config.CleanAfter {
  180. clean(dc.Aur)
  181. }
  182. return nil
  183. }
  184. return nil
  185. }
  186. func askCleanBuilds(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg) {
  187. for _, pkg := range pkgs {
  188. dir := config.BuildDir + pkg.PackageBase + "/"
  189. if _, err := os.Stat(dir); !os.IsNotExist(err) {
  190. str := pkg.Name
  191. if len(bases[pkg.PackageBase]) > 1 || pkg.PackageBase != pkg.Name {
  192. str += " ("
  193. for _, split := range bases[pkg.PackageBase] {
  194. str += split.Name + " "
  195. }
  196. str = str[:len(str)-1] + ")"
  197. }
  198. if !continueTask(str+" Directory exists. Clean Build?", "yY") {
  199. _ = os.RemoveAll(config.BuildDir + pkg.PackageBase)
  200. }
  201. }
  202. }
  203. }
  204. func checkForConflicts(dc *depCatagories) error {
  205. localDb, err := alpmHandle.LocalDb()
  206. if err != nil {
  207. return err
  208. }
  209. toRemove := make(map[string]stringSet)
  210. for _, pkg := range dc.Aur {
  211. for _, cpkg := range pkg.Conflicts {
  212. if _, err := localDb.PkgByName(cpkg); err == nil {
  213. _, ok := toRemove[pkg.Name]
  214. if !ok {
  215. toRemove[pkg.Name] = make(stringSet)
  216. }
  217. toRemove[pkg.Name].set(cpkg)
  218. }
  219. }
  220. }
  221. for _, pkg := range dc.Repo {
  222. pkg.Conflicts().ForEach(func(conf alpm.Depend) error {
  223. if _, err := localDb.PkgByName(conf.Name); err == nil {
  224. _, ok := toRemove[pkg.Name()]
  225. if !ok {
  226. toRemove[pkg.Name()] = make(stringSet)
  227. }
  228. toRemove[pkg.Name()].set(conf.Name)
  229. }
  230. return nil
  231. })
  232. }
  233. if len(toRemove) != 0 {
  234. fmt.Println(
  235. redFg("Package conflicts found:"))
  236. for name, pkgs := range toRemove {
  237. str := yellowFg("\t" + name) + " Replaces"
  238. for pkg := range pkgs {
  239. str += " " + yellowFg(pkg)
  240. }
  241. fmt.Println(str)
  242. }
  243. if !continueTask("Continue with install?", "nN") {
  244. return fmt.Errorf("Aborting due to user")
  245. }
  246. ask, _ := strconv.Atoi(cmdArgs.globals["ask"])
  247. uask := alpm.Question(ask) | alpm.QuestionConflictPkg
  248. cmdArgs.globals["ask"] = fmt.Sprint(uask)
  249. }
  250. return nil
  251. }
  252. func askEditPkgBuilds(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg) error {
  253. for _, pkg := range pkgs {
  254. dir := config.BuildDir + pkg.PackageBase + "/"
  255. str := "Edit PKGBUILD? " + pkg.PackageBase
  256. if len(bases[pkg.PackageBase]) > 1 || pkg.PackageBase != pkg.Name {
  257. str += " ("
  258. for _, split := range bases[pkg.PackageBase] {
  259. str += split.Name + " "
  260. }
  261. str = str[:len(str)-1] + ")"
  262. }
  263. if !continueTask(str, "yY") {
  264. editcmd := exec.Command(editor(), dir+"PKGBUILD")
  265. editcmd.Stdin, editcmd.Stdout, editcmd.Stderr = os.Stdin, os.Stdout, os.Stderr
  266. editcmd.Run()
  267. }
  268. }
  269. return nil
  270. }
  271. func parsesrcinfos(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD) error {
  272. for _, pkg := range pkgs {
  273. dir := config.BuildDir + pkg.PackageBase + "/"
  274. cmd := exec.Command(config.MakepkgBin, "--printsrcinfo")
  275. cmd.Stderr = os.Stderr
  276. cmd.Dir = dir
  277. srcinfo, err := cmd.Output()
  278. if err != nil {
  279. return err
  280. }
  281. pkgbuild, err := gopkg.ParseSRCINFOContent(srcinfo)
  282. if err != nil {
  283. return fmt.Errorf("%s: %s", pkg.Name, err)
  284. }
  285. srcinfos[pkg.PackageBase] = pkgbuild
  286. for _, pkgsource := range pkgbuild.Source {
  287. owner, repo := parseSource(pkgsource)
  288. if owner != "" && repo != "" {
  289. err = branchInfo(pkg.Name, owner, repo)
  290. if err != nil {
  291. return err
  292. }
  293. }
  294. }
  295. }
  296. return nil
  297. }
  298. func dowloadPkgBuilds(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg) (err error) {
  299. for _, pkg := range pkgs {
  300. //todo make pretty
  301. str := "Downloading: " + pkg.PackageBase + "-" + pkg.Version
  302. if len(bases[pkg.PackageBase]) > 1 || pkg.PackageBase != pkg.Name {
  303. str += " ("
  304. for _, split := range bases[pkg.PackageBase] {
  305. str += split.Name + " "
  306. }
  307. str = str[:len(str)-1] + ")"
  308. }
  309. fmt.Println(str)
  310. err = downloadAndUnpack(baseURL+pkg.URLPath, config.BuildDir, false)
  311. if err != nil {
  312. return
  313. }
  314. }
  315. return
  316. }
  317. func downloadPkgBuildsSources(pkgs []*rpc.Pkg) (err error) {
  318. for _, pkg := range pkgs {
  319. dir := config.BuildDir + pkg.PackageBase + "/"
  320. err = passToMakepkg(dir, "--nobuild", "--nocheck", "--noprepare", "--nodeps")
  321. if err != nil {
  322. return
  323. }
  324. }
  325. return
  326. }
  327. func buildInstallPkgBuilds(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD, targets stringSet, parser *arguments, bases map[string][]*rpc.Pkg) error {
  328. //for n := len(pkgs) -1 ; n > 0; n-- {
  329. for n := 0; n < len(pkgs); n++ {
  330. pkg := pkgs[n]
  331. dir := config.BuildDir + pkg.PackageBase + "/"
  332. built := true
  333. srcinfo := srcinfos[pkg.PackageBase]
  334. version := srcinfo.CompleteVersion()
  335. for _, split := range bases[pkg.PackageBase] {
  336. file, err := completeFileName(dir, split.Name+"-"+version.String())
  337. if err != nil {
  338. return err
  339. }
  340. if file == "" {
  341. built = false
  342. }
  343. }
  344. if built {
  345. fmt.Println(boldRedFgBlackBg(arrow+" Warning:"),
  346. blackBg(pkg.Name+"-"+pkg.Version+" Already made -- skipping build"))
  347. } else {
  348. err := passToMakepkg(dir, "-Cscf", "--noconfirm")
  349. if err != nil {
  350. return err
  351. }
  352. }
  353. arguments := parser.copy()
  354. arguments.targets = make(stringSet)
  355. arguments.op = "U"
  356. arguments.delArg("confirm")
  357. arguments.delArg("c", "clean")
  358. arguments.delArg("q", "quiet")
  359. arguments.delArg("q", "quiet")
  360. arguments.delArg("y", "refresh")
  361. arguments.delArg("u", "sysupgrade")
  362. arguments.delArg("w", "downloadonly")
  363. depArguments := makeArguments()
  364. depArguments.addArg("D", "asdeps")
  365. for _, split := range bases[pkg.PackageBase] {
  366. file, err := completeFileName(dir, split.Name+"-"+version.String())
  367. if err != nil {
  368. return err
  369. }
  370. if file == "" {
  371. return fmt.Errorf("Could not find built package " + split.Name + "-" + version.String())
  372. }
  373. arguments.addTarget(file)
  374. if !targets.get(split.Name) {
  375. depArguments.addTarget(split.Name)
  376. }
  377. }
  378. oldConfirm := config.NoConfirm
  379. config.NoConfirm = true
  380. err := passToPacman(arguments)
  381. if err != nil {
  382. return err
  383. }
  384. if len(depArguments.targets) > 0 {
  385. err = passToPacman(depArguments)
  386. if err != nil {
  387. return err
  388. }
  389. }
  390. config.NoConfirm = oldConfirm
  391. }
  392. return nil
  393. }
  394. func clean(pkgs []*rpc.Pkg) {
  395. for _, pkg := range pkgs {
  396. dir := config.BuildDir + pkg.PackageBase + "/"
  397. fmt.Println(boldGreenFg(arrow +
  398. " CleanAfter enabled. Deleting " + pkg.Name + " source folder."))
  399. os.RemoveAll(dir)
  400. }
  401. }
  402. func completeFileName(dir, name string) (string, error) {
  403. files, err := ioutil.ReadDir(dir)
  404. if err != nil {
  405. return "", err
  406. }
  407. for _, file := range files {
  408. if file.IsDir() {
  409. continue
  410. }
  411. if strings.HasPrefix(file.Name(), name) {
  412. return dir + file.Name(), nil
  413. }
  414. }
  415. return "", nil
  416. }