|
|
|
@@ -128,6 +128,8 @@ func packsToList(p pkg.Packages) string {
|
|
|
|
|
for _, pp := range p {
|
|
|
|
|
packs = append(packs, pp.HumanReadableString())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sort.Strings(packs)
|
|
|
|
|
return strings.Join(packs, " ")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -137,6 +139,7 @@ func matchesToList(artefacts map[string]ArtifactMatch) string {
|
|
|
|
|
for fingerprint, match := range artefacts {
|
|
|
|
|
packs = append(packs, fmt.Sprintf("%s (%s)", fingerprint, match.Repository.GetName()))
|
|
|
|
|
}
|
|
|
|
|
sort.Strings(packs)
|
|
|
|
|
return strings.Join(packs, " ")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -194,11 +197,19 @@ func (l *LuetInstaller) Swap(toRemove pkg.Packages, toInstall pkg.Packages, s *S
|
|
|
|
|
toRemoveFinal = append(toRemoveFinal, pp)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
o := Option{
|
|
|
|
|
FullUninstall: false,
|
|
|
|
|
Force: true,
|
|
|
|
|
CheckConflicts: false,
|
|
|
|
|
FullCleanUninstall: false,
|
|
|
|
|
NoDeps: l.Options.NoDeps,
|
|
|
|
|
OnlyDeps: false,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return l.swap(syncedRepos, toRemoveFinal, toInstall, s, false)
|
|
|
|
|
return l.swap(o, syncedRepos, toRemoveFinal, toInstall, s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *LuetInstaller) computeSwap(syncedRepos Repositories, toRemove pkg.Packages, toInstall pkg.Packages, s *System) (map[string]ArtifactMatch, pkg.Packages, solver.PackagesAssertions, pkg.PackageDatabase, error) {
|
|
|
|
|
func (l *LuetInstaller) computeSwap(o Option, syncedRepos Repositories, toRemove pkg.Packages, toInstall pkg.Packages, s *System) (map[string]ArtifactMatch, pkg.Packages, solver.PackagesAssertions, pkg.PackageDatabase, error) {
|
|
|
|
|
|
|
|
|
|
allRepos := pkg.NewInMemoryDatabase(false)
|
|
|
|
|
syncedRepos.SyncDatabase(allRepos)
|
|
|
|
@@ -213,8 +224,8 @@ func (l *LuetInstaller) computeSwap(syncedRepos Repositories, toRemove pkg.Packa
|
|
|
|
|
|
|
|
|
|
systemAfterChanges := &System{Database: installedtmp}
|
|
|
|
|
|
|
|
|
|
packs, err := l.computeUninstall(systemAfterChanges, toRemove...)
|
|
|
|
|
if err != nil && !l.Options.Force {
|
|
|
|
|
packs, err := l.computeUninstall(o, systemAfterChanges, toRemove...)
|
|
|
|
|
if err != nil && !o.Force {
|
|
|
|
|
Error("Failed computing uninstall for ", packsToList(toRemove))
|
|
|
|
|
return nil, nil, nil, nil, errors.Wrap(err, "computing uninstall "+packsToList(toRemove))
|
|
|
|
|
}
|
|
|
|
@@ -225,30 +236,16 @@ func (l *LuetInstaller) computeSwap(syncedRepos Repositories, toRemove pkg.Packa
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match, packages, assertions, allRepos, err := l.computeInstall(syncedRepos, toInstall, systemAfterChanges)
|
|
|
|
|
match, packages, assertions, allRepos, err := l.computeInstall(o, syncedRepos, toInstall, systemAfterChanges)
|
|
|
|
|
for _, p := range toInstall {
|
|
|
|
|
assertions = append(assertions, solver.PackageAssert{Package: p.(*pkg.DefaultPackage), Value: true})
|
|
|
|
|
}
|
|
|
|
|
return match, packages, assertions, allRepos, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *LuetInstaller) swap(syncedRepos Repositories, toRemove pkg.Packages, toInstall pkg.Packages, s *System, forceNodeps bool) error {
|
|
|
|
|
forced := l.Options.Force
|
|
|
|
|
nodeps := l.Options.NoDeps
|
|
|
|
|
func (l *LuetInstaller) swap(o Option, syncedRepos Repositories, toRemove pkg.Packages, toInstall pkg.Packages, s *System) error {
|
|
|
|
|
|
|
|
|
|
// We don't want any conflict with the installed to raise during the upgrade.
|
|
|
|
|
// In this way we both force uninstalls and we avoid to check with conflicts
|
|
|
|
|
// against the current system state which is pending to deletion
|
|
|
|
|
// E.g. you can't check for conflicts for an upgrade of a new version of A
|
|
|
|
|
// if the old A results installed in the system. This is due to the fact that
|
|
|
|
|
// now the solver enforces the constraints and explictly denies two packages
|
|
|
|
|
// of the same version installed.
|
|
|
|
|
l.Options.Force = true
|
|
|
|
|
if forceNodeps {
|
|
|
|
|
l.Options.NoDeps = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match, packages, assertions, allRepos, err := l.computeSwap(syncedRepos, toRemove, toInstall, s)
|
|
|
|
|
match, packages, assertions, allRepos, err := l.computeSwap(o, syncedRepos, toRemove, toInstall, s)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "failed computing package replacement")
|
|
|
|
|
}
|
|
|
|
@@ -278,15 +275,173 @@ func (l *LuetInstaller) swap(syncedRepos Repositories, toRemove pkg.Packages, to
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = l.Uninstall(s, toRemove...)
|
|
|
|
|
if err != nil && !l.Options.Force {
|
|
|
|
|
Error("Failed uninstall for ", packsToList(toRemove))
|
|
|
|
|
return errors.Wrap(err, "uninstalling "+packsToList(toRemove))
|
|
|
|
|
ops := l.getOpsWithOptions(toRemove, match, Option{
|
|
|
|
|
Force: o.Force,
|
|
|
|
|
NoDeps: false,
|
|
|
|
|
OnlyDeps: o.OnlyDeps,
|
|
|
|
|
RunFinalizers: false,
|
|
|
|
|
}, o, syncedRepos, packages, assertions, allRepos)
|
|
|
|
|
|
|
|
|
|
err = l.runOps(ops, s)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "failed running installer options")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l.Options.Force = forced
|
|
|
|
|
l.Options.NoDeps = nodeps
|
|
|
|
|
return l.install(syncedRepos, match, packages, assertions, allRepos, s)
|
|
|
|
|
toFinalize, err := l.getFinalizers(allRepos, assertions, match, o.NoDeps)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "failed getting package to finalize")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s.ExecuteFinalizers(toFinalize)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Option struct {
|
|
|
|
|
Force bool
|
|
|
|
|
NoDeps bool
|
|
|
|
|
CheckConflicts bool
|
|
|
|
|
FullUninstall bool
|
|
|
|
|
FullCleanUninstall bool
|
|
|
|
|
OnlyDeps bool
|
|
|
|
|
RunFinalizers bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type operation struct {
|
|
|
|
|
Option Option
|
|
|
|
|
Package pkg.Package
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type installOperation struct {
|
|
|
|
|
operation
|
|
|
|
|
Reposiories Repositories
|
|
|
|
|
Packages pkg.Packages
|
|
|
|
|
Assertions solver.PackagesAssertions
|
|
|
|
|
Database pkg.PackageDatabase
|
|
|
|
|
Matches map[string]ArtifactMatch
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// installerOp is the operation that is sent to the
|
|
|
|
|
// upgradeWorker's channel (todo)
|
|
|
|
|
type installerOp struct {
|
|
|
|
|
Uninstall operation
|
|
|
|
|
Install installOperation
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *LuetInstaller) runOps(ops []installerOp, s *System) error {
|
|
|
|
|
all := make(chan installerOp)
|
|
|
|
|
|
|
|
|
|
wg := new(sync.WaitGroup)
|
|
|
|
|
|
|
|
|
|
// Do the real install
|
|
|
|
|
for i := 0; i < l.Options.Concurrency; i++ {
|
|
|
|
|
wg.Add(1)
|
|
|
|
|
go l.installerOpWorker(i, wg, all, s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, c := range ops {
|
|
|
|
|
all <- c
|
|
|
|
|
}
|
|
|
|
|
close(all)
|
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: use installerOpWorker in place of all the other workers.
|
|
|
|
|
// This one is general enough to read a list of operations and execute them.
|
|
|
|
|
func (l *LuetInstaller) installerOpWorker(i int, wg *sync.WaitGroup, c <-chan installerOp, s *System) error {
|
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
|
|
for p := range c {
|
|
|
|
|
if p.Uninstall.Package != nil {
|
|
|
|
|
Debug("Replacing package inplace")
|
|
|
|
|
toUninstall, uninstall, err := l.generateUninstallFn(p.Uninstall.Option, s, p.Uninstall.Package)
|
|
|
|
|
if err != nil {
|
|
|
|
|
Error("Failed to generate Uninstall function for" + err.Error())
|
|
|
|
|
continue
|
|
|
|
|
//return errors.Wrap(err, "while computing uninstall")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = uninstall()
|
|
|
|
|
if err != nil {
|
|
|
|
|
Error("Failed uninstall for ", packsToList(toUninstall))
|
|
|
|
|
continue
|
|
|
|
|
//return errors.Wrap(err, "uninstalling "+packsToList(toUninstall))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if p.Install.Package != nil {
|
|
|
|
|
artMatch := p.Install.Matches[p.Install.Package.GetFingerPrint()]
|
|
|
|
|
ass := p.Install.Assertions.Search(p.Install.Package.GetFingerPrint())
|
|
|
|
|
packageToInstall, _ := p.Install.Packages.Find(p.Install.Package.GetPackageName())
|
|
|
|
|
|
|
|
|
|
err := l.install(
|
|
|
|
|
p.Install.Option,
|
|
|
|
|
p.Install.Reposiories,
|
|
|
|
|
map[string]ArtifactMatch{p.Install.Package.GetFingerPrint(): artMatch},
|
|
|
|
|
pkg.Packages{packageToInstall},
|
|
|
|
|
solver.PackagesAssertions{*ass},
|
|
|
|
|
p.Install.Database,
|
|
|
|
|
s,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
Error(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// checks wheter we can uninstall and install in place and compose installer worker ops
|
|
|
|
|
func (l *LuetInstaller) getOpsWithOptions(
|
|
|
|
|
toUninstall pkg.Packages, installMatch map[string]ArtifactMatch, installOpt, uninstallOpt Option,
|
|
|
|
|
syncedRepos Repositories, toInstall pkg.Packages, solution solver.PackagesAssertions, allRepos pkg.PackageDatabase) []installerOp {
|
|
|
|
|
resOps := []installerOp{}
|
|
|
|
|
for _, match := range installMatch {
|
|
|
|
|
if pack, err := toUninstall.Find(match.Package.GetPackageName()); err == nil {
|
|
|
|
|
resOps = append(resOps, installerOp{
|
|
|
|
|
Uninstall: operation{Package: pack, Option: uninstallOpt},
|
|
|
|
|
Install: installOperation{
|
|
|
|
|
operation: operation{
|
|
|
|
|
Package: match.Package,
|
|
|
|
|
Option: installOpt,
|
|
|
|
|
},
|
|
|
|
|
Matches: installMatch,
|
|
|
|
|
Packages: toInstall,
|
|
|
|
|
Reposiories: syncedRepos,
|
|
|
|
|
Assertions: solution,
|
|
|
|
|
Database: allRepos,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
resOps = append(resOps, installerOp{
|
|
|
|
|
Install: installOperation{
|
|
|
|
|
operation: operation{Package: match.Package, Option: installOpt},
|
|
|
|
|
Matches: installMatch,
|
|
|
|
|
Reposiories: syncedRepos,
|
|
|
|
|
Packages: toInstall,
|
|
|
|
|
Assertions: solution,
|
|
|
|
|
Database: allRepos,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, p := range toUninstall {
|
|
|
|
|
found := false
|
|
|
|
|
|
|
|
|
|
for _, match := range installMatch {
|
|
|
|
|
if match.Package.GetPackageName() == p.GetPackageName() {
|
|
|
|
|
found = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if !found {
|
|
|
|
|
resOps = append(resOps, installerOp{
|
|
|
|
|
Uninstall: operation{Package: p, Option: uninstallOpt},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return resOps
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *LuetInstaller) checkAndUpgrade(r Repositories, s *System) error {
|
|
|
|
@@ -310,19 +465,33 @@ func (l *LuetInstaller) checkAndUpgrade(r Repositories, s *System) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We don't want any conflict with the installed to raise during the upgrade.
|
|
|
|
|
// In this way we both force uninstalls and we avoid to check with conflicts
|
|
|
|
|
// against the current system state which is pending to deletion
|
|
|
|
|
// E.g. you can't check for conflicts for an upgrade of a new version of A
|
|
|
|
|
// if the old A results installed in the system. This is due to the fact that
|
|
|
|
|
// now the solver enforces the constraints and explictly denies two packages
|
|
|
|
|
// of the same version installed.
|
|
|
|
|
o := Option{
|
|
|
|
|
FullUninstall: false,
|
|
|
|
|
Force: true,
|
|
|
|
|
CheckConflicts: false,
|
|
|
|
|
FullCleanUninstall: false,
|
|
|
|
|
NoDeps: true,
|
|
|
|
|
OnlyDeps: false,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if l.Options.Ask {
|
|
|
|
|
Info("By going forward, you are also accepting the licenses of the packages that you are going to install in your system.")
|
|
|
|
|
if Ask() {
|
|
|
|
|
l.Options.Ask = false // Don't prompt anymore
|
|
|
|
|
return l.swap(r, uninstall, toInstall, s, true)
|
|
|
|
|
return l.swap(o, r, uninstall, toInstall, s)
|
|
|
|
|
} else {
|
|
|
|
|
return errors.New("Aborted by user")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Spinner(32)
|
|
|
|
|
defer SpinnerStop()
|
|
|
|
|
return l.swap(r, uninstall, toInstall, s, true)
|
|
|
|
|
return l.swap(o, r, uninstall, toInstall, s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *LuetInstaller) Install(cp pkg.Packages, s *System) error {
|
|
|
|
@@ -338,7 +507,13 @@ func (l *LuetInstaller) Install(cp pkg.Packages, s *System) error {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match, packages, assertions, allRepos, err := l.computeInstall(syncedRepos, cp, s)
|
|
|
|
|
o := Option{
|
|
|
|
|
NoDeps: l.Options.NoDeps,
|
|
|
|
|
Force: l.Options.Force,
|
|
|
|
|
OnlyDeps: l.Options.OnlyDeps,
|
|
|
|
|
RunFinalizers: true,
|
|
|
|
|
}
|
|
|
|
|
match, packages, assertions, allRepos, err := l.computeInstall(o, syncedRepos, cp, s)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
@@ -375,12 +550,12 @@ func (l *LuetInstaller) Install(cp pkg.Packages, s *System) error {
|
|
|
|
|
Info("By going forward, you are also accepting the licenses of the packages that you are going to install in your system.")
|
|
|
|
|
if Ask() {
|
|
|
|
|
l.Options.Ask = false // Don't prompt anymore
|
|
|
|
|
return l.install(syncedRepos, match, packages, assertions, allRepos, s)
|
|
|
|
|
return l.install(o, syncedRepos, match, packages, assertions, allRepos, s)
|
|
|
|
|
} else {
|
|
|
|
|
return errors.New("Aborted by user")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return l.install(syncedRepos, match, packages, assertions, allRepos, s)
|
|
|
|
|
return l.install(o, syncedRepos, match, packages, assertions, allRepos, s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *LuetInstaller) download(syncedRepos Repositories, toDownload map[string]ArtifactMatch) error {
|
|
|
|
@@ -455,7 +630,7 @@ func (l *LuetInstaller) Reclaim(s *System) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *LuetInstaller) computeInstall(syncedRepos Repositories, cp pkg.Packages, s *System) (map[string]ArtifactMatch, pkg.Packages, solver.PackagesAssertions, pkg.PackageDatabase, error) {
|
|
|
|
|
func (l *LuetInstaller) computeInstall(o Option, syncedRepos Repositories, cp pkg.Packages, s *System) (map[string]ArtifactMatch, pkg.Packages, solver.PackagesAssertions, pkg.PackageDatabase, error) {
|
|
|
|
|
var p pkg.Packages
|
|
|
|
|
toInstall := map[string]ArtifactMatch{}
|
|
|
|
|
allRepos := pkg.NewInMemoryDatabase(false)
|
|
|
|
@@ -488,11 +663,11 @@ func (l *LuetInstaller) computeInstall(syncedRepos Repositories, cp pkg.Packages
|
|
|
|
|
var packagesToInstall pkg.Packages
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
if !l.Options.NoDeps {
|
|
|
|
|
if !o.NoDeps {
|
|
|
|
|
solv := solver.NewResolver(solver.Options{Type: l.Options.SolverOptions.Implementation, Concurrency: l.Options.Concurrency}, s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
|
|
|
|
|
solution, err = solv.Install(p)
|
|
|
|
|
/// TODO: PackageAssertions needs to be a map[fingerprint]pack so lookup is in O(1)
|
|
|
|
|
if err != nil && !l.Options.Force {
|
|
|
|
|
if err != nil && !o.Force {
|
|
|
|
|
return toInstall, p, solution, allRepos, errors.Wrap(err, "Failed solving solution for package")
|
|
|
|
|
}
|
|
|
|
|
// Gathers things to install
|
|
|
|
@@ -505,7 +680,7 @@ func (l *LuetInstaller) computeInstall(syncedRepos Repositories, cp pkg.Packages
|
|
|
|
|
packagesToInstall = append(packagesToInstall, assertion.Package)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if !l.Options.OnlyDeps {
|
|
|
|
|
} else if !o.OnlyDeps {
|
|
|
|
|
for _, currentPack := range p {
|
|
|
|
|
if _, err := s.Database.FindPackage(currentPack); err == nil {
|
|
|
|
|
// skip matching if it is installed already
|
|
|
|
@@ -540,7 +715,47 @@ func (l *LuetInstaller) computeInstall(syncedRepos Repositories, cp pkg.Packages
|
|
|
|
|
return toInstall, p, solution, allRepos, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *LuetInstaller) install(syncedRepos Repositories, toInstall map[string]ArtifactMatch, p pkg.Packages, solution solver.PackagesAssertions, allRepos pkg.PackageDatabase, s *System) error {
|
|
|
|
|
func (l *LuetInstaller) getFinalizers(allRepos pkg.PackageDatabase, solution solver.PackagesAssertions, toInstall map[string]ArtifactMatch, nodeps bool) ([]pkg.Package, error) {
|
|
|
|
|
var toFinalize []pkg.Package
|
|
|
|
|
if !nodeps {
|
|
|
|
|
// TODO: Lower those errors as warning
|
|
|
|
|
for _, w := range toInstall {
|
|
|
|
|
// Finalizers needs to run in order and in sequence.
|
|
|
|
|
ordered, err := solution.Order(allRepos, w.Package.GetFingerPrint())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return toFinalize, errors.Wrap(err, "While order a solution for "+w.Package.HumanReadableString())
|
|
|
|
|
}
|
|
|
|
|
ORDER:
|
|
|
|
|
for _, ass := range ordered {
|
|
|
|
|
if ass.Value {
|
|
|
|
|
installed, ok := toInstall[ass.Package.GetFingerPrint()]
|
|
|
|
|
if !ok {
|
|
|
|
|
// It was a dep already installed in the system, so we can skip it safely
|
|
|
|
|
continue ORDER
|
|
|
|
|
}
|
|
|
|
|
treePackage, err := installed.Repository.GetTree().GetDatabase().FindPackage(ass.Package)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return toFinalize, errors.Wrap(err, "Error getting package "+ass.Package.HumanReadableString())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toFinalize = append(toFinalize, treePackage)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for _, c := range toInstall {
|
|
|
|
|
treePackage, err := c.Repository.GetTree().GetDatabase().FindPackage(c.Package)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return toFinalize, errors.Wrap(err, "Error getting package "+c.Package.HumanReadableString())
|
|
|
|
|
}
|
|
|
|
|
toFinalize = append(toFinalize, treePackage)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return toFinalize, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *LuetInstaller) install(o Option, syncedRepos Repositories, toInstall map[string]ArtifactMatch, p pkg.Packages, solution solver.PackagesAssertions, allRepos pkg.PackageDatabase, s *System) error {
|
|
|
|
|
// Install packages into rootfs in parallel.
|
|
|
|
|
if err := l.download(syncedRepos, toInstall); err != nil {
|
|
|
|
|
return errors.Wrap(err, "Downloading packages")
|
|
|
|
@@ -569,46 +784,19 @@ func (l *LuetInstaller) install(syncedRepos Repositories, toInstall map[string]A
|
|
|
|
|
for _, c := range toInstall {
|
|
|
|
|
// Annotate to the system that the package was installed
|
|
|
|
|
_, err := s.Database.CreatePackage(c.Package)
|
|
|
|
|
if err != nil && !l.Options.Force {
|
|
|
|
|
if err != nil && !o.Force {
|
|
|
|
|
return errors.Wrap(err, "Failed creating package")
|
|
|
|
|
}
|
|
|
|
|
bus.Manager.Publish(bus.EventPackageInstall, c)
|
|
|
|
|
}
|
|
|
|
|
var toFinalize []pkg.Package
|
|
|
|
|
if !l.Options.NoDeps {
|
|
|
|
|
// TODO: Lower those errors as warning
|
|
|
|
|
for _, w := range p {
|
|
|
|
|
// Finalizers needs to run in order and in sequence.
|
|
|
|
|
ordered, err := solution.Order(allRepos, w.GetFingerPrint())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "While order a solution for "+w.HumanReadableString())
|
|
|
|
|
}
|
|
|
|
|
ORDER:
|
|
|
|
|
for _, ass := range ordered {
|
|
|
|
|
if ass.Value {
|
|
|
|
|
installed, ok := toInstall[ass.Package.GetFingerPrint()]
|
|
|
|
|
if !ok {
|
|
|
|
|
// It was a dep already installed in the system, so we can skip it safely
|
|
|
|
|
continue ORDER
|
|
|
|
|
}
|
|
|
|
|
treePackage, err := installed.Repository.GetTree().GetDatabase().FindPackage(ass.Package)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "Error getting package "+ass.Package.HumanReadableString())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toFinalize = append(toFinalize, treePackage)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !o.RunFinalizers {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for _, c := range toInstall {
|
|
|
|
|
treePackage, err := c.Repository.GetTree().GetDatabase().FindPackage(c.Package)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "Error getting package "+c.Package.HumanReadableString())
|
|
|
|
|
}
|
|
|
|
|
toFinalize = append(toFinalize, treePackage)
|
|
|
|
|
}
|
|
|
|
|
toFinalize, err := l.getFinalizers(allRepos, solution, toInstall, o.NoDeps)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "failed getting package to finalize")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s.ExecuteFinalizers(toFinalize)
|
|
|
|
@@ -688,6 +876,51 @@ func (l *LuetInstaller) installerWorker(i int, wg *sync.WaitGroup, c <-chan Arti
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func checkAndPrunePath(path string) {
|
|
|
|
|
// check if now the target path is empty
|
|
|
|
|
targetPath := filepath.Dir(path)
|
|
|
|
|
|
|
|
|
|
fi, err := os.Lstat(targetPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
// Warning("Dir not found (it was before?) ", err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch mode := fi.Mode(); {
|
|
|
|
|
case mode.IsDir():
|
|
|
|
|
files, err := ioutil.ReadDir(targetPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
Warning("Failed reading folder", targetPath, err.Error())
|
|
|
|
|
}
|
|
|
|
|
if len(files) != 0 {
|
|
|
|
|
Debug("Preserving not-empty folder", targetPath)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if err = os.Remove(targetPath); err != nil {
|
|
|
|
|
Warning("Failed removing file (maybe not present in the system target anymore ?)", targetPath, err.Error())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We will try to cleanup every path from the file, if the folders left behind are empty
|
|
|
|
|
func pruneEmptyFilePath(path string) {
|
|
|
|
|
checkAndPrunePath(path)
|
|
|
|
|
|
|
|
|
|
// A path is for e.g. /usr/bin/bar
|
|
|
|
|
// we want to create an array as "/usr", "/usr/bin", "/usr/bin/bar"
|
|
|
|
|
paths := strings.Split(path, string(os.PathSeparator))
|
|
|
|
|
currentPath := filepath.Join(string(os.PathSeparator), paths[0])
|
|
|
|
|
allPaths := []string{currentPath}
|
|
|
|
|
for _, p := range paths[1:] {
|
|
|
|
|
currentPath = filepath.Join(currentPath, p)
|
|
|
|
|
allPaths = append(allPaths, currentPath)
|
|
|
|
|
}
|
|
|
|
|
helpers.ReverseAny(allPaths)
|
|
|
|
|
for _, p := range allPaths {
|
|
|
|
|
checkAndPrunePath(p)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *LuetInstaller) uninstall(p pkg.Package, s *System) error {
|
|
|
|
|
var cp *config.ConfigProtect
|
|
|
|
|
annotationDir := ""
|
|
|
|
@@ -749,6 +982,8 @@ func (l *LuetInstaller) uninstall(p pkg.Package, s *System) error {
|
|
|
|
|
if err = os.Remove(target); err != nil {
|
|
|
|
|
Warning("Failed removing file (maybe not present in the system target anymore ?)", target, err.Error())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pruneEmptyFilePath(target)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, f := range notPresent {
|
|
|
|
@@ -762,6 +997,8 @@ func (l *LuetInstaller) uninstall(p pkg.Package, s *System) error {
|
|
|
|
|
if err = os.Remove(target); err != nil {
|
|
|
|
|
Debug("Failed removing file (not present in the system target)", target, err.Error())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pruneEmptyFilePath(target)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = s.Database.RemovePackageFiles(p)
|
|
|
|
@@ -779,17 +1016,17 @@ func (l *LuetInstaller) uninstall(p pkg.Package, s *System) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *LuetInstaller) computeUninstall(s *System, packs ...pkg.Package) (pkg.Packages, error) {
|
|
|
|
|
func (l *LuetInstaller) computeUninstall(o Option, s *System, packs ...pkg.Package) (pkg.Packages, error) {
|
|
|
|
|
|
|
|
|
|
var toUninstall pkg.Packages
|
|
|
|
|
// compute uninstall from all world - remove packages in parallel - run uninstall finalizer (in order) TODO - mark the uninstallation in db
|
|
|
|
|
// Get installed definition
|
|
|
|
|
checkConflicts := l.Options.CheckConflicts
|
|
|
|
|
full := l.Options.FullUninstall
|
|
|
|
|
if l.Options.Force == true { // IF forced, we want to remove the package and all its requires
|
|
|
|
|
checkConflicts = false
|
|
|
|
|
full = false
|
|
|
|
|
}
|
|
|
|
|
checkConflicts := o.CheckConflicts
|
|
|
|
|
full := o.FullUninstall
|
|
|
|
|
// if o.Force == true { // IF forced, we want to remove the package and all its requires
|
|
|
|
|
// checkConflicts = false
|
|
|
|
|
// full = false
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// Create a temporary DB with the installed packages
|
|
|
|
|
// so the solver is much faster finding the deptree
|
|
|
|
@@ -799,11 +1036,11 @@ func (l *LuetInstaller) computeUninstall(s *System, packs ...pkg.Package) (pkg.P
|
|
|
|
|
return toUninstall, errors.Wrap(err, "Failed create temporary in-memory db")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !l.Options.NoDeps {
|
|
|
|
|
if !o.NoDeps {
|
|
|
|
|
solv := solver.NewResolver(solver.Options{Type: l.Options.SolverOptions.Implementation, Concurrency: l.Options.Concurrency}, installedtmp, installedtmp, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
|
|
|
|
|
var solution pkg.Packages
|
|
|
|
|
var err error
|
|
|
|
|
if l.Options.FullCleanUninstall {
|
|
|
|
|
if o.FullCleanUninstall {
|
|
|
|
|
solution, err = solv.UninstallUniverse(packs)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return toUninstall, errors.Wrap(err, "Could not solve the uninstall constraints. Tip: try with --solver-type qlearning or with --force, or by removing packages excluding their dependencies with --nodeps")
|
|
|
|
@@ -824,32 +1061,47 @@ func (l *LuetInstaller) computeUninstall(s *System, packs ...pkg.Package) (pkg.P
|
|
|
|
|
|
|
|
|
|
return toUninstall, nil
|
|
|
|
|
}
|
|
|
|
|
func (l *LuetInstaller) Uninstall(s *System, packs ...pkg.Package) error {
|
|
|
|
|
|
|
|
|
|
func (l *LuetInstaller) generateUninstallFn(o Option, s *System, packs ...pkg.Package) (pkg.Packages, func() error, error) {
|
|
|
|
|
for _, p := range packs {
|
|
|
|
|
if packs, _ := s.Database.FindPackages(p); len(packs) == 0 {
|
|
|
|
|
return errors.New("Package not found in the system")
|
|
|
|
|
return nil, nil, errors.New("Package not found in the system")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Spinner(32)
|
|
|
|
|
toUninstall, err := l.computeUninstall(s, packs...)
|
|
|
|
|
toUninstall, err := l.computeUninstall(o, s, packs...)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "while computing uninstall")
|
|
|
|
|
return nil, nil, errors.Wrap(err, "while computing uninstall")
|
|
|
|
|
}
|
|
|
|
|
SpinnerStop()
|
|
|
|
|
|
|
|
|
|
uninstall := func() error {
|
|
|
|
|
for _, p := range toUninstall {
|
|
|
|
|
err := l.uninstall(p, s)
|
|
|
|
|
if err != nil && !l.Options.Force {
|
|
|
|
|
if err != nil && !o.Force {
|
|
|
|
|
return errors.Wrap(err, "Uninstall failed")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return toUninstall, uninstall, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *LuetInstaller) Uninstall(s *System, packs ...pkg.Package) error {
|
|
|
|
|
|
|
|
|
|
Spinner(32)
|
|
|
|
|
o := Option{
|
|
|
|
|
FullUninstall: l.Options.FullUninstall,
|
|
|
|
|
Force: l.Options.Force,
|
|
|
|
|
CheckConflicts: l.Options.CheckConflicts,
|
|
|
|
|
FullCleanUninstall: l.Options.FullCleanUninstall,
|
|
|
|
|
}
|
|
|
|
|
toUninstall, uninstall, err := l.generateUninstallFn(o, s, packs...)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "while computing uninstall")
|
|
|
|
|
}
|
|
|
|
|
SpinnerStop()
|
|
|
|
|
|
|
|
|
|
if len(toUninstall) == 0 {
|
|
|
|
|
Info("Nothing to do")
|
|
|
|
|
return nil
|
|
|
|
|