From b755cb2ae068d349ae3a8a6bbce068e0d121713f Mon Sep 17 00:00:00 2001 From: kitsunyan Date: Fri, 23 Mar 2018 23:52:23 +0300 Subject: Don't fail on AUR dependency resolution --- src/common.nim | 58 +++++---- src/feature/syncinstall.nim | 305 ++++++++++++++++++++++++-------------------- 2 files changed, 198 insertions(+), 165 deletions(-) diff --git a/src/common.nim b/src/common.nim index 779dacb..b35d4f9 100644 --- a/src/common.nim +++ b/src/common.nim @@ -25,6 +25,18 @@ type FullPackageTarget*[T] = object of SyncPackageTarget pkgInfo*: Option[T] + LookupBaseGroup = tuple[ + base: string, + version: string, + arch: string, + repo: string + ] + + LookupGitResult = tuple[ + group: LookupBaseGroup, + git: Option[GitRepo] + ] + proc checkAndRefresh*(color: bool, args: seq[Argument]): tuple[code: int, args: seq[Argument]] = let refreshCount = args.count((some("y"), "refresh")) if refreshCount > 0: @@ -246,28 +258,8 @@ proc reloadPkgInfos*(config: Config, path: string, pkgInfos: seq[PackageInfo]): else: pkgInfos -proc obtainBuildPkgInfos*(config: Config, - pacmanTargets: seq[FullPackageTarget[RpcPackageInfo]]): (seq[PackageInfo], seq[string]) = - type - LookupBaseGroup = tuple[ - base: string, - version: string, - arch: string, - repo: string - ] - - LookupGitResult = tuple[ - group: LookupBaseGroup, - git: Option[GitRepo] - ] - - let bases: seq[LookupBaseGroup] = pacmanTargets - .map(target => (block: - let info = target.foundInfo.get - let pkg = info.pkg.get - (pkg.base, pkg.version, pkg.arch.get, info.repo))) - .deduplicate - +proc obtainBuildPkgInfosInternal(config: Config, bases: seq[LookupBaseGroup], + pacmanTargetNames: seq[string]): (seq[PackageInfo], seq[string]) = let lookupResults: seq[LookupGitResult] = bases .map(b => (b, lookupGitRepo(b.repo, b.base, b.arch))) let notFoundRepos = lookupResults.filter(r => r.git.isNone) @@ -311,15 +303,27 @@ proc obtainBuildPkgInfos*(config: Config, let pkgInfosTable = pkgInfos.map(i => (i.name, i)).toTable - let foundPkgInfos = lc[x | (y <- pacmanTargets, - x <- pkgInfosTable.opt(y.name)), PackageInfo] - let messages = pacmanTargets - .filter(t => not pkgInfosTable.hasKey(t.name)) - .map(t => tr"$#: failed to get package info" % [t.name]) + let foundPkgInfos = lc[x | (y <- pacmanTargetNames, + x <- pkgInfosTable.opt(y)), PackageInfo] + let messages = pacmanTargetNames + .filter(n => not pkgInfosTable.hasKey(n)) + .map(n => tr"$#: failed to get package info" % [n]) discard rmdir(config.tmpRoot) (foundPkgInfos, messages) +proc obtainBuildPkgInfos*[T: RpcPackageInfo](config: Config, + pacmanTargets: seq[FullPackageTarget[T]]): (seq[PackageInfo], seq[string]) = + let bases = pacmanTargets + .map(proc (target: FullPackageTarget[T]): LookupBaseGroup = + let info = target.foundInfo.get + let pkg = info.pkg.get + (pkg.base, pkg.version, pkg.arch.get, info.repo)) + .deduplicate + + let pacmanTargetNames = pacmanTargets.map(t => t.name) + obtainBuildPkgInfosInternal(config, bases, pacmanTargetNames) + proc cloneRepo*(config: Config, basePackages: seq[PackageInfo]): (int, Option[string]) = let base = basePackages[0].base let repoPath = repoPath(config.tmpRoot, base) diff --git a/src/feature/syncinstall.nim b/src/feature/syncinstall.nim index 87b445c..d9762ed 100644 --- a/src/feature/syncinstall.nim +++ b/src/feature/syncinstall.nim @@ -27,17 +27,17 @@ proc groupsSeq(pkg: ptr AlpmPackage): seq[string] = toSeq(pkg.groups.items).map(s => $s) proc orderInstallation(ordered: seq[seq[seq[PackageInfo]]], grouped: seq[seq[PackageInfo]], - dependencies: Table[PackageReference, SatisfyResult]): seq[seq[seq[PackageInfo]]] = + satisfied: Table[PackageReference, SatisfyResult]): seq[seq[seq[PackageInfo]]] = let orderedNamesSet = lc[c.name | (a <- ordered, b <- a, c <- b), string].toSet proc hasBuildDependency(pkgInfos: seq[PackageInfo]): bool = for pkgInfo in pkgInfos: for reference in pkgInfo.allDepends: - let satres = dependencies[reference] - if satres.buildPkgInfo.isSome and - not (satres.buildPkgInfo.unsafeGet in pkgInfos) and - not (satres.buildPkgInfo.unsafeGet.name in orderedNamesSet): - return true + for satres in satisfied.opt(reference): + if satres.buildPkgInfo.isSome and + not (satres.buildPkgInfo.unsafeGet in pkgInfos) and + not (satres.buildPkgInfo.unsafeGet.name in orderedNamesSet): + return true return false let split: seq[tuple[pkgInfos: seq[PackageInfo], dependent: bool]] = @@ -50,15 +50,15 @@ proc orderInstallation(ordered: seq[seq[seq[PackageInfo]]], grouped: seq[seq[Pac if unordered.len == grouped.len: newOrdered & unordered else: - orderInstallation(newOrdered, unordered, dependencies) + orderInstallation(newOrdered, unordered, satisfied) else: newOrdered proc orderInstallation(pkgInfos: seq[PackageInfo], - dependencies: Table[PackageReference, SatisfyResult]): seq[seq[seq[PackageInfo]]] = + satisfied: Table[PackageReference, SatisfyResult]): seq[seq[seq[PackageInfo]]] = let grouped = pkgInfos.groupBy(i => i.base).map(p => p.values) - orderInstallation(@[], grouped, dependencies) + orderInstallation(@[], grouped, satisfied) .map(x => x.filter(s => s.len > 0)) .filter(x => x.len > 0) @@ -482,7 +482,7 @@ proc installGroupFromSources(config: Config, commonArgs: seq[Argument], proc handleInstall(args: seq[Argument], config: Config, upgradeCount: int, noconfirm: bool, explicits: HashSet[string], installed: seq[Installed], - dependencies: Table[PackageReference, SatisfyResult], + satisfied: Table[PackageReference, SatisfyResult], unsatisfied: seq[PackageReference], directPacmanTargets: seq[string], additionalPacmanTargets: seq[string], basePackages: seq[seq[seq[PackageInfo]]]): int = let (directCode, directSome) = if directPacmanTargets.len > 0 or upgradeCount > 0: @@ -491,8 +491,14 @@ proc handleInstall(args: seq[Argument], config: Config, upgradeCount: int, else: (0, false) - if directCode != 0: - directCode + let directSatisfiedCode = if directCode == 0 and unsatisfied.len > 0: (block: + printUnsatisfied(config, satisfied, unsatisfied) + 1) + else: + directCode + + if directSatisfiedCode != 0: + directSatisfiedCode else: let commonArgs = args.keepOnlyOptions(commonOptions, upgradeCommonOptions) @@ -665,12 +671,12 @@ proc handleInstall(args: seq[Argument], config: Config, upgradeCount: int, return true return false - lc[x.key | (x <- dependencies.namedPairs, not x.value.installed and + lc[x.key | (x <- satisfied.namedPairs, not x.value.installed and x.value.buildPkgInfo.isNone and not x.key.checkSatisfied), PackageReference] if unsatisfied.len > 0: removeTmp() - printUnsatisfied(config, dependencies, unsatisfied) + printUnsatisfied(config, satisfied, unsatisfied) 1 else: proc installNext(index: int, lastCode: int): (int, int) = @@ -692,15 +698,26 @@ proc handleInstall(args: seq[Argument], config: Config, upgradeCount: int, else: 0 -proc handlePrint(args: seq[Argument], config: Config, printFormat: string, - upgradeCount: int, directPacmanTargets: seq[string], additionalPacmanTargets: seq[string], +proc handlePrint(args: seq[Argument], config: Config, printFormat: string, upgradeCount: int, + satisfied: Table[PackageReference, SatisfyResult], unsatisfied: seq[PackageReference], + directPacmanTargets: seq[string], additionalPacmanTargets: seq[string], basePackages: seq[seq[seq[PackageInfo]]]): int = let code = if directPacmanTargets.len > 0 or - additionalPacmanTargets.len > 0 or upgradeCount > 0: - pacmanRun(false, config.color, args.filter(arg => not arg.isTarget) & - (directPacmanTargets & additionalPacmanTargets) - .map(t => (t, none(string), ArgumentType.target))) + additionalPacmanTargets.len > 0 or upgradeCount > 0: (block: + let pacmanTargets = if unsatisfied.len > 0: + directPacmanTargets + else: + directPacmanTargets & additionalPacmanTargets + + let code = pacmanRun(false, config.color, args.filter(arg => not arg.isTarget) & + pacmanTargets.map(t => (t, none(string), ArgumentType.target))) + + if unsatisfied.len > 0: + printUnsatisfied(config, satisfied, unsatisfied) + 1 + else: + code) else: 0 @@ -721,6 +738,77 @@ proc handlePrint(args: seq[Argument], config: Config, printFormat: string, else: code +proc filterIgnoresAndConflicts(config: Config, pkgInfos: seq[PackageInfo], + targetsSet: HashSet[string], installed: Table[string, Installed], + print: bool, noconfirm: bool): (seq[PackageInfo], seq[PackageInfo]) = + let acceptedPkgInfos = pkgInfos.filter(pkgInfo => (block: + let instGroups = lc[x | (i <- installed.opt(pkgInfo.name), + x <- i.groups), string] + + if config.ignored(pkgInfo.name, (instGroups & pkgInfo.groups).deduplicate): + if pkgInfo.name in targetsSet: + if not print: + let input = printColonUserChoice(config.color, + trp"%s is in IgnorePkg/IgnoreGroup. Install anyway?" % [pkgInfo.name], + ['y', 'n'], 'y', 'n', noconfirm, 'y') + input != 'n' + else: + true + else: + false + else: + true)) + + let nonConflicingPkgInfos = pkgInfos.foldl(block: + let conflictsWith = lc[p | (p <- a, p.name != b.name and + (lc[0 | (c <- b.conflicts, c.isProvidedBy(p.toPackageReference)), int].len > 0 or + lc[0 | (c <- p.conflicts, c.isProvidedBy(b.toPackageReference)), int].len > 0)), + PackageInfo] + if not print and conflictsWith.len > 0: + for conflict in conflictsWith: + printWarning(config.color, + tra("removing '%s' from target list because it conflicts with '%s'\n") % + [b.name, conflict.name]) + a + else: + a & b, + newSeq[PackageInfo]()) + + (nonConflicingPkgInfos, acceptedPkgInfos) + +proc checkNeeded(installed: Table[string, Installed], name: string, version: string): bool = + if installed.hasKey(name): + let i = installed[name] + vercmp(version, i.version) > 0 + else: + true + +proc obtainAurPackageInfos(config: Config, rpcInfos: seq[RpcPackageInfo], + targets: seq[FullPackageTarget[RpcPackageInfo]], + installed: Table[string, Installed], print: bool, needed: bool, + upgradeCount: int): (seq[PackageInfo], seq[string]) = + let targetRpcInfos: seq[tuple[rpcInfo: RpcPackageInfo, upgradeable: bool]] = + targets.map(t => t.pkgInfo.get).map(i => (i, installed.checkNeeded(i.name, i.version))) + + if not print and needed: + for pair in targetRpcInfos: + if not pair.upgradeable: + # not upgradeable assumes that package is installed + let inst = installed[pair.rpcInfo.name] + printWarning(config.color, tra("%s-%s is up to date -- skipping\n") % + [pair.rpcInfo.name, inst.version]) + + let targetsSet = targets.map(t => t.name).toSet + let fullRpcInfos = (targetRpcInfos + .filter(i => not needed or i.upgradeable).map(i => i.rpcInfo) & + rpcInfos.filter(i => upgradeCount > 0 and not (i.name in targetsSet) and + installed.checkNeeded(i.name, i.version))).deduplicate + + if fullRpcInfos.len > 0 and not print: + echo(tr"downloading full package descriptions...") + getAurPackageInfo(fullRpcInfos.map(i => i.name), + some(fullRpcInfos), config.arch, proc (a: int, b: int) = discard) + proc handleSyncInstall*(args: seq[Argument], config: Config): int = let (_, callArgs) = checkAndRefresh(config.color, args) @@ -770,19 +858,15 @@ proc handleSyncInstall*(args: seq[Argument], config: Config): int = withAur(): if realCheckAur.len > 0 and printFormat.isNone: printColon(config.color, tr"Checking AUR database...") - let (rpcInfos, aerrors) = getRpcPackageInfo(realCheckAur) - for e in aerrors: printError(config.color, e) + let (rpcInfos, rerrors) = getRpcPackageInfo(realCheckAur) + for e in rerrors: printError(config.color, e) - let (rpcInfoTable, notFoundTargets) = filterNotFoundSyncTargets(syncTargets, rpcInfos) + let (rpcInfoTable, rpcNotFoundTargets) = filterNotFoundSyncTargets(syncTargets, rpcInfos) - if notFoundTargets.len > 0: - printSyncNotFound(config, notFoundTargets) + if rpcNotFoundTargets.len > 0: + printSyncNotFound(config, rpcNotFoundTargets) 1 else: - let fullTargets = mapAurTargets(syncTargets, rpcInfos) - let pacmanTargets = fullTargets.filter(t => not isAurTargetFull(t)) - let aurTargets = fullTargets.filter(isAurTargetFull) - if upgradeCount > 0 and not noaur and printFormat.isNone and config.printAurNotFound: for inst in installed: if inst.foreign and not config.ignored(inst.name, inst.groups) and @@ -790,44 +874,26 @@ proc handleSyncInstall*(args: seq[Argument], config: Config): int = printWarning(config.color, tr"$# was not found in AUR" % [inst.name]) let installedTable = installed.map(i => (i.name, i)).toTable + let rpcAurTargets = mapAurTargets(syncTargets, rpcInfos).filter(isAurTargetFull) - proc checkNeeded(name: string, version: string): bool = - if installedTable.hasKey(name): - let i = installedTable[name] - vercmp(version, i.version) > 0 - else: - true + let (aurPkgInfos, aperrors) = obtainAurPackageInfos(config, rpcInfos, rpcAurTargets, + installedTable, printFormat.isSome, needed, upgradeCount) + for e in aperrors: printError(config.color, e) - let targetRpcInfos: seq[tuple[rpcInfo: RpcPackageInfo, upgradeable: bool]] = - aurTargets.map(t => t.pkgInfo.get).map(i => (i, checkNeeded(i.name, i.version))) - - if printFormat.isNone and needed: - for rpcInfo in targetRpcInfos: - if not rpcInfo.upgradeable: - # not upgradeable assumes that package is installed - let inst = installedTable[rpcInfo.rpcInfo.name] - printWarning(config.color, tra("%s-%s is up to date -- skipping\n") % - [rpcInfo.rpcInfo.name, inst.version]) - - let aurTargetsSet = aurTargets.map(t => t.name).toSet - let fullRpcInfos = (targetRpcInfos - .filter(i => not needed or i.upgradeable).map(i => i.rpcInfo) & - rpcInfos.filter(i => upgradeCount > 0 and not (i.name in aurTargetsSet) and - checkNeeded(i.name, i.version))).deduplicate - - if fullRpcInfos.len > 0 and printFormat.isNone: - echo(tr"downloading full package descriptions...") - let (aurPkgInfos, faerrors) = getAurPackageInfo(fullRpcInfos.map(i => i.name), - some(fullRpcInfos), config.arch, proc (a: int, b: int) = discard) - - if faerrors.len > 0: - for e in faerrors: printError(config.color, e) + let (_, notFoundTargets) = filterNotFoundSyncTargets(syncTargets, aurPkgInfos) + + if notFoundTargets.len > 0: + printSyncNotFound(config, notFoundTargets) 1 else: + let fullTargets = mapAurTargets(syncTargets, aurPkgInfos) + let pacmanTargets = fullTargets.filter(t => not isAurTargetFull(t)) + let aurTargets = fullTargets.filter(isAurTargetFull) + let neededPacmanTargets = if printFormat.isNone and build and needed: pacmanTargets.filter(target => (block: let version = target.foundInfo.get.pkg.get.version - if checkNeeded(target.name, version): + if installedTable.checkNeeded(target.name, version): true else: printWarning(config.color, tra("%s-%s is up to date -- skipping\n") % @@ -841,99 +907,62 @@ proc handleSyncInstall*(args: seq[Argument], config: Config): int = let (buildPkgInfos, obtainErrorMessages) = if checkPacmanPkgInfos: (block: printColon(config.color, tr"Checking repositories...") - obtainBuildPkgInfos(config, pacmanTargets)) + obtainBuildPkgInfos[PackageInfo](config, pacmanTargets)) else: (@[], @[]) if checkPacmanPkgInfos and buildPkgInfos.len < pacmanTargets.len: + # "--build" conflicts with "--sysupgrade", so it's ok to fail here for e in obtainErrorMessages: printError(config.color, e) 1 else: let pkgInfos = buildPkgInfos & aurPkgInfos - let targetsSet = fullTargets.map(t => t.name).toSet - - let acceptedPkgInfos = pkgInfos.filter(pkgInfo => (block: - let instGroups = lc[x | (i <- installedTable.opt(pkgInfo.name), - x <- i.groups), string] - - if config.ignored(pkgInfo.name, (instGroups & pkgInfo.groups).deduplicate): - if pkgInfo.name in targetsSet: - if printFormat.isNone: - let input = printColonUserChoice(config.color, - trp"%s is in IgnorePkg/IgnoreGroup. Install anyway?" % [pkgInfo.name], - ['y', 'n'], 'y', 'n', noconfirm, 'y') - input != 'n' - else: - true - else: - false - else: - true)) - - let nonConflicingPkgInfos = pkgInfos.foldl(block: - let conflictsWith = lc[p | (p <- a, p.name != b.name and - (lc[0 | (c <- b.conflicts, c.isProvidedBy(p.toPackageReference)), int].len > 0 or - lc[0 | (c <- p.conflicts, c.isProvidedBy(b.toPackageReference)), int].len > 0)), - PackageInfo] - if printFormat.isNone and conflictsWith.len > 0: - for conflict in conflictsWith: - printWarning(config.color, - tra("removing '%s' from target list because it conflicts with '%s'\n") % - [b.name, conflict.name]) - a - else: - a & b, - newSeq[PackageInfo]()) - - let finalPkgInfos = nonConflicingPkgInfos + let targetsSet = (pacmanTargets & aurTargets).map(t => t.name).toSet + let (finalPkgInfos, acceptedPkgInfos) = filterIgnoresAndConflicts(config, pkgInfos, + targetsSet, installedTable, printFormat.isSome, noconfirm) if finalPkgInfos.len > 0 and printFormat.isNone: echo(trp("resolving dependencies...\n")) let (satisfied, unsatisfied) = withAlpm(config.root, config.db, config.dbs, config.arch, handle, dbs, errors): - findDependencies(config, handle, dbs, finalPkgInfos, - printFormat.isSome, noaur) + findDependencies(config, handle, dbs, finalPkgInfos, printFormat.isSome, noaur) - if unsatisfied.len > 0: - printUnsatisfied(config, satisfied, unsatisfied) - 1 - else: - if printFormat.isNone: - let acceptedSet = acceptedPkgInfos.map(i => i.name).toSet - - for pkgInfo in pkgInfos: - if not (pkgInfo.name in acceptedSet): - if not (pkgInfo.name in targetsSet) and upgradeCount > 0 and - installedTable.hasKey(pkgInfo.name): - printWarning(config.color, tra("%s: ignoring package upgrade (%s => %s)\n") % - [pkgInfo.name, installedTable[pkgInfo.name].version, pkgInfo.version]) - else: - printWarning(config.color, trp("skipping target: %s\n") % [pkgInfo.name]) - elif pkgInfo.repo == "aur" and pkgInfo.maintainer.isNone: - printWarning(config.color, tr"$# is orphaned" % [pkgInfo.name]) - - let aurPrintSet = finalPkgInfos.map(i => i.name).toSet - let fullPkgInfos = finalPkgInfos & lc[i | (s <- satisfied.values, - i <- s.buildPkgInfo, not (i.name in aurPrintSet)), PackageInfo].deduplicate + if printFormat.isNone: + let acceptedSet = acceptedPkgInfos.map(i => i.name).toSet - let directPacmanTargets = pacmanTargets.map(t => t.formatArgument) - let additionalPacmanTargets = lc[x.name | (x <- satisfied.values, - not x.installed and x.buildPkgInfo.isNone), string] - let orderedPkgInfos = orderInstallation(fullPkgInfos, satisfied) - - let pacmanArgs = callArgs.filterExtensions(true, true) - - if printFormat.isSome: - handlePrint(pacmanArgs, config, printFormat.unsafeGet, upgradeCount, - directPacmanTargets, additionalPacmanTargets, orderedPkgInfos) - else: - let explicits = if not args.check((none(string), "asdeps")): - targets.map(t => t.name) + for pkgInfo in pkgInfos: + if not (pkgInfo.name in acceptedSet): + if not (pkgInfo.name in targetsSet) and upgradeCount > 0 and + installedTable.hasKey(pkgInfo.name): + printWarning(config.color, tra("%s: ignoring package upgrade (%s => %s)\n") % + [pkgInfo.name, installedTable[pkgInfo.name].version, pkgInfo.version]) else: - @[] + printWarning(config.color, trp("skipping target: %s\n") % [pkgInfo.name]) + elif pkgInfo.repo == "aur" and pkgInfo.maintainer.isNone: + printWarning(config.color, tr"$# is orphaned" % [pkgInfo.name]) + + let buildAndAurTargetSet = finalPkgInfos.map(i => i.name).toSet + let fullPkgInfos = finalPkgInfos & lc[i | (s <- satisfied.values, + i <- s.buildPkgInfo, not (i.name in buildAndAurTargetSet)), PackageInfo].deduplicate + + let directPacmanTargets = pacmanTargets.map(t => t.formatArgument) + let additionalPacmanTargets = lc[x.name | (x <- satisfied.values, + not x.installed and x.buildPkgInfo.isNone), string] + let orderedPkgInfos = orderInstallation(fullPkgInfos, satisfied) + + let pacmanArgs = callArgs.filterExtensions(true, true) + if printFormat.isSome: + handlePrint(pacmanArgs, config, printFormat.unsafeGet, upgradeCount, + satisfied, unsatisfied, directPacmanTargets, additionalPacmanTargets, + orderedPkgInfos) + else: + let explicits = if not args.check((none(string), "asdeps")): + targets.map(t => t.name) + else: + @[] - let passDirectPacmanTargets = if build: @[] else: directPacmanTargets + let passDirectPacmanTargets = if build: @[] else: directPacmanTargets - handleInstall(pacmanArgs, config, upgradeCount, noconfirm, - explicits.toSet, installed, satisfied, passDirectPacmanTargets, - additionalPacmanTargets, orderedPkgInfos) + handleInstall(pacmanArgs, config, upgradeCount, noconfirm, + explicits.toSet, installed, satisfied, unsatisfied, passDirectPacmanTargets, + additionalPacmanTargets, orderedPkgInfos) -- cgit v1.2.3-70-g09d2