177 lines
3.6 KiB
Crystal
177 lines
3.6 KiB
Crystal
require "colorize"
|
||
require "uuid"
|
||
require "file_utils"
|
||
|
||
require "../io.cr"
|
||
|
||
require "./weird.cr"
|
||
|
||
class Package::Context < Weird::Context
|
||
property root = "/"
|
||
|
||
# Stores informations about already installed packages, their
|
||
# manifests, and so on.
|
||
@database = Database.new
|
||
|
||
def initialize()
|
||
end
|
||
|
||
def root=(@root)
|
||
@database.root = "#{@root}#{Database::PATH_FROM_ROOT}"
|
||
end
|
||
|
||
def install(package : Package, data_directory : String)
|
||
end
|
||
|
||
# FIXME: Should atomic upgrades be handled here?
|
||
def install(files : Array(String))
|
||
work_dirs = Hash(String, String).new
|
||
manifests = Hash(String, Manifest).new
|
||
packages = Hash(String, Package).new
|
||
|
||
files.each do |file|
|
||
work_dir = get_work_directory
|
||
data_dir = "#{work_dir}/data"
|
||
|
||
work_dirs[file] = work_dir
|
||
|
||
Weird.extract file, work_dir, data_dir
|
||
|
||
package = Package.new "#{work_dir}/control.spec"
|
||
packages[file] = package
|
||
|
||
if is_installed? package.to_atom
|
||
raise ::Package::Exception.new "Package '#{package.to_atom.to_s}' is already installed."
|
||
end
|
||
|
||
Manifest.new("#{work_dir}/manifest").tap do |manifest|
|
||
manifest.no_collisions! self
|
||
|
||
manifests[file] = manifest
|
||
end
|
||
|
||
# FIXME: Check signatures (not implemented in packages ATM).
|
||
end
|
||
|
||
|
||
files.each do |file|
|
||
info "Installing '#{file}'"
|
||
|
||
work_dir = work_dirs[file]
|
||
data_dir = "#{work_dir}/data"
|
||
|
||
package = packages[file]
|
||
manifest = manifests[file]
|
||
|
||
manifest.each do |entry|
|
||
if entry.type == "directory" && Dir.exists? entry.file
|
||
next
|
||
end
|
||
|
||
debug "++ #{entry.file}"
|
||
entry.install data_dir, @root
|
||
end
|
||
|
||
@database << {package, manifest}
|
||
|
||
FileUtils.rm_r work_dir
|
||
end
|
||
end
|
||
def install(file : String)
|
||
install [file]
|
||
end
|
||
def install(atoms : Array(Atom))
|
||
end
|
||
def install(atom : Atom)
|
||
install [atom]
|
||
end
|
||
def upgrade(atom : Atom)
|
||
end
|
||
|
||
def remove(packages : Array(Package))
|
||
# First part of this function is about extracting
|
||
# reverse-dependencies. Once we have them all, we remove
|
||
# everything one by one.
|
||
all_packages = @database.get_all_packages
|
||
packages_to_remove = [] of Package
|
||
unchecked_packages = Deque(Package).new
|
||
|
||
packages.each do |package|
|
||
unchecked_packages << package
|
||
end
|
||
|
||
while unchecked_packages.size > 0
|
||
package = unchecked_packages.pop
|
||
|
||
packages_to_remove << package
|
||
|
||
all_packages
|
||
.select do |p|
|
||
p.dependencies.any? do |dependency|
|
||
# FIXME: Check full atoms and not just names.
|
||
|
||
# FIXME: Checking there’s no collision with world files is probably done around here.
|
||
packages_to_remove.find &.name.==(dependency)
|
||
end
|
||
end
|
||
.each do |p|
|
||
if unchecked_packages.find &.==(p)
|
||
next
|
||
end
|
||
|
||
if packages_to_remove.find &.==(p)
|
||
next
|
||
end
|
||
|
||
unchecked_packages << p
|
||
end
|
||
end
|
||
|
||
packages_to_remove.reverse.each do |package|
|
||
manifest = @database.get_manifest package
|
||
|
||
manifest.reverse.each do |entry|
|
||
if entry.type == "directory" && (!Dir.exists?(entry.file) || !Dir.empty?(entry.file))
|
||
next
|
||
end
|
||
|
||
debug "-- #{entry.file}"
|
||
entry.remove @root
|
||
end
|
||
|
||
@database.remove package
|
||
end
|
||
end
|
||
def remove(package : Package)
|
||
remove [package]
|
||
end
|
||
def remove(atoms : Array(Atom))
|
||
remove atoms.map { |atom| @database.get_package atom }
|
||
end
|
||
def remove(atom : Atom)
|
||
remove [atom]
|
||
end
|
||
|
||
def installed_packages : Array(Package)
|
||
end
|
||
|
||
def is_installed?(atom : Atom)
|
||
@database.is_installed? atom
|
||
end
|
||
|
||
def update_repository_cache
|
||
end
|
||
def download_to_cache(atom : Atom, url : String)
|
||
end
|
||
|
||
def database_directory
|
||
end
|
||
|
||
def get_work_directory
|
||
directory = "/tmp/package-#{UUID.random}"
|
||
FileUtils.mkdir_p directory
|
||
directory
|
||
end
|
||
end
|
||
|