aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/pakku.8.txt4
-rw-r--r--src/common.nim216
-rw-r--r--src/config.nim17
-rw-r--r--src/feature/syncinfo.nim2
-rw-r--r--src/feature/syncinstall.nim61
-rw-r--r--src/feature/syncsource.nim173
-rw-r--r--src/format.nim6
-rw-r--r--src/main.nim6
-rw-r--r--src/pacman.nim7
9 files changed, 367 insertions, 125 deletions
diff --git a/doc/pakku.8.txt b/doc/pakku.8.txt
index d9a0e00..8fb140b 100644
--- a/doc/pakku.8.txt
+++ b/doc/pakku.8.txt
@@ -39,6 +39,10 @@ Sync Options (apply to '-S')[[SO]]
root path, skip dependency checking using '-d', or run the program
from root.
+*-z, \--source*::
+ Retrieve PKGBUILD source. You can specify destination directory after
+ package name: pakku -Sz bash::/tmp/bash.
+
Configuration
-------------
See linkman:pacman.conf[5] and linkman:pakku.conf[5] for details on configuring
diff --git a/src/common.nim b/src/common.nim
index b66237a..351dd73 100644
--- a/src/common.nim
+++ b/src/common.nim
@@ -1,6 +1,6 @@
import
future, options, os, osproc, posix, sequtils, sets, strutils, tables,
- args, config, lists, package, pacman, utils,
+ args, config, format, lists, package, pacman, utils,
"wrapper/alpm"
type
@@ -18,6 +18,7 @@ type
PackageTarget* = object of RootObj
reference*: PackageReference
repo*: Option[string]
+ destination*: Option[string]
SyncPackageTarget* = object of PackageTarget
foundInfos*: seq[SyncFoundInfo]
@@ -51,15 +52,25 @@ proc checkAndRefresh*(color: bool, args: seq[Argument]): tuple[code: int, args:
else:
(0, args)
-proc packageTargets*(args: seq[Argument]): seq[PackageTarget] =
+proc packageTargets*(args: seq[Argument], parseDestination: bool): seq[PackageTarget] =
args.targets.map(target => (block:
- let splitTarget = target.split('/', 2)
- let (repo, nameConstraint) = if splitTarget.len == 2:
- (some(splitTarget[0]), splitTarget[1])
+ let (noDestinationTarget, destination) = if parseDestination: (block:
+ let split = target.split("::", 2)
+ if split.len == 2:
+ (split[0], some(split[1]))
+ else:
+ (target, none(string)))
+ else:
+ (target, none(string))
+
+ let splitRepoTarget = noDestinationTarget.split('/', 2)
+ let (repo, nameConstraint) = if splitRepoTarget.len == 2:
+ (some(splitRepoTarget[0]), splitRepoTarget[1])
else:
- (none(string), target)
+ (none(string), noDestinationTarget)
+
let reference = parsePackageReference(nameConstraint, false)
- PackageTarget(reference: reference, repo: repo)))
+ PackageTarget(reference: reference, repo: repo, destination: destination)))
proc isAurTargetSync*(target: SyncPackageTarget): bool =
target.foundInfos.len == 0 and (target.repo.isNone or target.repo == some("aur"))
@@ -67,6 +78,29 @@ proc isAurTargetSync*(target: SyncPackageTarget): bool =
proc isAurTargetFull*[T: RpcPackageInfo](target: FullPackageTarget[T]): bool =
target.foundInfos.len > 0 and target.foundInfos[0].repo == "aur"
+proc filterNotFoundSyncTargetsInternal(syncTargets: seq[SyncPackageTarget],
+ pkgInfoReferencesTable: Table[string, PackageReference],
+ upToDateNeededTable: Table[string, PackageReference]): seq[SyncPackageTarget] =
+ # collect packages which were found neither in sync DB nor in AUR
+ syncTargets.filter(t => not (upToDateNeededTable.opt(t.reference.name)
+ .map(r => t.reference.isProvidedBy(r)).get(false)) and t.foundInfos.len == 0 and
+ not (t.isAurTargetSync and pkgInfoReferencesTable.opt(t.reference.name)
+ .map(r => t.reference.isProvidedBy(r)).get(false)))
+
+proc filterNotFoundSyncTargets*[T: RpcPackageInfo](syncTargets: seq[SyncPackageTarget],
+ pkgInfos: seq[T], upToDateNeededTable: Table[string, PackageReference]): seq[SyncPackageTarget] =
+ let pkgInfoReferencesTable = pkgInfos.map(i => (i.name, i.toPackageReference)).toTable
+ filterNotFoundSyncTargetsInternal(syncTargets, pkgInfoReferencesTable, upToDateNeededTable)
+
+proc printSyncNotFound*(config: Config, notFoundTargets: seq[SyncPackageTarget]) =
+ let dbs = config.dbs.toSet
+
+ for target in notFoundTargets:
+ if target.repo.isNone or target.repo == some("aur") or target.repo.unsafeGet in dbs:
+ printError(config.color, trp("target not found: %s\n") % [$target.reference])
+ else:
+ printError(config.color, trp("database not found: %s\n") % [target.repo.unsafeGet])
+
proc findSyncTargets*(handle: ptr AlpmHandle, dbs: seq[ptr AlpmDatabase],
targets: seq[PackageTarget], allowGroups: bool, checkProvides: bool):
(seq[SyncPackageTarget], seq[string]) =
@@ -125,7 +159,7 @@ proc findSyncTargets*(handle: ptr AlpmHandle, dbs: seq[ptr AlpmDatabase],
return @[]
let syncTargets = targets.map(t => SyncPackageTarget(reference: t.reference,
- repo: t.repo, foundInfos: findSync(t)))
+ repo: t.repo, destination: t.destination, foundInfos: findSync(t)))
let checkAurNames = syncTargets.filter(isAurTargetSync).map(t => t.reference.name)
(syncTargets, checkAurNames)
@@ -146,10 +180,10 @@ proc mapAurTargets*[T: RpcPackageInfo](targets: seq[SyncPackageTarget],
if res.isSome:
let (syncInfo, pkgInfo) = res.get
FullPackageTarget[T](reference: target.reference, repo: target.repo,
- foundInfos: @[syncInfo], pkgInfo: some(pkgInfo))
+ destination: target.destination, foundInfos: @[syncInfo], pkgInfo: some(pkgInfo))
else:
FullPackageTarget[T](reference: target.reference, repo: target.repo,
- foundInfos: target.foundInfos, pkgInfo: none(T)))
+ destination: target.destination, foundInfos: target.foundInfos, pkgInfo: none(T)))
proc queryUnrequired*(handle: ptr AlpmHandle, withOptional: bool, withoutOptional: bool,
assumeExplicit: HashSet[string]): (HashSet[string], HashSet[string], HashSet[string]) =
@@ -213,22 +247,46 @@ proc queryUnrequired*(handle: ptr AlpmHandle, withOptional: bool, withoutOptiona
proc `$`*[T: PackageTarget](target: T): string =
target.repo.map(proc (r: string): string = r & "/" & $target.reference).get($target.reference)
-proc ensureTmpOrError*(config: Config): Option[string] =
+template tmpRoot(config: Config, dropPrivileges: bool): string =
+ if dropPrivileges: config.tmpRootInitial else: config.tmpRootCurrent
+
+proc ensureTmpOrError*(config: Config, dropPrivileges: bool): Option[string] =
let tmpRootExists = try:
- let user = initialUser.get(currentUser)
- discard config.tmpRoot.existsOrCreateDir()
- discard chown(config.tmpRoot, (Uid) user.uid, (Gid) user.gid)
+ discard config.tmpRoot(dropPrivileges).existsOrCreateDir()
+ if dropPrivileges:
+ let user = initialUser.get(currentUser)
+ discard chown(config.tmpRoot(dropPrivileges), (Uid) user.uid, (Gid) user.gid)
true
except:
false
if not tmpRootExists:
- some(tr"failed to create tmp directory '$#'" % [config.tmpRoot])
+ some(tr"failed to create tmp directory '$#'" % [config.tmpRoot(dropPrivileges)])
else:
none(string)
+proc getGitFiles*(repoPath: string, gitSubdir: Option[string],
+ dropPrivileges: bool): seq[string] =
+ if gitSubdir.isSome:
+ forkWaitRedirect(() => (block:
+ if not dropPrivileges or dropPrivileges():
+ execResult(gitCmd, "-C", repoPath, "ls-tree", "-r", "--name-only", "@",
+ gitSubdir.unsafeGet & "/")
+ else:
+ quit(1)))
+ .output
+ .map(s => s[gitSubdir.unsafeGet.len + 1 .. ^1])
+ else:
+ forkWaitRedirect(() => (block:
+ if not dropPrivileges or dropPrivileges():
+ execResult(gitCmd, "-C", repoPath, "ls-tree", "-r", "--name-only", "@")
+ else:
+ quit(1)))
+ .output
+
proc bisectVersion(repoPath: string, debug: bool, firstCommit: Option[string],
- compareMethod: string, gitSubdir: string, version: string): Option[string] =
+ compareMethod: string, gitSubdir: string, version: string,
+ dropPrivileges: bool): Option[string] =
template forkExecWithoutOutput(args: varargs[string]): int =
forkWait(() => (block:
discard close(0)
@@ -236,7 +294,7 @@ proc bisectVersion(repoPath: string, debug: bool, firstCommit: Option[string],
discard close(1)
discard close(2)
- if dropPrivileges():
+ if not dropPrivileges or dropPrivileges():
execResult(args)
else:
quit(1)))
@@ -245,7 +303,7 @@ proc bisectVersion(repoPath: string, debug: bool, firstCommit: Option[string],
(firstCommit, false)
else:
(forkWaitRedirect(() => (block:
- if dropPrivileges():
+ if not dropPrivileges or dropPrivileges():
execResult(gitCmd, "-C", repoPath,
"rev-list", "--max-parents=0", "@")
else:
@@ -253,7 +311,7 @@ proc bisectVersion(repoPath: string, debug: bool, firstCommit: Option[string],
.output.optLast, true)
let (realLastThreeCommits, _) = forkWaitRedirect(() => (block:
- if dropPrivileges():
+ if not dropPrivileges or dropPrivileges():
execResult(gitCmd, "-C", repoPath,
"rev-list", "--max-count=3", "@")
else:
@@ -273,7 +331,7 @@ proc bisectVersion(repoPath: string, debug: bool, firstCommit: Option[string],
none(string)
else:
let foundVersion = forkWaitRedirect(() => (block:
- if dropPrivileges():
+ if not dropPrivileges or dropPrivileges():
execResult(pkgLibDir & "/bisect",
compareMethod, repoPath & "/" & gitSubdir, version)
else:
@@ -317,7 +375,7 @@ proc bisectVersion(repoPath: string, debug: bool, firstCommit: Option[string],
"bisect", "run", pkgLibDir & "/bisect", compareMethod, gitSubdir, version)
let commit = forkWaitRedirect(() => (block:
- if dropPrivileges():
+ if not dropPrivileges or dropPrivileges():
execResult(gitCmd, "-C", repoPath,
"rev-list", "--max-count=1", "refs/bisect/bad")
else:
@@ -333,7 +391,8 @@ proc bisectVersion(repoPath: string, debug: bool, firstCommit: Option[string],
checkedCommit
else:
# non-incremental git history (e.g. downgrade without epoch change), bisect again
- bisectVersion(repoPath, debug, commit, compareMethod, gitSubdir, version)
+ bisectVersion(repoPath, debug, commit, compareMethod, gitSubdir,
+ version, dropPrivileges)
elif checkFirst and workFirstCommit.isSome:
checkCommit(workFirstCommit.unsafeGet)
else:
@@ -360,8 +419,52 @@ proc reloadPkgInfos*(config: Config, path: string, pkgInfos: seq[PackageInfo]):
else:
pkgInfos
+proc clonePackageRepoInternal(config: Config, base: string, version: string,
+ git: GitRepo, dropPrivileges: bool): Option[string] =
+ let tmpRoot = config.tmpRoot(dropPrivileges)
+ let repoPath = repoPath(tmpRoot, base)
+ removeDirQuiet(repoPath)
+
+ if forkWait(() => (block:
+ if not dropPrivileges or dropPrivileges():
+ execResult(gitCmd, "-C", tmpRoot,
+ "clone", "-q", git.url, "-b", git.branch,
+ "--single-branch", base)
+ else:
+ quit(1))) == 0:
+ let commit = bisectVersion(repoPath, config.debug, none(string),
+ "source", git.path, version, dropPrivileges)
+
+ if commit.isNone:
+ removeDirQuiet(repoPath)
+ none(string)
+ else:
+ discard forkWait(() => (block:
+ if not dropPrivileges or dropPrivileges():
+ execResult(gitCmd, "-C", repoPath,
+ "checkout", "-q", commit.unsafeGet)
+ else:
+ quit(1)))
+
+ some(repoPath)
+ else:
+ removeDirQuiet(repoPath)
+ none(string)
+
+proc clonePackageRepo*(config: Config, base: string, version: string,
+ git: GitRepo, dropPrivileges: bool): Option[string] =
+ let message = ensureTmpOrError(config, dropPrivileges)
+ if message.isSome:
+ message
+ else:
+ let repoPath = clonePackageRepoInternal(config, base, version, git, dropPrivileges)
+ if repoPath.isNone:
+ some(tr"$#: failed to clone git repository" % [base])
+ else:
+ none(string)
+
proc obtainBuildPkgInfosInternal(config: Config, bases: seq[LookupBaseGroup],
- pacmanTargetNames: seq[string], progressCallback: (int, int) -> void):
+ pacmanTargetNames: seq[string], progressCallback: (int, int) -> void, dropPrivileges: bool):
(seq[PackageInfo], seq[string], seq[string]) =
let lookupResults: seq[LookupGitResult] = bases
.map(b => (b, lookupGitRepo(b.repo, b.base, b.arch)))
@@ -371,43 +474,21 @@ proc obtainBuildPkgInfosInternal(config: Config, bases: seq[LookupBaseGroup],
let messages = notFoundRepos.map(r => tr"$#: repository not found" % [r.group.base])
(newSeq[PackageInfo](), newSeq[string](), messages)
else:
- let message = ensureTmpOrError(config)
+ let message = ensureTmpOrError(config, dropPrivileges)
if message.isSome:
(@[], @[], @[message.unsafeGet])
else:
proc findCommitAndGetSrcInfo(base: string, version: string,
repo: string, git: GitRepo): tuple[pkgInfos: seq[PackageInfo], path: Option[string]] =
- let repoPath = repoPath(config.tmpRoot, base)
- removeDirQuiet(repoPath)
-
- if forkWait(() => (block:
- if dropPrivileges():
- execResult(gitCmd, "-C", config.tmpRoot,
- "clone", "-q", git.url, "-b", git.branch,
- "--single-branch", base)
- else:
- quit(1))) == 0:
- let commit = bisectVersion(repoPath, config.debug, none(string),
- "source", git.path, version)
-
- if commit.isNone:
- removeDirQuiet(repoPath)
- (newSeq[PackageInfo](), none(string))
- else:
- discard forkWait(() => (block:
- if dropPrivileges():
- execResult(gitCmd, "-C", repoPath,
- "checkout", "-q", commit.unsafeGet)
- else:
- quit(1)))
-
- let srcInfo = obtainSrcInfo(repoPath & "/" & git.path)
- let pkgInfos = parseSrcInfo(repo, srcInfo, config.arch,
- git.url, some(git.path))
- .filter(i => i.version == version)
- (pkgInfos, some(repoPath))
+ let repoPath = clonePackageRepoInternal(config, base, version, git, dropPrivileges)
+
+ if repoPath.isSome:
+ let srcInfo = obtainSrcInfo(repoPath.unsafeGet & "/" & git.path)
+ let pkgInfos = parseSrcInfo(repo, srcInfo, config.arch,
+ git.url, some(git.path))
+ .filter(i => i.version == version)
+ (pkgInfos, repoPath)
else:
- removeDirQuiet(repoPath)
(newSeq[PackageInfo](), none(string))
progressCallback(0, lookupResults.len)
@@ -434,12 +515,12 @@ proc obtainBuildPkgInfosInternal(config: Config, bases: seq[LookupBaseGroup],
if errorMessages.len > 0:
for path in paths:
removeDirQuiet(path)
- discard rmdir(config.tmpRoot)
+ discard rmdir(config.tmpRoot(dropPrivileges))
(foundPkgInfos, paths, errorMessages)
proc obtainBuildPkgInfos*[T: RpcPackageInfo](config: Config,
- pacmanTargets: seq[FullPackageTarget[T]], progressCallback: (int, int) -> void):
- (seq[PackageInfo], seq[string], seq[string]) =
+ pacmanTargets: seq[FullPackageTarget[T]], progressCallback: (int, int) -> void,
+ dropPrivileges: bool): (seq[PackageInfo], seq[string], seq[string]) =
let bases = pacmanTargets
.map(proc (target: FullPackageTarget[T]): LookupBaseGroup =
let info = target.foundInfos[0]
@@ -448,20 +529,21 @@ proc obtainBuildPkgInfos*[T: RpcPackageInfo](config: Config,
.deduplicate
let pacmanTargetNames = pacmanTargets.map(t => t.reference.name)
- obtainBuildPkgInfosInternal(config, bases, pacmanTargetNames, progressCallback)
+ obtainBuildPkgInfosInternal(config, bases, pacmanTargetNames, progressCallback, dropPrivileges)
-proc cloneAurRepo*(config: Config, base: string, gitUrl: string): (int, Option[string]) =
- let repoPath = repoPath(config.tmpRoot, base)
+proc cloneAurRepo*(config: Config, base: string, gitUrl: string,
+ dropPrivileges: bool): (int, Option[string]) =
+ let repoPath = repoPath(config.tmpRoot(dropPrivileges), base)
- let message = ensureTmpOrError(config)
+ let message = ensureTmpOrError(config, dropPrivileges)
if message.isSome:
(1, message)
elif repoPath.existsDir():
(0, none(string))
else:
let cloneCode = forkWait(() => (block:
- if dropPrivileges():
- execResult(gitCmd, "-C", config.tmpRoot,
+ if not dropPrivileges or dropPrivileges():
+ execResult(gitCmd, "-C", config.tmpRoot(dropPrivileges),
"clone", "-q", gitUrl, "--single-branch", base)
else:
quit(1)))
@@ -472,7 +554,7 @@ proc cloneAurRepo*(config: Config, base: string, gitUrl: string): (int, Option[s
(0, none(string))
proc cloneAurReposWithPackageInfos*(config: Config, rpcInfos: seq[RpcPackageInfo],
- keepRepos: bool, progressCallback: (int, int) -> void):
+ keepRepos: bool, progressCallback: (int, int) -> void, dropPrivileges: bool):
(seq[PackageInfo], seq[PackageInfo], seq[string], seq[string]) =
let bases: seq[tuple[base: string, gitUrl: string]] = rpcInfos
.map(i => (i.base, i.gitUrl)).deduplicate
@@ -484,11 +566,11 @@ proc cloneAurReposWithPackageInfos*(config: Config, rpcInfos: seq[RpcPackageInfo
if index >= bases.len:
(toSeq(pkgInfos.items), toSeq(paths.items), toSeq(errors.items))
else:
- let repoPath = repoPath(config.tmpRoot, bases[index].base)
+ let repoPath = repoPath(config.tmpRoot(dropPrivileges), bases[index].base)
removeDirQuiet(repoPath)
let (cloneCode, cloneErrorMessage) = cloneAurRepo(config,
- bases[index].base, bases[index].gitUrl)
+ bases[index].base, bases[index].gitUrl, dropPrivileges)
progressCallback(index + 1, bases.len)
@@ -516,5 +598,5 @@ proc cloneAurReposWithPackageInfos*(config: Config, rpcInfos: seq[RpcPackageInfo
let names = rpcInfos.map(i => i.name).toSet
let additionalPkgInfos = fullPkgInfos.filter(i => not (i.name in names))
- discard rmdir(config.tmpRoot)
+ discard rmdir(config.tmpRoot(dropPrivileges))
(resultPkgInfos, additionalPkgInfos, paths, errors)
diff --git a/src/config.nim b/src/config.nim
index a6f65b9..ad57550 100644
--- a/src/config.nim
+++ b/src/config.nim
@@ -27,7 +27,8 @@ type
Config* = object of CommonConfig
root*: string
db*: string
- tmpRoot*: string
+ tmpRootInitial*: string
+ tmpRootCurrent*: string
color*: bool
aurComments*: bool
checkIgnored*: bool
@@ -112,10 +113,13 @@ proc obtainConfig*(config: PacmanConfig): Config =
let db = config.db
let color = config.colorMode.get
- let user = initialUser.get(currentUser)
- let tmpRoot = options.opt("TmpDir").get("/tmp/pakku-${USER}")
- .replace("${UID}", $user.uid)
- .replace("${USER}", user.name)
+ proc obtainTmpDir(user: User): string =
+ options.opt("TmpDir").get("/tmp/pakku-${USER}")
+ .replace("${UID}", $user.uid)
+ .replace("${USER}", user.name)
+
+ let tmpRootInitial = obtainTmpDir(initialUser.get(currentUser))
+ let tmpRootCurrent = obtainTmpDir(currentUser)
let aurComments = options.hasKey("AurComments")
let checkIgnored = options.hasKey("CheckIgnored")
let printAurNotFound = options.hasKey("PrintAurNotFound")
@@ -123,7 +127,8 @@ proc obtainConfig*(config: PacmanConfig): Config =
let viewNoDefault = options.hasKey("ViewNoDefault")
let preBuildCommand = options.opt("PreBuildCommand")
- Config(root: root, db: db, tmpRoot: tmpRoot, color: color,
+ Config(root: root, db: db,
+ tmpRootInitial: tmpRootInitial, tmpRootCurrent: tmpRootCurrent, color: color,
dbs: config.dbs, arch: config.arch, debug: config.debug, progressBar: config.progressBar,
verbosePkgList: config.verbosePkgList, pgpKeyserver: config.pgpKeyserver,
ignorePkgs: config.ignorePkgs, ignoreGroups: config.ignoreGroups,
diff --git a/src/feature/syncinfo.nim b/src/feature/syncinfo.nim
index 42d7e91..b0765f4 100644
--- a/src/feature/syncinfo.nim
+++ b/src/feature/syncinfo.nim
@@ -101,7 +101,7 @@ proc handleTarget(config: Config, padding: int, args: seq[Argument],
proc handleSyncInfo*(args: seq[Argument], config: Config): int =
let (_, callArgs) = checkAndRefresh(config.color, args)
- let targets = args.packageTargets
+ let targets = args.packageTargets(false)
let (syncTargets, checkAurNames) = withAlpm(config.root, config.db,
config.dbs, config.arch, handle, dbs, errors):
diff --git a/src/feature/syncinstall.nim b/src/feature/syncinstall.nim
index c214198..ab01df1 100644
--- a/src/feature/syncinstall.nim
+++ b/src/feature/syncinstall.nim
@@ -197,7 +197,7 @@ proc findDependencies(config: Config, handle: ptr AlpmHandle, dbs: seq[ptr AlpmD
let (rpcInfos, aerrors) = getRpcPackageInfos(aurCheck.map(r => r.name))
for e in aerrors: printError(config.color, e)
let (pkgInfos, additionalPkgInfos, paths, cerrors) =
- cloneAurReposWithPackageInfos(config, rpcInfos, not printMode, update)
+ cloneAurReposWithPackageInfos(config, rpcInfos, not printMode, update, true)
for e in cerrors: printError(config.color, e)
(pkgInfos, additionalPkgInfos, paths))
@@ -247,30 +247,7 @@ proc findDependencies(config: Config, handle: ptr AlpmHandle,
template clearPaths(paths: untyped) =
for path in paths:
removeDirQuiet(path)
- discard rmdir(config.tmpRoot)
-
-proc filterNotFoundSyncTargetsInternal(syncTargets: seq[SyncPackageTarget],
- pkgInfoReferencesTable: Table[string, PackageReference],
- upToDateNeededTable: Table[string, PackageReference]): seq[SyncPackageTarget] =
- # collect packages which were found neither in sync DB nor in AUR
- syncTargets.filter(t => not (upToDateNeededTable.opt(t.reference.name)
- .map(r => t.reference.isProvidedBy(r)).get(false)) and t.foundInfos.len == 0 and
- not (t.isAurTargetSync and pkgInfoReferencesTable.opt(t.reference.name)
- .map(r => t.reference.isProvidedBy(r)).get(false)))
-
-proc filterNotFoundSyncTargets[T: RpcPackageInfo](syncTargets: seq[SyncPackageTarget],
- pkgInfos: seq[T], upToDateNeededTable: Table[string, PackageReference]): seq[SyncPackageTarget] =
- let pkgInfoReferencesTable = pkgInfos.map(i => (i.name, i.toPackageReference)).toTable
- filterNotFoundSyncTargetsInternal(syncTargets, pkgInfoReferencesTable, upToDateNeededTable)
-
-proc printSyncNotFound(config: Config, notFoundTargets: seq[SyncPackageTarget]) =
- let dbs = config.dbs.toSet
-
- for target in notFoundTargets:
- if target.repo.isNone or target.repo == some("aur") or target.repo.unsafeGet in dbs:
- printError(config.color, trp("target not found: %s\n") % [$target.reference])
- else:
- printError(config.color, trp("database not found: %s\n") % [target.repo.unsafeGet])
+ discard rmdir(config.tmpRootInitial)
proc printUnsatisfied(config: Config,
satisfied: Table[PackageReference, SatisfyResult], unsatisfied: seq[PackageReference]) =
@@ -328,19 +305,7 @@ proc editLoop(config: Config, base: string, repoPath: string, gitSubdir: Option[
else:
res
- let rawFiles = if gitSubdir.isSome:
- forkWaitRedirect(() => (block:
- dropPrivilegesAndChdir(none(string)):
- execResult(gitCmd, "-C", repoPath, "ls-tree", "-r", "--name-only", "@",
- gitSubdir.unsafeGet & "/")))
- .output
- .map(s => s[gitSubdir.unsafeGet.len + 1 .. ^1])
- else:
- forkWaitRedirect(() => (block:
- dropPrivilegesAndChdir(none(string)):
- execResult(gitCmd, "-C", repoPath, "ls-tree", "-r", "--name-only", "@")))
- .output
-
+ let rawFiles = getGitFiles(repoPath, gitSubdir, true)
let files = ("PKGBUILD" & rawFiles.filter(x => x != ".SRCINFO")).deduplicate
proc editFileLoopAll(index: int): char =
@@ -355,7 +320,7 @@ proc editLoop(config: Config, base: string, repoPath: string, gitSubdir: Option[
proc buildLoop(config: Config, pkgInfos: seq[PackageInfo], noconfirm: bool,
noextract: bool): (Option[BuildResult], int, bool) =
let base = pkgInfos[0].base
- let repoPath = repoPath(config.tmpRoot, base)
+ let repoPath = repoPath(config.tmpRootInitial, base)
let gitSubdir = pkgInfos[0].gitSubdir
let buildPath = buildPath(repoPath, gitSubdir)
@@ -365,7 +330,7 @@ proc buildLoop(config: Config, pkgInfos: seq[PackageInfo], noconfirm: bool,
else:
confFileEnv
- let workConfFile = config.tmpRoot & "/makepkg.conf"
+ let workConfFile = config.tmpRootInitial & "/makepkg.conf"
let workConfFileCopySuccess = try:
copyFile(confFile, workConfFile)
@@ -377,7 +342,7 @@ proc buildLoop(config: Config, pkgInfos: seq[PackageInfo], noconfirm: bool,
file.writeLine("# PAKKU OVERRIDES")
file.writeLine('#'.repeat(73))
file.writeLine("CARCH=" & config.arch.bashEscape)
- file.writeLine("PKGDEST=" & config.tmpRoot.bashEscape)
+ file.writeLine("PKGDEST=" & config.tmpRootInitial.bashEscape)
finally:
file.close()
true
@@ -458,7 +423,7 @@ proc buildLoop(config: Config, pkgInfos: seq[PackageInfo], noconfirm: bool,
proc buildFromSources(config: Config, commonArgs: seq[Argument],
pkgInfos: seq[PackageInfo], noconfirm: bool): (Option[BuildResult], int) =
let base = pkgInfos[0].base
- let repoPath = repoPath(config.tmpRoot, base)
+ let repoPath = repoPath(config.tmpRootInitial, base)
let gitSubdir = pkgInfos[0].gitSubdir
proc loop(noextract: bool, showEditLoop: bool): (Option[BuildResult], int) =
@@ -536,7 +501,7 @@ proc installGroupFromSources(config: Config, commonArgs: seq[Argument],
proc formatArchiveFile(pkgInfo: PackageInfo, ext: string): string =
let arch = if config.arch in pkgInfo.archs: config.arch else: "any"
- config.tmpRoot & "/" & pkgInfo.name & "-" & pkgInfo.version & "-" & arch & ext
+ config.tmpRootInitial & "/" & pkgInfo.name & "-" & pkgInfo.version & "-" & arch & ext
let allFiles = lc[(r.name, formatArchiveFile(r.pkgInfo, br.ext)) |
(br <- buildResults, r <- br.replacePkgInfos), tuple[name: Option[string], file: string]]
@@ -554,7 +519,7 @@ proc installGroupFromSources(config: Config, commonArgs: seq[Argument],
discard
if not clear:
- printWarning(config.color, tr"packages are saved to '$#'" % [config.tmpRoot])
+ printWarning(config.color, tr"packages are saved to '$#'" % [config.tmpRootInitial])
if buildCode != 0:
handleTmpRoot(true)
@@ -654,7 +619,7 @@ proc confirmViewAndImportKeys(config: Config, basePackages: seq[seq[seq[PackageI
if index < flatBasePackages.len:
let pkgInfos = flatBasePackages[index]
let base = pkgInfos[0].base
- let repoPath = repoPath(config.tmpRoot, base)
+ let repoPath = repoPath(config.tmpRootInitial, base)
let aur = pkgInfos[0].repo == "aur"
@@ -1072,7 +1037,7 @@ proc obtainAurPackageInfos(config: Config, rpcInfos: seq[RpcPackageInfo],
else: (block:
let (rpcInfos, aerrors) = getRpcPackageInfos(fullRpcInfos.map(i => i.name))
let (pkgInfos, additionalPkgInfos, paths, cerrors) =
- cloneAurReposWithPackageInfos(config, rpcInfos, not printMode, update)
+ cloneAurReposWithPackageInfos(config, rpcInfos, not printMode, update, true)
(pkgInfos, additionalPkgInfos, paths, (toSeq(aerrors.items) & cerrors).deduplicate))
terminate()
@@ -1113,7 +1078,7 @@ proc obtainPacmanBuildTargets(config: Config, pacmanTargets: seq[FullPackageTarg
let (buildPkgInfos, buildPaths, obtainErrorMessages) = if checkPacmanBuildPkgInfos: (block:
echo(tr"checking official repositories...")
let (update, terminate) = createCloneProgress(config, pacmanTargets.len, printMode)
- let res = obtainBuildPkgInfos[PackageInfo](config, pacmanTargets, update)
+ let res = obtainBuildPkgInfos[PackageInfo](config, pacmanTargets, update, true)
terminate()
res)
else:
@@ -1259,7 +1224,7 @@ proc handleSyncInstall*(args: seq[Argument], config: Config): int =
arg.matchOption(%%%"noconfirm")).optLast
.map(arg => arg.key == "noconfirm").get(false)
- let targets = args.packageTargets
+ let targets = args.packageTargets(false)
withAur():
let (code, installed, foreignUpgrade, targetNamesSet, pacmanTargets,
diff --git a/src/feature/syncsource.nim b/src/feature/syncsource.nim
new file mode 100644
index 0000000..fba7062
--- /dev/null
+++ b/src/feature/syncsource.nim
@@ -0,0 +1,173 @@
+import
+ future, options, os, posix, sequtils, strutils, tables,
+ "../args", "../aur", "../common", "../config", "../format", "../lists",
+ "../package", "../pacman", "../utils",
+ "../wrapper/alpm"
+
+type
+ BaseTarget = tuple[
+ base: string,
+ version: string,
+ destination: string,
+ aurGitUrl: Option[string],
+ gitRepo: Option[GitRepo]
+ ]
+
+ CloneResult = tuple[
+ base: string,
+ path: string,
+ files: seq[string],
+ destination: string
+ ]
+
+proc getFilesOrClear(base: string, repoPath: string, gitSubdir: Option[string]):
+ (seq[string], Option[string]) =
+ let rawFiles = getGitFiles(repoPath, gitSubdir, false)
+ .filter(f => f != ".gitignore" and f != ".SRCINFO" and f.find('/') < 0)
+ .map(f => gitSubdir.map(s => s & "/" & f).get(f))
+
+ if rawFiles.len > 0:
+ (rawFiles, none(string))
+ else:
+ removeDirQuiet(repoPath)
+ (newSeq[string](), some(tr"$#: failed to clone git repository" % [base]))
+
+proc cloneRepositories(config: Config, targets: seq[BaseTarget],
+ update: (int, int) -> void): (List[CloneResult], List[string]) =
+ proc cloneNext(index: int, results: List[CloneResult], messages: List[string]):
+ (List[CloneResult], List[string]) =
+ update(index, targets.len)
+
+ if index >= targets.len:
+ (results.reversed, messages.reversed)
+ else:
+ let target = targets[index]
+ let repoPath = repoPath(config.tmpRootCurrent, target.base)
+ removeDirQuiet(repoPath)
+
+ if target.aurGitUrl.isSome:
+ let (cloneCode, cerror) = cloneAurRepo(config,
+ target.base, target.aurGitUrl.unsafeGet, false)
+
+ if cloneCode != 0:
+ cloneNext(index + 1, results, toSeq(cerror.items) ^& messages)
+ else:
+ let (files, ferror) = getFilesOrClear(target.base, repoPath, none(string))
+ if ferror.isSome:
+ cloneNext(index + 1, results, ferror.unsafeGet ^& messages)
+ else:
+ cloneNext(index + 1, (target.base, repoPath, files,
+ target.destination) ^& results, messages)
+ elif target.gitRepo.isSome:
+ let gitRepo = target.gitRepo.unsafeGet
+ let cerror = clonePackageRepo(config, target.base,
+ target.version, gitRepo, false)
+
+ if cerror.isSome:
+ cloneNext(index + 1, results, cerror.unsafeGet ^& messages)
+ else:
+ let (files, ferror) = getFilesOrClear(target.base, repoPath, some(gitRepo.path))
+ if ferror.isSome:
+ cloneNext(index + 1, results, ferror.unsafeGet ^& messages)
+ else:
+ cloneNext(index + 1, (target.base, repoPath, files,
+ target.destination) ^& results, messages)
+ else:
+ let message = tr"$#: repository not found" % [target.base]
+ cloneNext(index + 1, results, message ^& messages)
+
+ cloneNext(0, nil, nil)
+
+proc copyFiles(config: Config, quiet: bool, results: seq[CloneResult]): List[string] =
+ proc copyNext(index: int, messages: List[string]): List[string] =
+ if index >= results.len:
+ messages.reversed
+ else:
+ let res = results[index]
+ discard mkdir(res.destination, 0o755)
+
+ let error = try:
+ for f in res.files:
+ let index = f.rfind('/')
+ let name = if index >= 0: f[index + 1 .. ^1] else: f
+ let dest = if res.destination == ".": name else: res.destination & "/" & name
+ moveFile(res.path & "/" & f, dest)
+ printFile(config.color, quiet, res.base, dest)
+ none(string)
+ except OSError:
+ some(tr"$#: failed to move files" % [res.base])
+
+ copyNext(index + 1, toSeq(error.items) ^& messages)
+
+ copyNext(0, nil)
+
+proc cloneAndCopy(config: Config, quiet: bool,
+ fullTargets: seq[FullPackageTarget[RpcPackageInfo]]): int =
+ let baseTargets = fullTargets.foldl(block:
+ let bases = a.map(x => x.base)
+ if b.isAurTargetFull:
+ let rpcInfo = b.pkgInfo.get
+ if rpcInfo.base in bases:
+ a
+ else:
+ a & (rpcInfo.base, rpcInfo.version, b.destination.get(rpcInfo.base),
+ some(rpcInfo.gitUrl), none(GitRepo))
+ else:
+ let foundInfo = b.foundInfos[0]
+ let pkg = foundInfo.pkg.get
+ if pkg.base in bases:
+ a
+ else:
+ let git = lookupGitRepo(foundInfo.repo, pkg.base, pkg.arch.get)
+ a & (pkg.base, pkg.version, b.destination.get(pkg.base),
+ none(string), git),
+ newSeq[BaseTarget]())
+
+ let (update, terminate) = if quiet:
+ (proc (a: int, b: int) {.closure.} = discard, proc () {.closure.} = discard)
+ else:
+ printProgressShare(config.progressBar, tr"cloning repositories")
+
+ let (results, rerrors) = cloneRepositories(config, baseTargets, update)
+ terminate()
+ for e in rerrors: printError(config.color, e)
+
+ let cerrors = copyFiles(config, quiet, toSeq(results.items))
+ for e in cerrors: printError(config.color, e)
+
+ for result in results:
+ removeDirQuiet(result.path)
+ discard rmdir(config.tmpRootCurrent)
+
+ if rerrors != nil and cerrors != nil:
+ 1
+ else:
+ 0
+
+proc handleSyncSource*(args: seq[Argument], config: Config): int =
+ discard checkAndRefresh(config.color, args)
+
+ let quiet = args.check(%%%"quiet")
+ let targets = args.packageTargets(true)
+
+ if targets.len == 0:
+ printError(config.color, trp("no targets specified (use -h for help)\n"))
+ 1
+ else:
+ let (syncTargets, checkAurNames) = withAlpm(config.root, config.db,
+ config.dbs, config.arch, handle, dbs, errors):
+ for e in errors: printError(config.color, e)
+ findSyncTargets(handle, dbs, targets, false, false)
+
+ let (rpcInfos, aerrors) = getRpcPackageInfos(checkAurNames)
+ for e in aerrors: printError(config.color, e)
+
+ let notFoundTargets = filterNotFoundSyncTargets(syncTargets,
+ rpcInfos, initTable[string, PackageReference]())
+
+ if notFoundTargets.len > 0:
+ printSyncNotFound(config, notFoundTargets)
+ 1
+ else:
+ let fullTargets = mapAurTargets[RpcPackageInfo](syncTargets, rpcInfos)
+ cloneAndCopy(config, quiet, fullTargets)
diff --git a/src/format.nim b/src/format.nim
index c3a7e5e..bb5ae1f 100644
--- a/src/format.nim
+++ b/src/format.nim
@@ -65,6 +65,12 @@ proc printError*(color: bool, s: string) =
proc printWarning*(color: bool, s: string) =
stderr.writeLine(^Color.yellow, trp"warning: ", ^Color.normal, s)
+proc printFile*(color: bool, quiet: bool, name: string, file: string) =
+ if quiet:
+ echo(file)
+ else:
+ echo(^Color.bold, name, ^Color.normal, ' ', file)
+
proc formatPkgRating*(votes: int, popularity: float): string =
$votes & " / " & formatFloat(popularity, format = ffDecimal, precision = 6)
diff --git a/src/main.nim b/src/main.nim
index fbf3364..bf32cdd 100644
--- a/src/main.nim
+++ b/src/main.nim
@@ -4,8 +4,9 @@ import
import
"feature/syncinfo",
- "feature/syncsearch",
"feature/syncinstall",
+ "feature/syncsearch",
+ "feature/syncsource",
"feature/localquery"
proc passValidation(args: seq[Argument], config: Config,
@@ -82,6 +83,8 @@ proc handleSync(args: seq[Argument], config: Config): int =
elif syncArgs.check(%%%"search") and
syncArgs.checkOpGroup(OpGroup.syncSearch):
handleSyncSearch(args, config)
+ elif syncArgs.check(%%%"source"):
+ handleSyncSource(args, config)
elif syncArgs.checkOpGroup(OpGroup.syncInstall) and
(args.check(%%%"sysupgrade") or args.targets.len > 0):
let printMode = args.check(%%%"print") or args.check(%%%"print-format")
@@ -185,6 +188,7 @@ proc handleHelp(operation: OperationType) =
printHelp(%%%"build", none(string), tr"build targets from source")
printHelp(%%%"keyserver", some("name"), tr"use name as keyserver to receive keys from")
printHelp(%%%"noaur", none(string), tr"disable all AUR operations")
+ printHelp(%%%"source", none(string), tr"retrieve PKGBUILD source")
else:
discard
diff --git a/src/pacman.nim b/src/pacman.nim
index f5df4e4..f9e921c 100644
--- a/src/pacman.nim
+++ b/src/pacman.nim
@@ -142,7 +142,8 @@ const
o("y", "refresh") + g(syncInstall, syncSearch, syncQuery),
$o("n", "build") + g(syncInstall),
$(^o("keyserver")) + g(syncInstall),
- $o("noaur") + g(syncInstall)
+ $o("noaur") + g(syncInstall),
+ $o("z", "source") + g()
]
databaseOptions*: seq[CommandOption] = @[
@@ -177,7 +178,9 @@ const
("asdeps", @["asexplicit"]),
("build", @["nodeps", "assume-installed", "dbonly", "clean",
"groups", "info", "list", "search", "sysupgrade", "downloadonly"]),
- ("keyserver", @["clean", "groups", "info", "list", "search"])
+ ("keyserver", @["clean", "groups", "info", "list", "search"]),
+ ("source", @["clean", "groups", "info", "list", "search", "sysupgrade",
+ "downloadonly", "build", "keyserver", "noaur"])
]
allConflictingOptions = syncConflictingOptions