diff --git a/lib/pkgman.lua b/lib/pkgman.lua new file mode 100644 index 0000000..183cf01 --- /dev/null +++ b/lib/pkgman.lua @@ -0,0 +1,152 @@ +local serial = require "serialization" +local dl = require "download" +local pkg = {} +pkg.cfgPath = "/boot/cfg/pkg" +pkg.sourcePath = pkg.cfgPath .. "/sources.cfg" +pkg.installedPath = pkg.cfgPath .. "/installed.cfg" + +local function getSources() + local f = io.open(pkg.sourcePath,"rb") + if not f then return {} end + local c = f:read("*a") + f:close() + return serial.unserialize(c) +end +local function saveSources(t) + fs.makeDirectory(pkg.cfgPath) + local f = io.open(pkg.sourcePath,"wb") + f:write(serial.serialize(t)) + f:close() +end + +local function getInstalled() + local f = io.open(pkg.installedPath,"rb") + if not f then return {} end + local c = f:read("*a") + f:close() + return serial.unserialize(c) +end +local function saveInstalled(t) + fs.makeDirectory(pkg.cfgPath) + local f = io.open(pkg.installedPath,"wb") + if not f then return false end + f:write(serial.serialize(t)) + f:close() +end + +local function getRepoMeta(repo) + if not getSources()[repo].cache or not fs.exists("/boot/cfg/pkg/repo-"..repo..".cfg") then + dl(getSources()[repo].path.."/packages.cfg","/boot/cfg/pkg/repo-"..repo..".cfg") + end + local f = io.open("/boot/cfg/pkg/repo-"..repo..".cfg","rb") + local rt = serial.unserialize(f:read("*a")) + f:close() + if not getSources()[repo].cache then + fs.remove("/boot/cfg/pkg/repo-"..repo..".cfg") + end + return rt +end + +local function activatePackage(path,compressed) + require("pkgfs").add(path,compressed) +end +local function deactivatePackage(path) + require("pkgfs").remove(path) +end + +function pkg.addRepo(name,path,cache) -- string string boolean -- boolean -- Adds a repository, referred to as *name*, to the list of package sources, with the remote path *path*. If *cache* is set, keep a local copy of the repository index. + local sources = getSources() + sources[name] = {path=path,cache=cache,name=name} + saveSources(sources) +end + +function pkg.delRepo(name) -- string -- boolean -- Removes a repository from the list of repositories. + local sources = getSources() + sources[name] = nil + saveSources(sources) +end + +function pkg.update() -- Re-download cached repository indexes. + for repo,meta in pairs(getSources()) do + fs.remove("/boot/cfg/pkg/repo-"..repo..".cfg") + if meta.cache then + getRepoMeta(repo) + end + end +end + +function pkg.list(filter) -- string -- -- Print a list of available packages matching *filter*. + filter = filter or "" + local pkglist = {} + for repo,_ in pairs(getSources()) do + for pkg,meta in pairs(getRepoMeta(repo)) do + if pkg:find(filter) or (pkg.meta or ""):find(filter) then + meta.repo = repo + pkglist[pkg] = meta + end + end + end + for k,v in pairs(pkglist) do + print(string.format("%s/%s\n %s",v.repo,k,v.description)) + end +end + +function pkg.getMeta(pkgname) -- string -- table -- Returns the metadata for a the package specified in *pkgname*. + print("Finding package "..pkgname) + for repo,info in pairs(getSources()) do + local pkg = getRepoMeta(repo)[pkgname] + if pkg then + print("Package "..pkgname.." located in repo "..repo.." at "..info.path) + pkg.repository = info + return pkg + end + end +end + +function pkg.get(pkgname,auto) -- string boolean -- boolean -- Downloads and mounts a package, identified as *pkgname*, onto the pkgfs. + local pkginfo = pkg.getMeta(pkgname) + if not pkginfo then error("unable to locate package "..pkgname) end + pkginfo.manual = not auto + fs.makeDirectory("/boot/pkg") + for k,v in ipairs(pkginfo.dependencies or {}) do + if not getInstalled()[v] then + pkg.get(v) + end + end + dl(pkginfo.repository.path.."/"..pkginfo.filename,"/boot/pkg/"..pkginfo.filename) + local installed = getInstalled() + installed[pkgname] = pkginfo + saveInstalled(installed) + pcall(activatePackage,"/boot/pkg/"..pkginfo.filename,pkginfo.compressed) + return true +end + +function pkg.upgrade(force) -- boolean -- boolean -- Upgrades all packages on the system to the current version stored in the relevant repository. If *force* is set, re-download all packages. + pkg.update() + fs.makeDirectory("/boot/pkg") + local installed = getInstalled() + for repo,info in pairs(getSources()) do + for pkgname,pkg in pairs(getRepoMeta(repo)) do + if pkg.version ~= installed[pkgname].version or force then + dl(info.path.."/"..pkg.filename,"/boot/pkg/"..pkg.filename) + installed[pkgname] = pkg + pcall(activatePackage,"/boot/pkg/"..pkg.filename,pkg.compressed) + return true + end + end + end + saveInstalled(installed) +end + +function pkg.remove(pkgname) -- string -- boolean -- Remove the package *pkgname* from the pkgfs and package directory. + local installed = getInstalled() + local pkginfo = installed[pkgname] + if not pkginfo then error(pkgname .." not installed") end + pcall(deactivatePackage,"/boot/pkg/"..pkginfo.filename) + fs.remove("/boot/pkg/"..pkginfo.filename) + installed[pkgname] = nil + saveInstalled(installed) + return true +end + +return pkg