Compare commits
20
Commits
5d42d56441
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96e0ccfbdd | ||
|
|
4b895a0591 | ||
|
|
94379cb720 | ||
|
|
7587d72578 | ||
|
|
cfe99f928a | ||
|
|
2fc05ba7ea | ||
|
|
30fed4b734 | ||
|
|
caf66028e6 | ||
|
|
d5188a3385 | ||
|
|
618ad9222a | ||
|
|
4ff79cfdd6 | ||
|
|
1386633baf | ||
|
|
24c9bc3758 | ||
|
|
d0600c4b07 | ||
|
|
db903ca802 | ||
|
|
57ea39b406 | ||
|
|
f2cab01e36 | ||
|
|
a11405d93f | ||
|
|
c682ef1049 | ||
|
|
d44c761978 |
@@ -65,6 +65,9 @@
|
||||
- license 数组, 软件许可证
|
||||
- sources 数组, 构建过程使用到的所有源码包和补丁,可以是 `https://...` 也可以是文件相对路径
|
||||
- urls 数组, `sources` 各项对应的地址, 当前仅限使用 `wget` 获取或复制文件
|
||||
- 以下三个数组有一个即可
|
||||
- sha512sums 数组, `sources` 各项对应的 sha512 checksum
|
||||
- sha256sums 数组, `sources` 各项对应的 sha256 checksum
|
||||
- md5sums 数组, `sources` 各项对应的 md5 checksum
|
||||
- options 数组, 构建选项, 详见第4小节
|
||||
- srcdir 字符串, 构建路径(在leaf主程序中定义)
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
shopt -s inherit_errexit 2>/dev/null || true
|
||||
|
||||
RED_COLOR="\e[1;31m"
|
||||
PURPLE_COLOR="\e[1;35m"
|
||||
GREEN_COLOR="\e[1;32m"
|
||||
CLEAR_COLOR="\e[0m"
|
||||
RED_COLOR=$"\e[1;31m"
|
||||
PURPLE_COLOR=$"\e[1;35m"
|
||||
GREEN_COLOR=$'\e[1;32m'
|
||||
CLEAR_COLOR=$'\e[0m'
|
||||
|
||||
_DEF_PARALLEL_JOBS=4
|
||||
_ENV_PARALLEL_JOBS="${PARALLEL_JOBS}"
|
||||
source /etc/leaf.conf
|
||||
export PATH="/usr/bin:/usr/sbin"
|
||||
if [ -n "${_ENV_PARALLEL_JOBS}" ]; then
|
||||
export PARALLEL_JOBS="${_ENV_PARALLEL_JOBS}"
|
||||
elif [ -z "${PARALLEL_JOBS}" ]; then
|
||||
@@ -18,8 +20,11 @@ else
|
||||
fi
|
||||
export MAKEFLAGS="-j${PARALLEL_JOBS}"
|
||||
export NINJAJOBS="${PARALLEL_JOBS}"
|
||||
export LC_ALL=C
|
||||
export TESTSUITEFLAGS="-j${PARALLEL_JOBS}"
|
||||
export LANG=C
|
||||
export LC_CTYPE=C.UTF-8
|
||||
export leaf_flags="CFLAGS CXXFLAGS FCFLAGS FFLAGS RUSTFLAGS"
|
||||
export LEAF_MESSAGE_DIR="${TEMP_DIR:-/tmp}/leaf-messages"
|
||||
for flag in ${leaf_flags}; do
|
||||
export $flag
|
||||
done
|
||||
@@ -27,7 +32,15 @@ done
|
||||
declare -A BUILD_OPTIONS
|
||||
BUILD_OPTIONS=([strip]="0" [libtool]="0" [zipman]="0")
|
||||
|
||||
# global message file list
|
||||
declare -a LEAF_MESSAGE_FILES=()
|
||||
|
||||
leaf_set_title() {
|
||||
printf '\e]2;%s\a' "$*"
|
||||
}
|
||||
|
||||
main() {
|
||||
leaf_set_title "leaf"
|
||||
leaf_check_directories
|
||||
if [ $# -le 1 ]; then
|
||||
case $1 in
|
||||
@@ -54,6 +67,7 @@ main() {
|
||||
leaf_find_pkgbuild "${_item}"
|
||||
fi
|
||||
leaf_reset_state
|
||||
leaf_message_init
|
||||
if [[ x"$1" == x"remove" ]]; then
|
||||
source "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}/PKGBUILD" || leaf_record_message "cannot find PKGBUILD, ingore."
|
||||
else
|
||||
@@ -61,13 +75,24 @@ main() {
|
||||
fi
|
||||
leaf_parse_options
|
||||
srcdir="${BUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}"
|
||||
LEAFTMPDIR="/tmp/leaf-tmp/${PKG_NAME}"
|
||||
LEAFHOME="/tmp/leaf-tmp/${PKG_NAME}"
|
||||
export TMPDIR="${LEAFTMPDIR}"
|
||||
export HOME=${LEAFHOME}
|
||||
pkgdir="${srcdir}"/__pkgdir__
|
||||
if [[ x"$1" != x"dirct-install" ]]; then
|
||||
rm -rf "${srcdir}" && install -dm755 "${srcdir}" && cd "${srcdir}"
|
||||
fi
|
||||
leaf_${1}_package
|
||||
#rm -rf "${LEAFTMPDIR}"
|
||||
#rm -rf "${LEAFHOME}"
|
||||
done
|
||||
;;
|
||||
upgrade)
|
||||
leaf_check_permission
|
||||
[ $# -eq 3 ] || leaf_error "Usage: leaf upgrade <oldpkg> <newpkg>"
|
||||
leaf_upgrade_package "$2" "$3"
|
||||
;;
|
||||
search)
|
||||
leaf_search_package "${@:2}"
|
||||
;;
|
||||
@@ -84,13 +109,16 @@ main() {
|
||||
leaf_unpack_package "${_item}"
|
||||
done
|
||||
;;
|
||||
query)
|
||||
shift
|
||||
[[ $# -eq 1 ]] || leaf_error "usage: leaf query <atom-or-name>"
|
||||
leaf_query "$1"
|
||||
;;
|
||||
*)
|
||||
leaf_print_usage
|
||||
;;
|
||||
esac
|
||||
if [ -n "${MESSAGE}" ]; then
|
||||
echo -e "${MESSAGE}"
|
||||
fi
|
||||
leaf_print_message
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -98,6 +126,10 @@ main() {
|
||||
# Help functions that give PKGBUILD to use
|
||||
###########################################################
|
||||
|
||||
leaf_info() {
|
||||
echo -e "${GREEN_COLOR} * $1${CLEAR_COLOR}"
|
||||
}
|
||||
|
||||
leaf_clear_flags() {
|
||||
for flag in $leaf_flags; do
|
||||
unset $flag
|
||||
@@ -167,9 +199,7 @@ leaf_compress_man_pages() {
|
||||
}
|
||||
|
||||
leaf_error() {
|
||||
if [ -n "${MESSAGE}" ]; then
|
||||
echo -e "${MESSAGE}"
|
||||
fi
|
||||
leaf_print_message
|
||||
echo -e "${RED_COLOR}* $1${CLEAR_COLOR}" && exit 1
|
||||
}
|
||||
|
||||
@@ -228,6 +258,7 @@ leaf_invoke_hooks() {
|
||||
|
||||
leaf_merge_package() {
|
||||
pushd $1 > /dev/null 2>&1
|
||||
leaf_set_title "leaf: ${PKG_PREFIX}/${PKG_NAME} (Merging)"
|
||||
local _trace_dir="${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}"
|
||||
local _item _item_conflict _mode _owner _group
|
||||
local _time="$(date +%Y%m%d%H%M%S)"
|
||||
@@ -237,27 +268,46 @@ leaf_merge_package() {
|
||||
_group=$(stat -c %g ."${_item}")
|
||||
install -d -m ${_mode} -o ${_owner} -g ${_group} "${_item}"
|
||||
done
|
||||
cat "${_trace_dir}"/FILES | while read -r _item; do
|
||||
|
||||
cat "${_trace_dir}"/FILES | while read -r _item; do
|
||||
_mode=$(stat -c %a ."${_item}")
|
||||
_owner=$(stat -c %u ."${_item}")
|
||||
_group=$(stat -c %g ."${_item}")
|
||||
if [[ "${_item}" == /etc/* && -e "${_item}" ]]; then
|
||||
_item_conflict="$(dirname "${_item}")/._$(basename "${_item}").conflict_${PKG_NAME}_${_time}"
|
||||
if (diff ."${_item}" "${_item}" > /dev/null); then
|
||||
install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item}"
|
||||
else
|
||||
install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item_conflict}"
|
||||
leaf_record_message "Config file confliction on ${_item}, the package provided version is installed as ${_item_conflict}."
|
||||
fi
|
||||
else
|
||||
|
||||
# Fast path: non-/etc, or /etc path that doesn't exist yet -> install/overwrite directly
|
||||
if [[ "${_item}" != /etc/* || ! -e "${_item}" ]]; then
|
||||
install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item}"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Now: /etc/* and already exists on the system.
|
||||
# Upgrade optimization: if OLD_TRACE_DIR is set and the current /etc file matches old baseline,
|
||||
# then user didn't modify it -> overwrite with new version directly.
|
||||
if [[ -n "${OLD_TRACE_DIR:-}" ]] \
|
||||
&& leaf_conffile_is_unmodified "${_item}" "${OLD_TRACE_DIR}"; then
|
||||
install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item}"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Otherwise, fall back to conflict logic:
|
||||
# - if content is identical -> overwrite (keeps permissions/ownership from pkg)
|
||||
# - else -> write conflict file and keep user's current file
|
||||
_item_conflict="$(dirname "${_item}")/._$(basename "${_item}").conflict_${PKG_NAME}_${_time}"
|
||||
if diff -q ."${_item}" "${_item}" >/dev/null 2>&1; then
|
||||
install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item}"
|
||||
else
|
||||
install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item_conflict}"
|
||||
leaf_record_message "Config file confliction on ${_item}, the package provided version is installed as ${_item_conflict}."
|
||||
fi
|
||||
done
|
||||
|
||||
cat "${_trace_dir}"/LINKS | while read -r _item; do
|
||||
cp -dp ."${_item}" "${_item}"
|
||||
done
|
||||
leaf_invoke_hooks install
|
||||
popd > /dev/null 2>&1
|
||||
rm -rf "${LEAFHOME}"
|
||||
rm -rf "${LEAFTMPDIR}"
|
||||
}
|
||||
|
||||
leaf_parse_options() {
|
||||
@@ -290,9 +340,10 @@ Usage: leaf [option] [packages]
|
||||
Option: prepare prepare packages but do not build
|
||||
build build packages but do not install
|
||||
install install packages
|
||||
forece-install install packages even if test faild
|
||||
forece-install install packages even if test failed
|
||||
dirct-install install packages directly from previous build
|
||||
remove remove packages
|
||||
upgrade upgrade oldpkg to newpkg (leaf upgrade old new)
|
||||
clean clean all source folders
|
||||
list list installed packages
|
||||
search search packages by keyword
|
||||
@@ -303,41 +354,90 @@ Option: prepare prepare packages but do not build
|
||||
EOF
|
||||
}
|
||||
|
||||
leaf_message_init() {
|
||||
# one log per package invocation
|
||||
local d="${LEAF_MESSAGE_DIR}/${PKG_PREFIX}/${PKG_NAME}"
|
||||
install -dm755 -- "$d"
|
||||
LEAF_MESSAGE_FILE="${d}/messages.$$.$(date +%Y%m%d%H%M%S).log"
|
||||
: > "${LEAF_MESSAGE_FILE}"
|
||||
export LEAF_MESSAGE_FILE
|
||||
|
||||
LEAF_MESSAGE_FILES+=("${LEAF_MESSAGE_FILE}")
|
||||
}
|
||||
|
||||
leaf_record_message() {
|
||||
MESSAGE+="\n${PURPLE_COLOR}* Message from ${PKG_PREFIX}/${PKG_NAME}${CLEAR_COLOR}\n"
|
||||
local out
|
||||
local _item
|
||||
for _item in "${@:1}"; do
|
||||
MESSAGE+="${GREEN_COLOR}* ${_item}${CLEAR_COLOR}\n"
|
||||
out+="${GREEN_COLOR}* ${_item}${CLEAR_COLOR}\n"
|
||||
done
|
||||
|
||||
if [ -n "${LEAF_MESSAGE_FILE}" ]; then
|
||||
# printf %b will explain \n 和 \e
|
||||
printf '%b' "${out}" >> "${LEAF_MESSAGE_FILE}"
|
||||
fi
|
||||
}
|
||||
|
||||
leaf_print_message() {
|
||||
local f prefix name
|
||||
|
||||
for f in "${LEAF_MESSAGE_FILES[@]}"; do
|
||||
[ -n "$f" ] || continue
|
||||
[ -s "$f" ] || continue
|
||||
|
||||
# ${TEMP_DIR}/leaf-messages/<prefix>/<name>/messages....
|
||||
prefix="${f#${TEMP_DIR}/leaf-messages/}"
|
||||
prefix="${prefix%%/*}"
|
||||
name="${f#${TEMP_DIR}/leaf-messages/${prefix}/}"
|
||||
name="${name%%/*}"
|
||||
|
||||
echo -e "\n${PURPLE_COLOR}* Message from ${prefix}/${name}${CLEAR_COLOR}\n"
|
||||
cat -- "$f"
|
||||
done
|
||||
|
||||
rm -rf -- "${LEAF_MESSAGE_DIR}"/*
|
||||
|
||||
LEAF_MESSAGE_FILES=()
|
||||
unset LEAF_MESSAGE_FILE
|
||||
}
|
||||
|
||||
|
||||
leaf_remove_libtool_files() {
|
||||
find "${pkgdir}" -name "*.la" -delete
|
||||
}
|
||||
|
||||
leaf_noop() { :; }
|
||||
|
||||
leaf_reset_state() {
|
||||
options=()
|
||||
src_preinstall() {
|
||||
:
|
||||
}
|
||||
src_postinstall() {
|
||||
:
|
||||
}
|
||||
src_preremove() {
|
||||
:
|
||||
}
|
||||
src_postremove() {
|
||||
:
|
||||
}
|
||||
src_prepare() {
|
||||
:
|
||||
}
|
||||
src_build() {
|
||||
:
|
||||
}
|
||||
src_check() {
|
||||
:
|
||||
}
|
||||
src_preinstall() { leaf_noop; }
|
||||
src_postinstall() { leaf_noop; }
|
||||
src_preremove() { leaf_noop; }
|
||||
src_postremove() { leaf_noop; }
|
||||
|
||||
src_prepare() { leaf_noop; }
|
||||
src_build() { leaf_noop; }
|
||||
src_check() { leaf_noop; }
|
||||
src_install() { leaf_noop; }
|
||||
}
|
||||
|
||||
leaf_is_noop_func() {
|
||||
# usage: leaf_is_noop_func <funcname>
|
||||
local fn="$1" body
|
||||
|
||||
declare -F "$fn" >/dev/null || return 0
|
||||
|
||||
body="$(declare -f "$fn" \
|
||||
| sed -n '0,/{/d; :a; /}/q; p; n; ba')"
|
||||
|
||||
body="$(printf '%s' "$body" | tr -d '[:space:]' | tr -d ';')"
|
||||
|
||||
# allowd no-op form: leaf_noop, :, true
|
||||
case "$body" in
|
||||
leaf_noop|:|true) return 0 ;;
|
||||
esac
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
leaf_strip_files() {
|
||||
@@ -383,35 +483,187 @@ leaf_trace_package() {
|
||||
find . -type l | sort > "${_trace_dir}"/LINKS
|
||||
sed -i "s/.//" "${_trace_dir}"/{DIRS,FILES,LINKS}
|
||||
install -vm644 "${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}.PKGBUILD" "${_trace_dir}/PKGBUILD"
|
||||
leaf_trace_conffiles "${_trace_dir}"
|
||||
popd > /dev/null 2>&1
|
||||
}
|
||||
|
||||
leaf_update_package_database() {
|
||||
local _item="$(echo "$2" | awk 'BEGIN{FS="/";OFS="\\/"}{print $1,$2}')"
|
||||
case $1 in
|
||||
add)
|
||||
sed -i "/${_item}/d" "${INSTALLED_PACKAGES}"
|
||||
echo "$2" >> ${INSTALLED_PACKAGES}
|
||||
leaf_trace_conffiles() {
|
||||
# usage: leaf_trace_conffiles <trace_dir>
|
||||
# called with CWD = pkgdir (same as leaf_trace_package)
|
||||
local _trace_dir="$1"
|
||||
local _out="${_trace_dir}/CONFFILES"
|
||||
local _p _type _val _mode _uid _gid
|
||||
|
||||
[ -d ./etc ] || return 0
|
||||
|
||||
: > "${_out}"
|
||||
|
||||
# regular files
|
||||
while IFS= read -r -d '' _p; do
|
||||
_mode=$(stat -c %a -- "$_p")
|
||||
_uid=$(stat -c %u -- "$_p")
|
||||
_gid=$(stat -c %g -- "$_p")
|
||||
_val=$(sha256sum -- "$_p" | awk '{print $1}')
|
||||
printf '%s\tf\t%s\t%s\t%s\t%s\n' "/${_p#./}" "${_val}" "${_mode}" "${_uid}" "${_gid}" >> "${_out}"
|
||||
done < <(find ./etc -type f -print0)
|
||||
|
||||
# symlinks
|
||||
while IFS= read -r -d '' _p; do
|
||||
_mode=$(stat -c %a -- "$_p")
|
||||
_uid=$(stat -c %u -- "$_p")
|
||||
_gid=$(stat -c %g -- "$_p")
|
||||
_val=$(readlink -- "$_p")
|
||||
printf '%s\tl\t%s\t%s\t%s\t%s\n' "/${_p#./}" "${_val}" "${_mode}" "${_uid}" "${_gid}" >> "${_out}"
|
||||
done < <(find ./etc -type l -print0)
|
||||
|
||||
sort -o "${_out}" "${_out}" 2>/dev/null || true
|
||||
}
|
||||
|
||||
leaf_conffile_is_unmodified() {
|
||||
# usage: leaf_conffile_is_unmodified <abs_path> <trace_dir>
|
||||
# return 0 if unmodified (safe to remove), 1 otherwise
|
||||
local path="$1" tdir="$2"
|
||||
local cf="${tdir}/CONFFILES"
|
||||
local line type val mode uid gid
|
||||
local cur_val cur_mode cur_uid cur_gid
|
||||
|
||||
[ -r "${cf}" ] || return 1
|
||||
[ -e "${path}" ] || return 1
|
||||
|
||||
line="$(grep -F -m1 -- "${path}"$'\t' "${cf}" 2>/dev/null)" || return 1
|
||||
|
||||
IFS=$'\t' read -r _p type val mode uid gid <<< "${line}"
|
||||
|
||||
cur_mode="$(stat -c %a -- "${path}" 2>/dev/null)" || return 1
|
||||
cur_uid="$(stat -c %u -- "${path}" 2>/dev/null)" || return 1
|
||||
cur_gid="$(stat -c %g -- "${path}" 2>/dev/null)" || return 1
|
||||
|
||||
# metadata changed -> treat as modified
|
||||
if [ "${cur_mode}" != "${mode}" ] || [ "${cur_uid}" != "${uid}" ] || [ "${cur_gid}" != "${gid}" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
case "${type}" in
|
||||
f)
|
||||
cur_val="$(sha256sum -- "${path}" 2>/dev/null | awk '{print $1}')" || return 1
|
||||
[ "${cur_val}" = "${val}" ] && return 0 || return 1
|
||||
;;
|
||||
delete)
|
||||
sed -i "/${_item}/d" "${INSTALLED_PACKAGES}"
|
||||
l)
|
||||
cur_val="$(readlink -- "${path}" 2>/dev/null)" || return 1
|
||||
[ "${cur_val}" = "${val}" ] && return 0 || return 1
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
leaf_update_package_database() {
|
||||
local action=$1 atom=$2 db="${INSTALLED_PACKAGES}"
|
||||
local tmp="${db}.tmp.$$"
|
||||
|
||||
case "$action" in
|
||||
add)
|
||||
{ [ -f "$db" ] && grep -Fvx -- "$atom" "$db" || :; printf '%s\n' "$atom"; } >"$tmp" &&
|
||||
mv -f -- "$tmp" "$db"
|
||||
;;
|
||||
delete)
|
||||
grep -Fvx -- "$atom" "$db" >"$tmp" &&
|
||||
mv -f -- "$tmp" "$db"
|
||||
;;
|
||||
*)
|
||||
leaf_error "unknown operation leaf_update_package_database ${action}."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
leaf_update_environment() {
|
||||
ldconfig
|
||||
[ ! -r /etc/profile ] || source /etc/profile
|
||||
}
|
||||
|
||||
leaf_run_phase() {
|
||||
# usage: leaf_run_phase <phase_name> <func_name>
|
||||
local phase="$1" fn="$2"
|
||||
|
||||
LEAF_PHASE_RC=0
|
||||
|
||||
declare -F "$fn" >/dev/null || {
|
||||
LEAF_PHASE_RC=127
|
||||
return 0
|
||||
}
|
||||
|
||||
if leaf_is_noop_func "$fn"; then
|
||||
LEAF_PHASE_RC=0
|
||||
return 0
|
||||
fi
|
||||
|
||||
leaf_set_title "leaf: ${PKG_PREFIX}/${PKG_NAME} (${phase})"
|
||||
echo -e "${GREEN_COLOR}>>> ${PKG_PREFIX}/${PKG_NAME}: ${phase}${CLEAR_COLOR}"
|
||||
|
||||
set +e
|
||||
(
|
||||
set -eE -o pipefail
|
||||
"$fn"
|
||||
)
|
||||
LEAF_PHASE_RC=$?
|
||||
set -e
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
leaf_phase_must() {
|
||||
local phase="$1" fn="$2" msg="$3"
|
||||
leaf_run_phase "$phase" "$fn"
|
||||
if [ "${LEAF_PHASE_RC}" -ne 0 ]; then
|
||||
leaf_error "${msg:-${fn} failed (rc=${LEAF_PHASE_RC})}"
|
||||
fi
|
||||
}
|
||||
|
||||
leaf_phase_try() {
|
||||
# just run, caller checks $LEAF_PHASE_RC
|
||||
leaf_run_phase "$1" "$2"
|
||||
}
|
||||
|
||||
|
||||
leaf_prepare_package() {
|
||||
local url sourcefile index _md5sum local_src pkg_src_root
|
||||
local url sourcefile index _sum local_src
|
||||
local checksum_algo checksum_arr_name
|
||||
local decl
|
||||
|
||||
leaf_set_title "leaf: ${PKG_PREFIX}/${PKG_NAME} (Fetching)"
|
||||
|
||||
distdir="${DIST_DIR}"
|
||||
filedir="${PKGBUILD_DIR}/${PKG_PREFIX}/${pkgname}"
|
||||
mkdir -pv -- "${distdir}"
|
||||
|
||||
pkg_src_root="${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}"
|
||||
# choose checksum type (sha512 -> sha256 -> md5)
|
||||
if declare -p sha512sums &>/dev/null; then
|
||||
decl="$(declare -p sha512sums 2>/dev/null)"
|
||||
[[ "${decl}" == "declare -a "* || "${decl}" == "declare -a"* ]] \
|
||||
|| leaf_error "sha512sums is defined but is not an array"
|
||||
checksum_algo="sha512"
|
||||
checksum_arr_name="sha512sums"
|
||||
elif declare -p sha256sums &>/dev/null; then
|
||||
decl="$(declare -p sha256sums 2>/dev/null)"
|
||||
[[ "${decl}" == "declare -a "* || "${decl}" == "declare -a"* ]] \
|
||||
|| leaf_error "sha256sums is defined but is not an array"
|
||||
checksum_algo="sha256"
|
||||
checksum_arr_name="sha256sums"
|
||||
elif declare -p md5sums &>/dev/null; then
|
||||
decl="$(declare -p md5sums 2>/dev/null)"
|
||||
[[ "${decl}" == "declare -a "* || "${decl}" == "declare -a"* ]] \
|
||||
|| leaf_error "md5sums is defined but is not an array"
|
||||
checksum_algo="md5"
|
||||
checksum_arr_name="md5sums"
|
||||
else
|
||||
leaf_error "no checksum list defined: need one of sha512sums/sha256sums/md5sums"
|
||||
fi
|
||||
|
||||
local -n checksums_ref="${checksum_arr_name}"
|
||||
|
||||
# enter ${distdir}
|
||||
pushd "${distdir}" >/dev/null || leaf_error "cannot enter distdir: ${distdir}"
|
||||
|
||||
for index in "${!sources[@]}"; do
|
||||
@@ -424,12 +676,17 @@ leaf_prepare_package() {
|
||||
|
||||
if [ -f "${sourcefile}" ]; then
|
||||
echo "${sourcefile} exists, checking..."
|
||||
_md5sum="$(md5sum -- "${sourcefile}" | awk '{print $1}')"
|
||||
if [ "${_md5sum}" = "${md5sums[$index]}" ]; then
|
||||
echo "md5sum match, skip ${sourcefile}."
|
||||
case "${checksum_algo}" in
|
||||
sha512) _sum="$(sha512sum -- "${sourcefile}" | awk '{print $1}')" ;;
|
||||
sha256) _sum="$(sha256sum -- "${sourcefile}" | awk '{print $1}')" ;;
|
||||
md5) _sum="$(md5sum -- "${sourcefile}" | awk '{print $1}')" ;;
|
||||
*) leaf_error "internal error: unknown checksum algo: ${checksum_algo}" ;;
|
||||
esac
|
||||
if [ "${_sum}" = "${checksums_ref[$index]}" ]; then
|
||||
echo "${checksum_algo}sum match, skip ${sourcefile}."
|
||||
continue
|
||||
fi
|
||||
echo "md5sum does not match, refetching..."
|
||||
echo "${checksum_algo}sum does not match, refetching..."
|
||||
rm -f -- "${sourcefile}"
|
||||
fi
|
||||
|
||||
@@ -439,37 +696,66 @@ leaf_prepare_package() {
|
||||
wget -O "${sourcefile}" -- "${url}" || leaf_error "wget failed: ${url}"
|
||||
;;
|
||||
*)
|
||||
local_src="${pkg_src_root}/${url}"
|
||||
local_src="${filedir}/${url}"
|
||||
echo "Copying ${sourcefile} from local file ${local_src}..."
|
||||
[ -f "${local_src}" ] || leaf_error "local source not found: ${local_src}"
|
||||
cp -f -- "${local_src}" "${sourcefile}" || leaf_error "cp failed: ${local_src}"
|
||||
;;
|
||||
esac
|
||||
|
||||
_md5sum="$(md5sum -- "${sourcefile}" | awk '{print $1}')"
|
||||
if [ "${_md5sum}" != "${md5sums[$index]}" ]; then
|
||||
leaf_error "md5sums of ${sourcefile}(${_md5sum}) does not match with ${md5sums[$index]}!"
|
||||
case "${checksum_algo}" in
|
||||
sha512) _sum="$(sha512sum -- "${sourcefile}" | awk '{print $1}')" ;;
|
||||
sha256) _sum="$(sha256sum -- "${sourcefile}" | awk '{print $1}')" ;;
|
||||
md5) _sum="$(md5sum -- "${sourcefile}" | awk '{print $1}')" ;;
|
||||
*) leaf_error "internal error: unknown checksum algo: ${checksum_algo}" ;;
|
||||
esac
|
||||
if [ "${_sum}" != "${checksums_ref[$index]}" ]; then
|
||||
leaf_error "${checksum_algo}sum of ${sourcefile}(${_sum}) does not match with ${checksums_ref[$index]}!"
|
||||
fi
|
||||
done
|
||||
|
||||
popd >/dev/null || true
|
||||
echo "Sources ready."
|
||||
src_prepare
|
||||
leaf_phase_must "Preparing" src_prepare "Prepare failed."
|
||||
}
|
||||
|
||||
leaf_build_package() {
|
||||
leaf_prepare_package
|
||||
src_build
|
||||
rm -rf -- ${TMPDIR} ${HOME}
|
||||
mkdir -pv ${TMPDIR}
|
||||
mkdir -pv ${HOME}
|
||||
|
||||
if ! src_check; then
|
||||
if [ "${FORCE_INSTALL}" == "1" ]; then
|
||||
leaf_prepare_package
|
||||
leaf_phase_must "Building" src_build "Build failed."
|
||||
|
||||
leaf_phase_try "Checking" src_check
|
||||
if [ "${LEAF_PHASE_RC}" -ne 0 ]; then
|
||||
if [ "${FORCE_INSTALL:-0}" = "1" ]; then
|
||||
echo -e "${RED_COLOR}* Tests failed, but is in force-install mode.${CLEAR_COLOR}"
|
||||
else
|
||||
leaf_error "Tests failed, please check. Aborting installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
src_install
|
||||
## run tests (strict: any failing command inside src_check fails the whole check)
|
||||
#local _check_rc=0
|
||||
#set +e
|
||||
#( set -e; src_check )
|
||||
#_check_rc=$?
|
||||
#set -e
|
||||
#
|
||||
#if [ "${_check_rc}" -ne 0 ]; then
|
||||
# if [ "${FORCE_INSTALL}" = "1" ]; then
|
||||
# echo -e "${RED_COLOR}* Tests failed, but is in force-install mode.${CLEAR_COLOR}"
|
||||
# else
|
||||
# leaf_error "Tests failed, please check. Aborting installation."
|
||||
# fi
|
||||
#fi
|
||||
|
||||
# ensure pkgdir exists
|
||||
rm -rf -- "${pkgdir}"
|
||||
install -dm755 -- "${pkgdir}"
|
||||
|
||||
leaf_phase_must "Installing" src_install "Installing failed."
|
||||
local _option
|
||||
for _option in $(echo ${!BUILD_OPTIONS[*]}); do
|
||||
if [[ x"${_option}" == x"strip" ]] && [[ x"${BUILD_OPTIONS[$_option]}" == x"1" ]]; then
|
||||
@@ -493,9 +779,9 @@ leaf_install_package() {
|
||||
# trace need to to in the same fakeroot
|
||||
leaf_trace_package "${pkgdir}"
|
||||
# ROOT starts
|
||||
src_preinstall
|
||||
leaf_phase_must "Preinstall" src_preinstall "Preinstall failed."
|
||||
leaf_merge_package "${pkgdir}"
|
||||
src_postinstall
|
||||
leaf_phase_must "Postinstall" src_postinstall "Postinstall failed."
|
||||
leaf_update_package_database add "${PKG_PREFIX}/${PKG_NAME}"
|
||||
leaf_update_environment
|
||||
# ROOT ends
|
||||
@@ -508,9 +794,9 @@ leaf_dirct-install_package() {
|
||||
# trace need to to in the same fakeroot
|
||||
leaf_trace_package "${pkgdir}"
|
||||
# ROOT starts
|
||||
src_preinstall
|
||||
leaf_phase_must "Preinstall" src_preinstall "Preinstall failed."
|
||||
leaf_merge_package "${pkgdir}"
|
||||
src_postinstall
|
||||
leaf_phase_must "Postinstall" src_postinstall "Postinstall failed."
|
||||
leaf_update_package_database add "${PKG_PREFIX}/${PKG_NAME}"
|
||||
leaf_update_environment
|
||||
# ROOT ends
|
||||
@@ -522,39 +808,207 @@ leaf_force-install_package(){
|
||||
leaf_install_package
|
||||
}
|
||||
|
||||
leaf_upgrade_package() {
|
||||
local old_query="$1" new_query="$2"
|
||||
local old_prefix old_name new_prefix new_name
|
||||
local new_srcdir new_pkgdir
|
||||
local old_trace_dir new_trace_dir
|
||||
local skipfile
|
||||
|
||||
# -------- resolve OLD (installed) --------
|
||||
leaf_find_remove_pkgbuild "${old_query}"
|
||||
old_prefix="${PKG_PREFIX}"
|
||||
old_name="${PKG_NAME}"
|
||||
old_trace_dir="${TRACE_DIR}/${old_prefix}/${old_name}"
|
||||
OLD_TRACE_DIR="${old_trace_dir}"
|
||||
|
||||
# -------- resolve NEW (available) --------
|
||||
leaf_find_pkgbuild "${new_query}"
|
||||
new_prefix="${PKG_PREFIX}"
|
||||
new_name="${PKG_NAME}"
|
||||
|
||||
# sanity
|
||||
if [[ "${old_prefix}/${old_name}" == "${new_prefix}/${new_name}" ]]; then
|
||||
leaf_error "upgrade: old and new refer to the same package: ${old_prefix}/${old_name}"
|
||||
fi
|
||||
|
||||
# =========================================================
|
||||
# 1) BUILD NEW (no ROOT touch)
|
||||
# =========================================================
|
||||
PKG_PREFIX="${new_prefix}"
|
||||
PKG_NAME="${new_name}"
|
||||
|
||||
leaf_reset_state
|
||||
leaf_message_init
|
||||
source "${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}.PKGBUILD"
|
||||
leaf_parse_options
|
||||
|
||||
srcdir="${BUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}"
|
||||
LEAFTMPDIR="/tmp/leaf-tmp/${PKG_NAME}"
|
||||
LEAFHOME="${LEAFTMPDIR}"
|
||||
export TMPDIR="${LEAFTMPDIR}"
|
||||
export HOME="${LEAFHOME}"
|
||||
pkgdir="${srcdir}/__pkgdir__"
|
||||
|
||||
rm -rf -- "${srcdir}" && install -dm755 -- "${srcdir}" && cd "${srcdir}"
|
||||
mkdir -pv -- "${LEAFTMPDIR}" "${LEAFHOME}"
|
||||
|
||||
leaf_build_package
|
||||
|
||||
new_srcdir="${srcdir}"
|
||||
new_pkgdir="${pkgdir}"
|
||||
|
||||
# =========================================================
|
||||
# 2) INSTALL NEW (touch ROOT)
|
||||
# Use dirct-install path so we don't rebuild.
|
||||
# =========================================================
|
||||
PKG_PREFIX="${new_prefix}"
|
||||
PKG_NAME="${new_name}"
|
||||
|
||||
leaf_reset_state
|
||||
leaf_message_init
|
||||
source "${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}.PKGBUILD"
|
||||
leaf_parse_options
|
||||
|
||||
srcdir="${new_srcdir}"
|
||||
pkgdir="${new_pkgdir}"
|
||||
LEAFTMPDIR="/tmp/leaf-tmp/${PKG_NAME}"
|
||||
LEAFHOME="${LEAFTMPDIR}"
|
||||
export TMPDIR="${LEAFTMPDIR}"
|
||||
export HOME="${LEAFHOME}"
|
||||
|
||||
[ -d "${pkgdir}" ] || leaf_error "upgrade: built pkgdir missing: ${pkgdir}"
|
||||
cd "${srcdir}" || leaf_error "upgrade: cannot enter build dir: ${srcdir}"
|
||||
|
||||
leaf_dirct-install_package
|
||||
|
||||
#rm -rf -- "${LEAFTMPDIR}" "${LEAFHOME}"
|
||||
|
||||
# new trace dir now exists (generated by leaf_dirct-install_package -> leaf_trace_package)
|
||||
new_trace_dir="${TRACE_DIR}/${new_prefix}/${new_name}"
|
||||
[ -d "${new_trace_dir}" ] || leaf_error "upgrade: new trace dir missing: ${new_trace_dir}"
|
||||
|
||||
rm -rf "${LEAFHOME}"
|
||||
rm -rf "${LEAFTMPDIR}"
|
||||
|
||||
# =========================================================
|
||||
# 3) REMOVE OLD, but SKIP paths owned by NEW
|
||||
# =========================================================
|
||||
skipfile="$(mktemp "${TEMP_DIR}/leaf-upgrade-skip.XXXXXX")" || leaf_error "mktemp failed"
|
||||
cat "${new_trace_dir}/FILES" "${new_trace_dir}/LINKS" "${new_trace_dir}/DIRS" > "${skipfile}" || {
|
||||
rm -f -- "${skipfile}"
|
||||
leaf_error "upgrade: cannot create skiplist"
|
||||
}
|
||||
|
||||
PKG_PREFIX="${old_prefix}"
|
||||
PKG_NAME="${old_name}"
|
||||
|
||||
leaf_reset_state
|
||||
leaf_message_init
|
||||
# old remove sources PKGBUILD from TRACE (best effort)
|
||||
source "${old_trace_dir}/PKGBUILD" 2>/dev/null || true
|
||||
|
||||
export LEAF_REMOVE_SKIPLIST="${skipfile}"
|
||||
leaf_remove_package
|
||||
unset LEAF_REMOVE_SKIPLIST
|
||||
|
||||
rm -f -- "${skipfile}"
|
||||
|
||||
echo -e "${GREEN_COLOR}* Upgraded ${old_prefix}/${old_name} -> ${new_prefix}/${new_name}${CLEAR_COLOR}\n"
|
||||
}
|
||||
|
||||
leaf_in_skiplist() {
|
||||
# usage: leaf_in_skiplist <abs_path>
|
||||
# return 0 if path in LEAF_REMOVE_SKIPLIST file
|
||||
local p="$1"
|
||||
[ -n "${LEAF_REMOVE_SKIPLIST:-}" ] || return 1
|
||||
[ -r "${LEAF_REMOVE_SKIPLIST}" ] || return 1
|
||||
grep -Fxq -- "$p" "${LEAF_REMOVE_SKIPLIST}" 2>/dev/null
|
||||
}
|
||||
|
||||
leaf_remove_package() {
|
||||
[ -n "$(grep "${PKG_PREFIX}/${PKG_NAME}" ${INSTALLED_PACKAGES})" ] || {
|
||||
leaf_error "Package ${PKG_PREFIX}/${PKG_NAME} is NOT installed"
|
||||
}
|
||||
src_preremove
|
||||
|
||||
leaf_phase_must "Preremove" src_preremove "Preremove failed."
|
||||
|
||||
local _file _link _directory _etc_backup_path _relative_path _backup_etc=false
|
||||
_etc_backup_path="/etc/.leaf_backup/${PKGNAME}_$(date +%Y%m%d%H%M%S)"
|
||||
cat "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}/FILES" | while read -r _file; do
|
||||
if [[ "${_file}" == /etc/* && -f "${_file}" ]]; then
|
||||
_backup_etc=true
|
||||
_relative_path="${_file#/etc/}"
|
||||
mkdir -pv "${_etc_backup_path}/$(dirname "${_relative_path}")"
|
||||
mv -v "${_file}" "${_etc_backup_path}/${_relative_path}"
|
||||
else
|
||||
rm -fv "${_file}"
|
||||
local _trace_dir="${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}"
|
||||
local _has_conffiles=0
|
||||
|
||||
[ -r "${_trace_dir}/CONFFILES" ] && _has_conffiles=1
|
||||
|
||||
_etc_backup_path="/etc/.leaf_backup/${PKG_NAME}_$(date +%Y%m%d%H%M%S)"
|
||||
|
||||
# FILES
|
||||
while read -r _file; do
|
||||
if leaf_in_skiplist "${_file}"; then
|
||||
continue
|
||||
fi
|
||||
done
|
||||
if [[ "${_file}" == /etc/* && -e "${_file}" ]]; then
|
||||
if [ "${_has_conffiles}" -eq 1 ]; then
|
||||
if leaf_conffile_is_unmodified "${_file}" "${_trace_dir}"; then
|
||||
rm -fv -- "${_file}"
|
||||
else
|
||||
leaf_record_message "Preserved modified config file: ${_file}"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
# fallback for old packages without CONFFILES (keep old behavior)
|
||||
if [[ -f "${_file}" ]]; then
|
||||
_backup_etc=true
|
||||
_relative_path="${_file#/etc/}"
|
||||
mkdir -pv "${_etc_backup_path}/$(dirname "${_relative_path}")"
|
||||
mv -v -- "${_file}" "${_etc_backup_path}/${_relative_path}"
|
||||
else
|
||||
rm -fv -- "${_file}"
|
||||
fi
|
||||
else
|
||||
rm -fv -- "${_file}"
|
||||
fi
|
||||
done < "${_trace_dir}/FILES"
|
||||
|
||||
if [[ "${_backup_etc}" == true ]]; then
|
||||
leaf_record_message "Found files in /etc, backuped to ${_etc_backup_path}."
|
||||
fi
|
||||
cat "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}/LINKS" | while read -r _link; do
|
||||
[ ! -L "${_link}" ] || rm -fv "${_link}"
|
||||
done
|
||||
cat "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}/DIRS" | while read -r _directory; do
|
||||
if [[ -d _directory ]]; then
|
||||
|
||||
# LINKS
|
||||
while read -r _link; do
|
||||
if leaf_in_skiplist "${_link}"; then
|
||||
continue
|
||||
fi
|
||||
if [[ "${_link}" == /etc/* && -e "${_link}" ]]; then
|
||||
if [ "${_has_conffiles}" -eq 1 ]; then
|
||||
if leaf_conffile_is_unmodified "${_link}" "${_trace_dir}"; then
|
||||
rm -fv -- "${_link}"
|
||||
else
|
||||
leaf_record_message "Preserved modified config link: ${_link}"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
else
|
||||
[ ! -L "${_link}" ] || rm -fv -- "${_link}"
|
||||
fi
|
||||
done < "${_trace_dir}/LINKS"
|
||||
|
||||
# DIRS
|
||||
while read -r _directory; do
|
||||
if leaf_in_skiplist "${_directory}"; then
|
||||
continue
|
||||
fi
|
||||
if [[ -d "${_directory}" ]]; then
|
||||
rmdir --ignore-fail-on-non-empty "${_directory}"
|
||||
fi
|
||||
done
|
||||
src_postremove
|
||||
done < "${_trace_dir}/DIRS"
|
||||
|
||||
leaf_phase_must "Postremove" src_postremove "Postremove failed."
|
||||
leaf_invoke_hooks remove
|
||||
rm -rf "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}"
|
||||
rm -rf "${_trace_dir}"
|
||||
leaf_update_package_database delete "${PKG_PREFIX}/${PKG_NAME}"
|
||||
leaf_update_environment
|
||||
echo -e "${GREEN_COLOR}<<< removed ${PKG_PREFIX}/${PKG_NAME}${CLEAR_COLOR}"
|
||||
}
|
||||
|
||||
leaf_search_package() {
|
||||
@@ -577,6 +1031,40 @@ leaf_show_package() {
|
||||
echo -e "License: ${license[@]}\n"
|
||||
}
|
||||
|
||||
leaf_query() {
|
||||
local q="$1"
|
||||
local trace_root="${TRACE_DIR:-/var/lib/leaf/trace}"
|
||||
|
||||
if ! leaf_find_remove_pkgbuild "$q"; then
|
||||
leaf_error "${q} cannot found in the leaf installed database."
|
||||
fi
|
||||
|
||||
local pkgbuild_path="${trace_root}/${PKG_PREFIX}/${PKG_NAME}/PKGBUILD"
|
||||
[[ -f "$pkgbuild_path" ]] || leaf_error "query: missing PKGBUILD: $pkgbuild_path"
|
||||
[[ -r "$pkgbuild_path" ]] || leaf_error "query: PKGBUILD not readable: $pkgbuild_path"
|
||||
|
||||
(
|
||||
set -euo pipefail
|
||||
|
||||
unset pkgname pkgver
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
source "$pkgbuild_path"
|
||||
|
||||
local full="${PKG_PREFIX}/${PKG_NAME}"
|
||||
|
||||
if [[ -t 1 && -z "${NO_COLOR-}" ]]; then
|
||||
local BOLD=$'\e[1m'
|
||||
printf '%s%s%s %s %s%s%s\n' \
|
||||
"${BOLD}" "${full}" "${CLEAR_COLOR}" \
|
||||
"${pkgname}" \
|
||||
"${GREEN_COLOR}" "${pkgver}" "${CLEAR_COLOR}"
|
||||
else
|
||||
printf '%s %s %s\n' "${full}" "${pkgname}" "${pkgver}"
|
||||
fi
|
||||
)
|
||||
}
|
||||
|
||||
leaf_pack_package() {
|
||||
leaf_build_package
|
||||
pushd "${srcdir}" > /dev/null 2>&1
|
||||
|
||||
Reference in New Issue
Block a user