forked from kiss-community/kiss
335 lines
9.5 KiB
Bash
Executable File
335 lines
9.5 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# kiss - package manager for kiss linux.
|
|
|
|
die() {
|
|
# Print a message and exit with '1' (error).
|
|
printf '\033[31m!>\033[m %s\n' "$@" >&2
|
|
exit 1
|
|
}
|
|
|
|
log() {
|
|
# Print a message with a colorful arrow to distinguish
|
|
# from other output.
|
|
printf '\033[32m=>\033[m %s\n' "$@"
|
|
}
|
|
|
|
source_type() {
|
|
# Figure out what kind of source we are dealing with.
|
|
# This removes the need to repeat these same tests
|
|
# in each function.
|
|
[ -z "$1" ] && return 1 # No file.
|
|
[ -f "$1" ] && return 2 # Local file.
|
|
[ -f "$src_dir/${1##*/}" ] && return 3 # Cached downloaded file.
|
|
[ -z "${1##git:*}" ] && return 4 # Git repository.
|
|
[ -z "${1##*://*}" ] && return 5 # Remote file.
|
|
}
|
|
|
|
pkg_clean() {
|
|
# Clean up on exit or error. This removes everything related
|
|
# to the build.
|
|
rm -rf -- "$mak_dir" "$pkg_dir" "$tar_dir" \
|
|
"$cac_dir/manifest-$$" "$cac_dir/checksums-$$" \
|
|
"$cac_dir/mv" "$cac_dir/mkdir" "$cac_dir/find"
|
|
}
|
|
|
|
pkg_search() {
|
|
# Figure out which repository a package belongs to by
|
|
# searching for directories matching the package name
|
|
# in $KISS_PATH.
|
|
|
|
# Disable globbing with 'set -f' to ensure that the unquoted
|
|
# variable doesn't expand to anything nasty.
|
|
# shellcheck disable=2086,2046
|
|
{
|
|
set -f
|
|
set -- "$1" $(IFS=:; find $KISS_PATH -maxdepth 1 -name "$1")
|
|
set +f
|
|
}
|
|
|
|
[ -z "$2" ] && die "Package '$1' not in any repository."
|
|
|
|
printf '%s\n' "$2"
|
|
}
|
|
|
|
pkg_setup() {
|
|
# Check that each mandatory file in the package entry exists.
|
|
pkg_location=$(pkg_search "$1")
|
|
|
|
cd "$pkg_location" || die "'$pkg_location' not accessible"
|
|
[ -f sources ] || die "Sources file not found."
|
|
[ -x build ] || die "Build file not found or not executable."
|
|
[ -f licenses ] || die "License file not found or empty."
|
|
|
|
read -r pkg_ver pkg_rel < version || die "Version file not found."
|
|
pkg_name=$1
|
|
pkg_tar=$name\#$ver-$rel.tar.gz
|
|
}
|
|
|
|
pkg_depends() {
|
|
[ -f depends ] && while read -r dep opt; do
|
|
pkg_list "$dep" || {
|
|
[ "$1" = install ] && [ "$opt" = make ] && continue
|
|
|
|
case $missing in
|
|
*" $dep,"*) ;;
|
|
*) missing="$missing $dep,"
|
|
pkg_setup "$dep"
|
|
pkg_depends ;;
|
|
esac
|
|
}
|
|
done < depends
|
|
}
|
|
|
|
pkg_sources() {
|
|
src_dir=$cac_dir/sources/$name
|
|
mkdir -p "$src_dir"
|
|
|
|
while read -r src _; do
|
|
case $(source_type "$src"; echo $?) in
|
|
4) git clone "${src##git:}" "$mak_dir" ;;
|
|
5) wget -P "$src_dir" "$src" || die "Failed to download $src." ;;
|
|
0|1) die "Source file '$src' not found." ;;
|
|
esac
|
|
done < sources
|
|
}
|
|
|
|
pkg_checksum() {
|
|
while read -r src _; do
|
|
case $(source_type "$src"; echo $?) in
|
|
2) src_path=$src ;;
|
|
3) src_path=$src_dir/${src##*/} ;;
|
|
4) continue
|
|
esac
|
|
|
|
(cd "${src_path%/*}" >/dev/null; sha256sum "${src##*/}") ||
|
|
die "Failed to generate checksums."
|
|
done < sources > "${1-checksums}"
|
|
}
|
|
|
|
pkg_verify() {
|
|
pkg_checksum "$cac_dir/checksums-$$"
|
|
|
|
cmp -s "$cac_dir/checksums-$$" checksums ||
|
|
die "Checksum mismatch, run '$kiss checksum $name'."
|
|
}
|
|
|
|
pkg_extract() {
|
|
while read -r src dest; do
|
|
[ "$dest" ] && mkdir -p "$mak_dir/$dest"
|
|
|
|
case $(source_type "$src"; echo $?)-$src in
|
|
2-*) cp -f "$src" "$mak_dir/$dest" ;;
|
|
|
|
3-*.tar*|3-*.tgz)
|
|
tar xf "$src_dir/${src##*/}" -C "$mak_dir/$dest" \
|
|
--strip-components 1 || die "Couldn't extract ${src##*/}" ;;
|
|
|
|
[01]-*) die "${src##*/} not found."
|
|
esac
|
|
done < sources
|
|
}
|
|
|
|
pkg_build() {
|
|
(cd "$mak_dir"; "$OLDPWD/build" "$pkg_dir") || die "Build failed."
|
|
cp -Rf "$rep_dir/$name" "$pkg_db"
|
|
log "Sucessfully built $pkg." 2> "$pkg_db/$name/manifest"
|
|
}
|
|
|
|
pkg_strip() {
|
|
log "Stripping unneeded symbols from binaries and libraries."
|
|
|
|
find "$pkg_dir" -type f | while read -r binary; do
|
|
case $(file -bi "$binary") in
|
|
application/x-sharedlib*|application/x-pie-executable*)
|
|
strip_opts=--strip-unneeded
|
|
;;
|
|
|
|
application/x-archive*) strip_opts=--strip-debug ;;
|
|
application/x-executable*) strip_opts=--strip-all ;;
|
|
|
|
*) continue ;;
|
|
esac
|
|
|
|
strip "$strip_opts" "$binary" 2>/dev/null
|
|
done
|
|
}
|
|
|
|
pkg_manifest() {
|
|
# Store the file and directory list of the package.
|
|
# Directories have a trailing '/' and the list is sorted in reverse.
|
|
(cd "$pkg_dir" && find ./* -type d -exec printf '%s/\n' {} + -or -print) |
|
|
sort -r | sed -e ss.ss > "$pkg_db/$name/manifest"
|
|
}
|
|
|
|
pkg_tar() {
|
|
tar zpcf "$bin_dir/$pkg" -C "$pkg_dir" . || die "Failed to create package."
|
|
log "Use '$kiss install $name' to install the package."
|
|
}
|
|
|
|
pkg_conflicts() {
|
|
log "Checking for package conflicts."
|
|
|
|
# Extract manifest from tarball and strip directories.
|
|
tar xf "$bin_dir/$pkg" -O "./var/db/$kiss/$name/manifest" |
|
|
while read -r line; do
|
|
[ "${line%%*/}" ] && printf '%s\n' "$line" >> "$cac_dir/manifest-$$"
|
|
done
|
|
|
|
# Compare extracted manifest to all installed manifests.
|
|
# If there are matching lines (files) there's a package
|
|
# conflict.
|
|
for db in "$sys_db"/*; do
|
|
[ "$name" = "${db##*/}" ] && continue
|
|
|
|
grep -Fxf "$cac_dir/manifest-$$" "$db/manifest" 2>/dev/null &&
|
|
die "Package '$name' conflicts with '${db##*/}'."
|
|
done
|
|
}
|
|
|
|
pkg_install() {
|
|
[ -f "$bin_dir/$pkg" ] || args b "$name"
|
|
|
|
pkg_conflicts
|
|
tar pxf "$bin_dir/$pkg" -C "$tar_dir/" || die "Failed to extract tarball."
|
|
|
|
# Create a backup of 'mv', 'mkdir' and 'find' so they aren't removed
|
|
# during package removal.
|
|
cp "$(command -v mv)" "$cac_dir"
|
|
cp "$(command -v mkdir)" "$cac_dir"
|
|
cp "$(command -v find)" "$cac_dir"
|
|
|
|
log "Removing previous version of package if it exists."
|
|
pkg_remove
|
|
|
|
cd "$tar_dir" || die "Aborting due to tar error."
|
|
|
|
# Optimization: Only find the deepest directories.
|
|
"$cac_dir/find" . -type d -links -3 -prune | while read -r dir; do
|
|
"$cac_dir/mkdir" -p "$sys_dir/${dir#./}"
|
|
done
|
|
|
|
"$cac_dir/find" ./ -mindepth 1 -not -type d | while read -r file; do
|
|
rpath=${file#.}
|
|
|
|
[ -z "${rpath##/etc/*}" ] && [ -f "$sys_dir${rpath%/*}/${file##*/}" ] &&
|
|
return
|
|
|
|
"$cac_dir/mv" "$file" "$sys_dir${rpath%/*}"
|
|
done
|
|
|
|
"$sys_db/$name/post-install" 2>/dev/null
|
|
|
|
log "Installed ${pkg%.tar.gz}"
|
|
}
|
|
|
|
pkg_remove() {
|
|
pkg_list "${1:-${name-null}}" || return 1
|
|
|
|
# Create a backup of 'rm' and 'rmdir' so they aren't
|
|
# removed during package removal.
|
|
cp "$(command -v rm)" "$cac_dir"
|
|
cp "$(command -v rmdir)" "$cac_dir"
|
|
|
|
while read -r file; do
|
|
[ "${file##/etc/*}" ] || continue
|
|
|
|
if [ -d "$sys_dir$file" ]; then
|
|
"$cac_dir/rmdir" "$sys_dir$file" 2>/dev/null || continue
|
|
else
|
|
"$cac_dir/rm" -f -- "$sys_dir$file" || log "Failed to remove $file."
|
|
fi
|
|
done < "$sys_db/${1:-$name}/manifest"
|
|
|
|
# Use the backup of 'rm' to remove 'rmdir' and itself.
|
|
"$cac_dir/rm" "$cac_dir/rmdir" "$cac_dir/rm"
|
|
|
|
log "Removed ${1:-$name}."
|
|
}
|
|
|
|
pkg_updates() {
|
|
for item in "$sys_db/"*; do
|
|
pkg_search "${item##*/}"
|
|
|
|
read -r db_ver db_rel < "$item/version"
|
|
read -r re_ver re_rel < "$rep_dir/${item##*/}/version"
|
|
|
|
[ "$db_ver-$db_rel" != "$re_ver-$re_rel" ] &&
|
|
printf '%s\n' "${item##*/} $re_ver-$re_rel"
|
|
done
|
|
}
|
|
|
|
pkg_list() {
|
|
[ "$1" ] && { [ -d "$sys_db/$1" ]; return "$?"; }
|
|
|
|
for item in "$sys_db/"*; do
|
|
read -r version release 2>/dev/null < "$item/version" &&
|
|
printf '%s\n' "${item##*/} $version-$release"
|
|
done
|
|
}
|
|
|
|
args() {
|
|
[ -w "$sys_dir/" ] || case $1 in
|
|
i*|r*) die "No write permissions to \$KISS_ROOT."
|
|
esac
|
|
|
|
case $1 in b*|c*|i*) pkg_setup "${2-null}"; esac
|
|
case $1 in
|
|
b*) [ -f checksums ] ||
|
|
die "Checksums missing, run '$kiss checksum $name'"
|
|
|
|
pkg_depends
|
|
|
|
[ -n "$missing" ] && die "Missing dependencies:${missing%,}"
|
|
|
|
pkg_sources
|
|
pkg_verify
|
|
pkg_extract
|
|
pkg_build
|
|
|
|
[ -f nostrip ] || pkg_strip
|
|
|
|
pkg_manifest
|
|
pkg_tar ;;
|
|
|
|
c*) pkg_sources
|
|
pkg_checksum
|
|
log "Generated checksums." ;;
|
|
|
|
i*) pkg_depends install
|
|
pkg_install ;;
|
|
|
|
l*) pkg_list "$2" ;;
|
|
r*) pkg_remove "${2-null}" || die "Package '${2-null}' not installed." ;;
|
|
u*) pkg_updates ;;
|
|
v*) log "$kiss 0.1.10" ;;
|
|
|
|
*) log "$kiss [b|c|i|l|r|u] [pkg]" \
|
|
"build: Build a package." \
|
|
"checksum: Generate checksums." \
|
|
"install: Install a package (Runs build if needed)." \
|
|
"list: List packages." \
|
|
"remove: Remove a package." \
|
|
"update: Check for updates."
|
|
esac
|
|
}
|
|
|
|
main() {
|
|
trap pkg_clean EXIT INT
|
|
kiss=${0##*/}
|
|
sys_db=${sys_dir:=$KISS_ROOT}/var/db/$kiss
|
|
|
|
[ -z "$KISS_PATH" ] && die "Set \$KISS_PATH to a repository location."
|
|
|
|
mkdir -p "${cac_dir:=${XDG_CACHE_HOME:=$HOME/.cache}/$kiss}" \
|
|
"${mak_dir:=$cac_dir/build-$$}" \
|
|
"${bin_dir:=$cac_dir/bin}" \
|
|
"${tar_dir:=$cac_dir/extract-$$}" \
|
|
"${pkg_db:=${pkg_dir:=$cac_dir/pkg-$$}/var/db/$kiss}" ||
|
|
die "Couldn't create directories."
|
|
|
|
args "$@"
|
|
}
|
|
|
|
main "$@"
|