From 5ee54183650256a7802e5e1bb1ce6b083da3cac8 Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Wed, 26 Nov 2025 16:42:26 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=B0=D0=BD=20=D1=81=D0=BA=D1=80=D0=B8=D0=BF=D1=82?= =?UTF-8?q?=20=D1=81=20=D1=83=D1=87=D0=B5=D1=82=D0=BE=D0=BC=20=D1=83=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D1=80=D0=B5=D0=B2=D1=88=D0=B5=D0=B9=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=81=D0=B8=D0=B8=20ar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appimage-builder.sh | 365 ++++++++++++++++++++++++++-------------- 1 file changed, 240 insertions(+), 125 deletions(-) diff --git a/src/appimage-builder.sh b/src/appimage-builder.sh index e95a022..1b713d9 100755 --- a/src/appimage-builder.sh +++ b/src/appimage-builder.sh @@ -1,65 +1,118 @@ #!/bin/bash -# title :appimage-builder.sh +# title :aib.sh # description :Script for building AppImage # author :Alexander Zhirov -# date :20240522 -# version :0.2.0 -# usage :bash appimage-builder.sh --help +# date :20251125 +# version :0.3.0 +# usage :bash aib.sh --help #=============================================================================== -# Пакеты AppImageTool -# https://github.com/AppImage/appimagetool/releases -# Среда выполнения -# https://github.com/AppImage/type2-runtime/releases +set -euo pipefail -set -e +VERSION="0.3.0" +SCRIPT_NAME=${0##*/} -version="0.2.0" +show_help() { + cat </dev/null) -cpio_tool=$(which cpio 2>/dev/null) +Опции: + -t, --appimage-tool Инструмент сборки AppImage + -r, --appimage-runtime Инструмент времени выполнения + -i, --input-path Директория с пакетами исходных файлов (по умолчанию: ./apps) + -o, --output-path Директория сохранения собранных AppImage (по умолчанию: \$HOME/AppImages) + -c, --cache-path Директория кеша для пакетов sources (по умолчанию: временная через mktemp -d) + -l, --log-path Директория для сохранения журнала сборки (по умолчанию: как output-path) -if [ ! -x "${rpm2cpio_tool}" ]; then - echo "Отсутствует утилита rpm2cpio" - exit 1 -fi + -h, --help Показать эту справку и выйти + -v, --version Показать версию скрипта и выйти +EOF +} -if [ ! -x "${cpio_tool}" ]; then - echo "Отсутствует утилита cpio" - exit 1 -fi +show_version() { + echo "${SCRIPT_NAME} ${VERSION}" +} + +check_requirements() { + local appimage_tool="$1" + local appimage_runtime="$2" + + local -a missing=() + local cmd + + # Базовые утилиты + for cmd in fusermount ar rpm2cpio cpio wget tar patch; do + if ! command -v "$cmd" >/dev/null 2>&1; then + missing+=("$cmd") + fi + done + + # appimagetool-x86_64.AppImage + if [[ -z "$appimage_tool" || ! -x "$appimage_tool" ]]; then + # тут проверяем именно логическое наличие утилиты, + # имя в сообщении — как пользователь ожидает её видеть + missing+=("appimagetool-x86_64.AppImage") + fi + + # runtime-x86_64 + if [[ -z "$appimage_runtime" || ! -x "$appimage_runtime" ]]; then + missing+=("runtime-x86_64") + fi + + if ((${#missing[@]} > 0)); then + echo "Ошибка: отсутствуют необходимые утилиты:" >&2 + printf ' - %s\n' "${missing[@]}" >&2 + echo "Установите их и повторите попытку." >&2 + exit 1 + fi +} extrpm() { local rpm="${1}" local destination_path="${2}" - ${rpm2cpio_tool} ${rpm} | ${cpio_tool} -idmvD ${destination_path} + rpm2cpio ${rpm} | cpio -idmvD ${destination_path} +} + +extdeb() { + local deb="$1" + local tar_name="$2" + local dest_dir="$3" + + local version + version=$(ar --version | head -n1 | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -n1) + + IFS='.' read -r major minor <<< "$version" + + if (( major > 2 )) || (( major == 2 && minor >= 39 )); then + ar xv "$deb" "$tar_name" --output "$dest_dir" + else + local tmpdir + tmpdir=$(mktemp -d 2>/dev/null) + (cd "$tmpdir" && ar x "$deb") + cp "$tmpdir/$tar_name" "$dest_dir/" + rm -rf "$tmpdir" + fi } build_appimage() { - local pkg="${1}" + local appimage_tool="${1}" + local appimage_runtime="${2}" + local input_path="${3}" + local output_path="${4}" + local cache_path="${5}" + local pkg="${6}" + local name=$(basename ${pkg}) local build="${pkg}/build.sh" local sources="${pkg}/sources" local files="${pkg}/files" - local cache="${appimage_cache}/${name}" + local cache="${cache_path}/${name}" - # Временные директории для сборки + # Временные директории для сборки local tmp="/tmp/appimage_${name}_$(date "+%Y%m%d%H%M%S")" local src="${tmp}/src" local app="${tmp}/app" @@ -103,7 +156,7 @@ build_appimage() { for deb in $(find ${cache} -name "*.deb"); do local tar_name=$(ar t ${deb} | grep data) local tar_archive="${src}/${tar_name}" - ar xv ${deb} ${tar_name} --output ${src} + extdeb ${deb} ${tar_name} ${src} tar xvf ${tar_archive} -C ${app} rm -v ${tar_archive} done @@ -116,144 +169,204 @@ build_appimage() { export current_path=$(dirname ${build}) export app_path=${app} ${build} - unset ${current_path} - unset ${app_path} + unset current_path + unset app_path fi - local name_appimage=$(grep "Name" ${files}/*.desktop | sed 's/Name=//' | tr ' ' '_')-x86_64.AppImage + local name_appimage=$(grep 'Name' ${files}/*.desktop | sed 's/Name=//' | tr ' ' '_')-x86_64.AppImage - ${appimage_tool} -v --runtime-file ${appimage_runtime} ${app} ${appimage_output}/${name_appimage} + ${appimage_tool} -v --runtime-file ${appimage_runtime} ${app} ${output_path}/${name_appimage} rm -rvf ${tmp} return 0 } -help_scritp() { - echo -e "\n" \ - "Использование: $(basename ${0}) [ОПЦИИ]... [СПИСОК ПАКЕТОВ ДЛЯ СБОРКИ]\n" \ - "Основные опции:\n" \ - "\t-t\t--appimage-tool\t\tИнструмент сборки AppImage\n" \ - "\t-r\t--appimage-runtime\tИнструмент времени выполнения\n" \ - "\t-i\t--input-path\t\tДиректория с пакетами исходных файлов\n" \ - "\t-o\t--output-path\t\tДиректория сохранения собранных AppImage\n" \ - "\t-c\t--cache-path\t\tДиректория кеша для пакетов sources\n" \ - "\t-l\t--log-path\t\tДиректория для сохранения журнала сборки\n" \ - "Дополнительно:\n" \ - "\t-h\t--help\t\t\tПоказать справку\n" \ - "\t-v\t--version\t\tВерсия скрипта\n" -} - main() { + local appimage_tool="" + local appimage_runtime="" + local input_path="" + local output_path="" + local cache_path="" + local log_path="" + + # флаги: директория была явно передана + local output_specified=false + local cache_specified=false + local log_specified=false + + # массив с именами пакетов для сборки + local -a appimage_apps=() + while [[ $# -gt 0 ]]; do - key="${1}" - value="${2}" - case "${key}" in + case "$1" in -t|--appimage-tool) - appimage_tool="${value}" - shift + if [[ $# -lt 2 ]]; then + echo "Ошибка: для опции $1 требуется аргумент" >&2 + exit 1 + fi + appimage_tool=$2 + shift 2 ;; -r|--appimage-runtime) - appimage_runtime="${value}" - shift + if [[ $# -lt 2 ]]; then + echo "Ошибка: для опции $1 требуется аргумент" >&2 + exit 1 + fi + appimage_runtime=$2 + shift 2 ;; -i|--input-path) - appimage_input="${value}" - shift + if [[ $# -lt 2 ]]; then + echo "Ошибка: для опции $1 требуется аргумент" >&2 + exit 1 + fi + input_path=$2 + shift 2 ;; -o|--output-path) - appimage_output="${value}" - shift + if [[ $# -lt 2 ]]; then + echo "Ошибка: для опции $1 требуется аргумент" >&2 + exit 1 + fi + output_path=$2 + output_specified=true + shift 2 ;; -c|--cache-path) - appimage_cache="${value}" - shift + if [[ $# -lt 2 ]]; then + echo "Ошибка: для опции $1 требуется аргумент" >&2 + exit 1 + fi + cache_path=$2 + cache_specified=true + shift 2 ;; -l|--log-path) - appimage_log="${value}" - shift + if [[ $# -lt 2 ]]; then + echo "Ошибка: для опции $1 требуется аргумент" >&2 + exit 1 + fi + log_path=$2 + log_specified=true + shift 2 ;; -h|--help) - help_scritp + show_help exit 0 ;; -v|--version) - echo "Версия скрипта: ${version}" + show_version exit 0 ;; + --) + shift + while [[ $# -gt 0 ]]; do + appimage_apps+=("$1") + shift + done + ;; + -*) + echo "Неизвестная опция: $1" >&2 + exit 1 + ;; *) - appimage_apps+=("${key}") + appimage_apps+=("$1") + shift ;; esac - shift done - if [ -z "${appimage_tool}" ] ; then - appimage_tool=$(which appimagetool-x86_64.AppImage 2>/dev/null) + # Значения по умолчанию для тулов + if [[ -z "$appimage_tool" ]]; then + appimage_tool=$(command -v appimagetool-x86_64.AppImage 2>/dev/null || true) fi - if [ ! -f "${appimage_tool}" ]; then - echo "Отсутствует инструмент сборки: https://github.com/AppImage/appimagetool" + if [[ -z "$appimage_runtime" ]]; then + appimage_runtime=$(command -v runtime-x86_64 2>/dev/null || true) + fi + + # Значения по умолчанию для директорий + if [[ -z "$input_path" ]]; then + input_path="${PWD}/apps" + fi + + if [[ -z "$output_path" ]]; then + output_path="${HOME}/AppImages" + fi + + # cache: если не указали — mktemp -d + if ! $cache_specified; then + cache_path=$(mktemp -d /tmp/appimage_cache.XXXXXX) + fi + + # log: если не указали — как output_path + if [[ -z "$log_path" ]]; then + log_path="$output_path" + fi + + # Проверяем наличие утилит уже с подставленными дефолтами + check_requirements "$appimage_tool" "$appimage_runtime" + + # Проверяем, что есть хотя бы один пакет + if ((${#appimage_apps[@]} == 0)); then + echo "Ошибка: не указан список пакетов для сборки." >&2 + echo "См. справку: ${SCRIPT_NAME} --help" >&2 exit 1 fi - if [ ! -x "${appimage_tool}" ]; then - echo "Инструмент сборки $(basename ${appimage_tool}) должен быть исполняемым" + # INPUT: всегда должен существовать, не создаём + if [[ ! -d "$input_path" ]]; then + echo "Ошибка: директория input-path не существует: $input_path" >&2 + echo "Создайте её или укажите путь явно с помощью -i|--input-path." >&2 exit 1 fi - if [ -z "${appimage_runtime}" ] ; then - appimage_runtime=$(which runtime-x86_64 2>/dev/null) + # OUTPUT: + if $output_specified; then + if [[ ! -d "$output_path" ]]; then + echo "Ошибка: указанная директория output-path не существует: $output_path" >&2 + exit 1 + fi + else + mkdir -p "$output_path" fi - if [ ! -f "${appimage_runtime}" ]; then - echo "Отсутствует инструмент времени выполнения: https://github.com/AppImage/type2-runtime" - exit 1 + # CACHE: + if $cache_specified; then + if [[ ! -d "$cache_path" ]]; then + echo "Ошибка: указанная директория cache-path не существует: $cache_path" >&2 + exit 1 + fi fi - if [ ! -x "${appimage_runtime}" ]; then - echo "Инструмент времени выполнения $(basename ${appimage_runtime}) должен быть исполняемым" - exit 1 - fi - - if [ ! -d "${appimage_input}" ]; then - echo "Не установлена директория исхоных файлов для сборки AppImage" - exit 1 - fi - - if [ "$(find ${appimage_input} -maxdepth 1 -type d | wc -l)" -eq 1 ]; then - echo "Директория не содержит исхоных файлов для сборки AppImage: ${appimage_input}" - exit 1 - fi - - if [ ! -d "${appimage_output}" ]; then - appimage_output="${HOME}/AppImages" - mkdir -p "${appimage_output}" - echo "Не установлена директория для сохранения AppImage. Будет использована директория по умолчанию: ${appimage_output}" - fi - - if [ ! -d "${appimage_cache}" ]; then - appimage_cache="/tmp/appimage_cache" - mkdir -p "${appimage_cache}" - echo "Не определена директория для кеша. Будет использована директория по умолчанию: ${appimage_cache}" - fi - - if [ ! -d "${appimage_log}" ]; then - appimage_log="${appimage_output}" - echo "Не определена директория для журнала. Будет использована директория по умолчанию: ${appimage_output}" + # LOG: + if $log_specified; then + if [[ ! -d "$log_path" ]]; then + echo "Ошибка: указанная директория log-path не существует: $log_path" >&2 + exit 1 + fi fi echo "Инструмент сборки: ${appimage_tool}" echo "Инструмент времени выполнения: ${appimage_runtime}" - echo "Директория исходных файлов: ${appimage_input}" - echo "Директория для сохранения AppImage: ${appimage_output}" - echo "Директория для кеша: ${appimage_cache}" - echo "Список собираемых пакетов: ${appimage_apps[@]}" + echo "Директория исходных файлов: ${input_path}" + echo "Директория для сохранения AppImage: ${output_path}" + echo "Директория для кеша: ${cache_path}" + echo "Директория для журнала: ${log_path}" + echo "Список собираемых пакетов: ${appimage_apps[*]}" - local pkgs=$(printf "%s\n" "${appimage_apps[@]}" | xargs -I {} find ${appimage_input} -name {} -mindepth 1 -maxdepth 1 -type d) + local pkgs=$(printf "%s\n" "${appimage_apps[*]}" | xargs -I {} find ${input_path} -name {} -mindepth 1 -maxdepth 1 -type d) for pkg in ${pkgs}; do - if build_appimage ${pkg} > "${appimage_log}/appimage_build.log" 2>&1 ; then + echo $pkg + if build_appimage \ + ${appimage_tool} \ + ${appimage_runtime} \ + ${input_path} \ + ${output_path} \ + ${cache_path} \ + ${pkg} &>> "${log_path}/appimage_build.log"; then echo "AppImage $(basename ${pkg}) был собран" else echo "AppImage $(basename ${pkg}) не был собран" @@ -261,4 +374,6 @@ main() { done } -main "${@}" +if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then + main "$@" +fi