add old lci module

This commit is contained in:
qing committed 2026-08-02 20:15:07 +08:00
1 parent 38a6e815f0
commit 8499631091
171 files changed
+25562

No files matched your search

+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 gopsc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+91
View File
@@ -0,0 +1,91 @@
## qemu_run_appimage是什么?
这个项目源于我想用qemu-user运行AppImage的想法。
qemu-user是一款支持多种架构的能够虚拟化运行单个程序的虚拟机软件,而AppImage是一款能够打包出单文件应用的工具。
## 跨架构运行的障碍是什么?
Linux系统支持许多架构,这些架构的机器码、汇编语言都是有差异的,所以不同架构的系统在一般情况下是无法混合使用的,qemu虚拟机能够解决这个痛点,但是这需要一些技巧。
通常而言,一个程序除了在编译阶段静态链接了许多库,出于一些原因的考量,它也会在运行时动态地加载一些动态库文件。
使用qemu-user跨架构运行,常常就会出现单个文件能够执行,但是动态链接库找不到的问题。
## AppImage起到一个什么作用?
AppImage能够将许多文件打包成一个二进制程序,你双击运行它就能够直接执行,因为关键的动态连接库都包含在里面了,通过一些技术手段让程序能够访问到。
所以我们能够将程序以及动态库打包成单个可执行程序,就有了能够虚拟化运行该程序的先决条件。
## 如何使用qemu-user运行AppImage
通常而言,AppImage里面会有一个叫AppRun的启动脚本,运行AppImage会使用shell解释器启动它,然后该脚本配置好环境,再由它启动目标程序。
但是qemu-user虚拟出的进程往往无法在宿主机系统中找到与它架构相对应的shell,所以就无法执行常规方式打包的AppImage文件。
那我通过与deepseek的深度交流,最终解决了这个问题。
我的思路有两个,一是打包AppImage时将AppRun编译成二进制文件,这个办法我经过了验证,是可行的。
还有一个办法是再打包一个shell解释器进去,这个我还没有尝试、
在运行时,我采用qemu-user提取出AppImage中的所有的文件,并且放在一个临时文件夹,然后在设置引用环境为该文件夹的方式下通过qemu-user执行其中的AppRun,应该就能够启动程序。
目前已经尝试了在树莓派上运行x86_64架构的一系列工具,包含ls命令、bash解释器、python3解释器,都能够成功,接下来我还会对网络等功能进行测试。
## 这个方法有哪些缺点?
我像一些朋友展示了这个方式,能够运行测试用例,但是他们说qemu的速度很慢,我了解了一下,qemu-user的性能损耗可能在2倍以内,一般的程序似乎还是可以接受,qemu虚拟机完整情况下的确较慢,性能损耗能够达到8~10倍。
所以一些开发者对常见架构进行了深度优化,成果比如box64(Arm64平台运行X86程序)、LATX(LA64平台运行x86程序),这些程序虽然支持的架构没有qemu-user多,在一些平台上可能也需要单独配置一些东西,但是在它们支持的架构下拥有更好的性能,在这里也推荐大家使用。
## 我的一些别的想法
既然qemu-user能够进行跨架构的实时翻译,那我们可能也能做到将一个二进制文件直接再编译成其他架构的二进制文件?这是我的一个想法。
## 项目逻辑与使用方法
项目中的auto-appimage1.sh这是一个打包脚本,能够在要运行的目标架构上打包出包含所有链接库的AppImage文件,且其中的入口程序AppRun是二进制的。
该脚本的使用方式如下:
```bash auto-appimage1.sh <filename>```
目前这个脚本仅能打包x86的程序,如果要在别的平台打包AppImage,可能需要进行调整。
qemu_run2.sh是在宿主机上的启动脚本,其中使用了qemu-user-static系列工具创建虚拟环境,static后缀的意思是这个工具是静态编译的,如果你的架构上没有这个软件,请安装它的非静态版本qemu-user,然后将其中的qemu-*-static都改为qemu-*
通过一下方式使用它:
```bash qemu_run2.sh <filename.AppImage>```
ls.AppImage是x86_64架构下的ls命令打包,是测试用例。
该项目的依赖:
---------------
打包机器依赖项:
appimagetool需要在github下载制品,并且添加可执行权限放到环境变量里。
```apt install fuse libfuse2```
---------------
宿主机依赖:
```apt install qemu-user-static```
或者
```apt install aemu-user```
————————————————
版权声明:本文为CSDN博主「gapcuda」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/gapcuda/article/details/156559857
(这是我自己的csdn账号)
@@ -0,0 +1,686 @@
#!/bin/bash
# appimage-packager-rofs-fixed.sh
# 已添加QEMU兼容性支持
set -e
# 彩色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 创建图标
create_icon() {
local icon_path="$1"
mkdir -p "$(dirname "$icon_path")"
echo 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==' | \
base64 -d > "$icon_path" 2>/dev/null || \
echo -ne '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\nIDATx\x9cc\xf8\x0f\x00\x00\x01\x00\x01\x05\x01\x00\x00\x00\x00IEND\xaeB`\x82' > "$icon_path"
}
# 创建简单二进制AppRun(修复版)
create_simple_binary_apprun() {
local app_dir="$1"
local app_name="$2"
local orig_name="$3"
echo "创建简单二进制AppRun..."
# 先回退到脚本版本
cat > "$app_dir/AppRun" << 'EOF'
#!/bin/sh
# 简单启动脚本(兼容性最好)
HERE="$(dirname "$(readlink -f "$0")")"
APP_NAME="$(basename "$0" .AppImage)"
# 设置环境
export PATH="$HERE/usr/bin:$PATH"
export LD_LIBRARY_PATH="$HERE/usr/lib:$LD_LIBRARY_PATH"
# 检测架构
if [ "$(uname -m)" = "x86_64" ]; then
# x86系统直接运行
exec "$HERE/usr/bin/$APP_NAME" "$@"
else
# ARM系统,通过QEMU运行
# 确保有lib64目录
if [ ! -d "$HERE/lib64" ] && [ -f "$HERE/usr/lib/ld-linux-x86-64.so.2" ]; then
mkdir -p "$HERE/lib64"
cp "$HERE/usr/lib/ld-linux-x86-64.so.2" "$HERE/lib64/" 2>/dev/null || true
fi
# 使用QEMU运行
exec qemu-x86_64-static -L "$HERE" "$HERE/usr/bin/$APP_NAME" "$@"
fi
EOF
chmod +x "$app_dir/AppRun"
echo -e "${GREEN}✓ 创建脚本版AppRun${NC}"
}
# 创建二进制AppRun函数(修复版)
create_binary_apprun() {
local app_dir="$1"
local app_name="$2"
local orig_name="$3"
echo -e "${YELLOW}创建二进制AppRun...${NC}"
# 创建修复的C源代码
cat > /tmp/apprun_fixed.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
// 获取程序所在目录
static char* get_program_dir() {
static char path[4096];
ssize_t len = readlink("/proc/self/exe", path, sizeof(path)-1);
if (len == -1) {
return NULL;
}
path[len] = '\0';
// 找到最后一个'/'
char* last_slash = strrchr(path, '/');
if (last_slash) {
*last_slash = '\0';
}
return path;
}
// 检查文件是否存在
static int file_exists(const char* path) {
struct stat st;
return stat(path, &st) == 0 && S_ISREG(st.st_mode);
}
// 执行程序
static void execute_program(const char* dir, const char* prog_name, char* const argv[]) {
char prog_path[4096];
snprintf(prog_path, sizeof(prog_path), "%s/usr/bin/%s", dir, prog_name);
if (!file_exists(prog_path)) {
// 尝试查找任何可执行文件
fprintf(stderr, "错误: 未找到可执行文件 %s\n", prog_path);
exit(1);
}
// 设置环境变量
setenv("HERE", dir, 1);
char ld_path[4096];
snprintf(ld_path, sizeof(ld_path), "%s/usr/lib", dir);
setenv("LD_LIBRARY_PATH", ld_path, 1);
char path[4096];
const char* old_path = getenv("PATH");
if (old_path) {
snprintf(path, sizeof(path), "%s/usr/bin:%s", dir, old_path);
} else {
snprintf(path, sizeof(path), "%s/usr/bin", dir);
}
setenv("PATH", path, 1);
// 执行程序
execv(prog_path, argv);
// 如果execv失败
fprintf(stderr, "无法执行程序: %s\n", prog_path);
exit(1);
}
int main(int argc, char* argv[]) {
// 获取程序所在目录
char* dir = get_program_dir();
if (!dir) {
fprintf(stderr, "无法获取程序目录\n");
return 1;
}
// 从AppImage文件名获取程序名
char* prog_name = "program";
// 尝试从argv[0]获取
if (argc > 0 && argv[0]) {
char* app_name = argv[0];
char* last_slash = strrchr(app_name, '/');
if (last_slash) {
app_name = last_slash + 1;
}
// 移除.AppImage后缀
char* dot_appimage = strstr(app_name, ".AppImage");
if (dot_appimage) {
*dot_appimage = '\0';
prog_name = app_name;
}
}
// 准备新参数数组
char** new_argv = malloc((argc + 1) * sizeof(char*));
if (!new_argv) {
fprintf(stderr, "内存分配失败\n");
return 1;
}
new_argv[0] = prog_name;
for (int i = 1; i < argc; i++) {
new_argv[i] = argv[i];
}
new_argv[argc] = NULL;
// 执行程序
execute_program(dir, prog_name, new_argv);
free(new_argv);
return 0;
}
EOF
# 尝试编译为x86_64二进制(静态链接)
echo "编译x86_64二进制..."
# 检查是否有交叉编译器
if command -v x86_64-linux-gnu-gcc >/dev/null 2>&1; then
echo "使用x86_64-linux-gnu-gcc编译..."
x86_64-linux-gnu-gcc -static -Os -o "$app_dir/AppRun" /tmp/apprun_fixed.c 2>&1 | grep -v "warning" || true
elif command -v gcc >/dev/null 2>&1; then
echo "使用gcc交叉编译..."
gcc -target x86_64-linux-gnu -static -Os -o "$app_dir/AppRun" /tmp/apprun_fixed.c 2>&1 | grep -v "warning" || true
else
echo "未找到编译器"
fi
# 检查编译是否成功
if [[ -f "$app_dir/AppRun" ]] && [[ -x "$app_dir/AppRun" ]]; then
# 验证是x86_64二进制
if file "$app_dir/AppRun" | grep -q "x86-64"; then
chmod +x "$app_dir/AppRun"
echo -e "${GREEN}✓ 二进制AppRun创建成功${NC}"
file "$app_dir/AppRun"
return 0
else
echo "编译出的不是x86-64二进制"
rm -f "$app_dir/AppRun"
fi
fi
# 编译失败,使用简单脚本
echo "二进制编译失败,使用脚本版本"
create_simple_binary_apprun "$app_dir" "$app_name" "$orig_name"
rm -f /tmp/apprun_fixed.c
}
# 创建简单二进制AppRun
create_simple_binary_apprun() {
local app_dir="$1"
local app_name="$2"
local orig_name="$3"
# 创建极简的汇编二进制
cat > /tmp/minimal.S << 'EOF'
.section .note.GNU-stack,"",@progbits
.section .text
.globl _start
_start:
# execve("./usr/bin/program", argv, envp)
mov $59, %rax # syscall: execve
# 构建路径字符串
lea path(%rip), %rdi # arg1: filename
# 构建参数数组 ["./usr/bin/program", NULL]
lea argv(%rip), %rsi # arg2: argv
# 环境变量
xor %rdx, %rdx # arg3: envp = NULL
syscall
# 如果失败,退出
mov $60, %rax # syscall: exit
mov $1, %rdi # status = 1
syscall
path:
.ascii "./usr/bin/program\0"
argv:
.quad path
.quad 0
EOF
# 汇编并链接
as --64 -o /tmp/minimal.o /tmp/minimal.S
ld -m elf_x86_64 -s -o "$app_dir/AppRun" /tmp/minimal.o
if [[ -f "$app_dir/AppRun" ]]; then
chmod +x "$app_dir/AppRun"
echo -e "${GREEN}✓ 极简二进制AppRun已创建${NC}"
else
echo -e "${RED}错误: 无法创建二进制AppRun${NC}"
# 回退到脚本
create_script_apprun "$app_dir" "$app_name" "$orig_name"
fi
rm -f /tmp/minimal.S /tmp/minimal.o
}
# 使用busybox作为AppRun
use_busybox_as_apprun() {
local app_dir="$1"
echo "使用busybox作为AppRun..."
# 查找或下载x86_64的busybox
if [[ -f /usr/bin/busybox ]] && file /usr/bin/busybox | grep -q "x86-64"; then
cp /usr/bin/busybox "$app_dir/AppRun"
elif [[ -f /bin/busybox ]] && file /bin/busybox | grep -q "x86-64"; then
cp /bin/busybox "$app_dir/AppRun"
else
# 尝试下载静态版busybox
echo "下载静态busybox..."
wget -q -O "$app_dir/AppRun" "https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox" 2>/dev/null || {
echo "下载失败,使用备用方案"
return 1
}
fi
if [[ -f "$app_dir/AppRun" ]]; then
chmod +x "$app_dir/AppRun"
# 创建busybox的symlink脚本
cat > "$app_dir/.busybox-run" << 'EOF'
#!/bin/sh
# busybox wrapper
HERE=$(dirname "$0")
export PATH="$HERE/usr/bin:$PATH"
export LD_LIBRARY_PATH="$HERE/usr/lib:$LD_LIBRARY_PATH"
exec "$HERE/AppRun" "$HERE/usr/bin/$(basename "$0" .AppImage)" "$@"
EOF
chmod +x "$app_dir/.busybox-run"
echo -e "${GREEN}✓ 使用busybox作为AppRun${NC}"
return 0
fi
return 1
}
# 收集所有动态库依赖
collect_all_dependencies() {
local executable="$1"
local lib_dir="$2"
echo -e "${YELLOW}收集动态库依赖...${NC}"
if ! ldd "$executable" 2>/dev/null | grep -q "=>"; then
echo "程序是静态链接,无需库文件"
return 0
fi
mkdir -p "$lib_dir"
local processed_libs=()
collect_libs_recursive() {
local target="$1"
ldd "$target" 2>/dev/null | grep "=>" | awk '{print $3}' | while read -r lib; do
if [[ -f "$lib" ]]; then
local libname=$(basename "$lib")
if [[ ! " ${processed_libs[@]} " =~ " ${libname} " ]]; then
processed_libs+=("$libname")
cp "$lib" "$lib_dir/" 2>/dev/null
if [[ $? -eq 0 ]]; then
echo " ✅ $libname"
collect_libs_recursive "$lib"
fi
fi
fi
done
}
collect_libs_recursive "$executable"
# 添加基础库
local common_libs=(
"ld-linux-x86-64.so.2"
"libc.so.6" "libm.so.6" "libpthread.so.0"
"libdl.so.2" "librt.so.1" "libgcc_s.so.1"
"libstdc++.so.6"
)
for lib in "${common_libs[@]}"; do
find /usr/lib /lib /lib64 -name "$lib" -type f 2>/dev/null | head -1 | while read -r libpath; do
if [[ -f "$libpath" ]] && [[ ! -f "$lib_dir/$(basename "$libpath")" ]]; then
cp "$libpath" "$lib_dir/" 2>/dev/null && echo " ✅ 基础: $(basename "$libpath")"
fi
done
done
local lib_count=$(ls -1 "$lib_dir"/*.so* 2>/dev/null | wc -l)
echo -e "${GREEN}✓ 添加了 $lib_count 个库${NC}"
}
# 设置QEMU目录结构
setup_qemu_structure() {
local app_dir="$1"
echo -e "${YELLOW}设置QEMU目录结构...${NC}"
# 创建QEMU需要的目录结构
mkdir -p "$app_dir/lib64"
mkdir -p "$app_dir/usr/gnemul/qemu-x86_64"
# 复制库到QEMU期望的位置
if [[ -d "$app_dir/usr/lib" ]]; then
echo "复制库文件到QEMU目录..."
# 复制所有库
cp -r "$app_dir/usr/lib/"* "$app_dir/usr/gnemul/qemu-x86_64/" 2>/dev/null || true
# 特别处理ld-linux动态链接器
if [[ -f "$app_dir/usr/lib/ld-linux-x86-64.so.2" ]]; then
echo "设置动态链接器..."
# 在lib64创建链接(QEMU查找的位置)
ln -sf ../usr/lib/ld-linux-x86-64.so.2 "$app_dir/lib64/ld-linux-x86-64.so.2" 2>/dev/null || true
# 同时也复制一份到lib64确保可用
cp "$app_dir/usr/lib/ld-linux-x86-64.so.2" "$app_dir/lib64/" 2>/dev/null || true
fi
echo -e "${GREEN}✓ QEMU目录结构已创建${NC}"
else
echo -e "${YELLOW}⚠ 未找到库目录,跳过QEMU结构设置${NC}"
fi
}
# 查找并复制数据文件
find_and_copy_data() {
local executable="$1"
local app_name="$2"
local data_dir="$3"
echo -e "${YELLOW}查找数据文件...${NC}"
local prog_dir=$(dirname "$(realpath "$executable")")
local patterns=("*.pem" "*.key" "*.crt" "*.cfg" "*.conf" "*.ini" "*.json" "*.xml")
mkdir -p "$data_dir"
local count=0
# 首先查找程序目录
for pattern in "${patterns[@]}"; do
find "$prog_dir" -maxdepth 2 -type f -name "$pattern" 2>/dev/null | while read -r file; do
if [[ "$file" != "$executable" ]]; then
cp "$file" "$data_dir/" 2>/dev/null && {
echo " ✅ $(basename "$file")"
count=$((count + 1))
}
fi
done
done
# 如果没有找到,尝试复制所有非可执行文件
if [[ $count -eq 0 ]]; then
find "$prog_dir" -maxdepth 1 -type f ! -name "$(basename "$executable")" ! -name "*.AppImage" | while read -r file; do
cp "$file" "$data_dir/" 2>/dev/null && echo " ✅ $(basename "$file")"
done
fi
# 如果没有数据文件,创建示例配置
if [[ $count -eq 0 ]] && [[ ! -f "$data_dir/config.ini" ]]; then
cat > "$data_dir/config.ini" <<EOF
# 程序配置文件
# 请根据实际情况修改
[General]
name=$app_name
version=1.0.0
[Paths]
data_dir=./data
log_dir=./logs
[Network]
host=127.0.0.1
port=8080
EOF
echo " 📝 创建示例配置文件"
fi
echo -e "${GREEN}✓ 数据文件处理完成${NC}"
}
# 主程序
main() {
if [[ $# -lt 1 ]]; then
echo -e "${GREEN}AppImage 打包工具 (QEMU兼容版)${NC}"
echo "用法: $0 <可执行文件> [输出名称]"
echo " $0 -d <可执行文件> [输出名称] (包含数据文件)"
echo ""
echo "示例:"
echo " $0 ./myapp"
echo " $0 -d ./myapp"
exit 1
fi
# 检查是否包含数据文件
INCLUDE_DATA=false
if [[ "$1" == "-d" ]] || [[ "$1" == "--data" ]]; then
INCLUDE_DATA=true
shift
fi
echo -e "${BLUE}════════════════════════════════════════${NC}"
echo -e "${BLUE} AppImage 打包工具 ${NC}"
echo -e "${BLUE} (QEMU兼容版) ${NC}"
echo -e "${BLUE}════════════════════════════════════════${NC}"
EXECUTABLE="$1"
APP_NAME="${2:-$(basename "$EXECUTABLE")}"
[[ ! -f "$EXECUTABLE" ]] && { echo -e "${RED}错误: 文件不存在${NC}"; exit 1; }
[[ ! -x "$EXECUTABLE" ]] && chmod +x "$EXECUTABLE"
echo "程序: $APP_NAME"
echo "原始: $(basename "$EXECUTABLE")"
# 清理
rm -rf "AppDir" "${APP_NAME}.AppImage"
# =========== 步骤1: 创建目录 ===========
echo -e "\n${YELLOW}[1/7] 创建目录...${NC}"
mkdir -p AppDir/usr/bin
mkdir -p AppDir/usr/lib
ORIG_NAME=$(basename "$EXECUTABLE")
# =========== 步骤2: 复制程序 ===========
echo -e "\n${YELLOW}[2/7] 复制程序...${NC}"
cp "$EXECUTABLE" "AppDir/usr/bin/$ORIG_NAME"
chmod +x "AppDir/usr/bin/$ORIG_NAME"
echo -e "${GREEN}✓ 程序: $ORIG_NAME${NC}"
# =========== 步骤3: 收集依赖 ===========
collect_all_dependencies "$EXECUTABLE" "AppDir/usr/lib"
# =========== 步骤4: 设置QEMU目录结构 ===========
setup_qemu_structure "AppDir"
# =========== 步骤5: 处理数据文件 ===========
if $INCLUDE_DATA; then
echo -e "\n${YELLOW}[5/7] 处理数据文件...${NC}"
find_and_copy_data "$EXECUTABLE" "$APP_NAME" "AppDir/usr/share/$APP_NAME"
fi
# =========== 步骤6: 创建图标 ===========
echo -e "\n${YELLOW}[6/7] 创建图标...${NC}"
create_icon "AppDir/$APP_NAME.png"
cp "AppDir/$APP_NAME.png" "AppDir/.DirIcon"
echo -e "${GREEN}✓ 图标已创建${NC}"
# =========== 步骤7: 创建二进制 AppRun ===========
echo -e "\n${YELLOW}[7/7] 创建二进制AppRun...${NC}"
# 首先尝试创建C语言版本
#use_busybox_as_apprun "AppDir" "$APP_NAME" "$ORIG_NAME"
create_binary_apprun "AppDir" "$APP_NAME" "$ORIG_NAME"
# 如果失败,使用备选方案
if [[ ! -f "AppDir/AppRun" ]] || [[ ! -x "AppDir/AppRun" ]]; then
echo "二进制创建失败,使用脚本版本..."
cat > "AppDir/AppRun" <<'EOF'
#!/bin/sh
# 兼容性脚本(当二进制不可用时)
HERE=$(dirname "$(readlink -f "$0")")
APP_NAME=$(basename "$0" .AppImage)
# 对于QEMU:直接运行内部程序
if [ "$(uname -m)" != "x86_64" ]; then
# ARM系统,通过QEMU运行
exec qemu-x86_64-static -L "$HERE" "$HERE/usr/bin/$APP_NAME" "$@"
else
# x86系统,直接运行
exec "$HERE/usr/bin/$APP_NAME" "$@"
fi
EOF
chmod +x AppDir/AppRun
echo -e "${YELLOW}⚠ 使用脚本版AppRun(QEMU兼容性可能有限)${NC}"
fi
# =========== 步骤8: 创建桌面文件 ===========
echo -e "\n${YELLOW}[8/7] 创建桌面文件...${NC}"
mkdir -p AppDir/usr/share/applications
cat > "AppDir/usr/share/applications/$APP_NAME.desktop" <<EOF
[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME
Comment=Packaged as AppImage (QEMU兼容)
Exec=$ORIG_NAME
Icon=$APP_NAME
Categories=Utility;
Terminal=true
StartupNotify=false
EOF
ln -sf usr/share/applications/$APP_NAME.desktop AppDir/
echo -e "${GREEN}✓ 桌面文件已创建${NC}"
# =========== 步骤9: 打包 ===========
echo -e "\n${YELLOW}[9/7] 打包...${NC}"
OUTPUT_FILE="${APP_NAME}.AppImage"
# 架构检测
if file -b "$EXECUTABLE" | grep -q "32-bit"; then
ARCH="i386"
else
ARCH="x86_64"
fi
export ARCH
echo "输出: $OUTPUT_FILE"
echo "架构: $ARCH"
# 打包
if appimagetool --no-appstream AppDir "$OUTPUT_FILE" 2>&1 | grep -v "gpg2" | grep -v "Warning"; then
echo -e "${GREEN}✅ 打包成功!${NC}"
else
appimagetool --no-appstream --no-fuse AppDir "$OUTPUT_FILE" 2>&1 | grep -v "gpg2" | grep -v "Warning"
echo -e "${GREEN}✅ 打包成功!${NC}"
fi
# =========== 测试 ===========
echo -e "\n${BLUE}════════════════════════════════════════${NC}"
echo -e "${GREEN}测试运行...${NC}"
chmod +x "$OUTPUT_FILE"
# 创建测试数据目录
TEST_DIR="$HOME/.local/share/$APP_NAME-test"
rm -rf "$TEST_DIR"
mkdir -p "$TEST_DIR"/{config,data,logs,cache}
# 复制可能的配置文件
if [ -d "AppDir/usr/share/$APP_NAME" ]; then
cp -r AppDir/usr/share/$APP_NAME/* "$TEST_DIR/config/" 2>/dev/null || true
fi
echo "测试命令:"
echo " 原生运行: ./\"$OUTPUT_FILE\" --help"
echo " QEMU运行: qemu-x86_64-static -L . ./\"$OUTPUT_FILE\" --help"
echo " 调试模式: DEBUG=1 ./\"$OUTPUT_FILE\" --help"
echo "----------------------------------------"
# 原生测试运行
export DEBUG=1
if timeout 5s ./"$OUTPUT_FILE" --help 2>&1 | head -20; then
echo -e "${GREEN}✅ 原生运行正常${NC}"
elif timeout 5s ./"$OUTPUT_FILE" -h 2>&1 | head -20; then
echo -e "${GREEN}✅ 原生运行正常${NC}"
else
echo -e "${YELLOW}⚠ 原生运行可能需要特定参数${NC}"
fi
unset DEBUG
# QEMU兼容性测试
echo -e "\n${YELLOW}QEMU兼容性测试...${NC}"
if command -v qemu-x86_64-static >/dev/null 2>&1; then
echo "测试命令: qemu-x86_64-static -L . ./\"$OUTPUT_FILE\" --version"
if timeout 5s qemu-x86_64-static -L . ./"$OUTPUT_FILE" --version 2>&1 | head -5; then
echo -e "${GREEN}✅ QEMU运行成功${NC}"
elif timeout 5s qemu-x86_64-static -L . ./"$OUTPUT_FILE" -v 2>&1 | head -5; then
echo -e "${GREEN}✅ QEMU运行成功${NC}"
elif timeout 5s qemu-x86_64-static -L . ./"$OUTPUT_FILE" 2>&1 | head -5; then
echo -e "${GREEN}✅ QEMU运行成功${NC}"
else
echo -e "${YELLOW}⚠ QEMU测试需要特定参数${NC}"
echo "尝试: qemu-x86_64-static -L . ./\"$OUTPUT_FILE\" --help"
fi
else
echo "qemu-x86_64-static 未安装,跳过QEMU测试"
echo "安装命令: sudo apt install qemu-user-static"
fi
# 最终信息
echo -e "\n${BLUE}════════════════════════════════════════${NC}"
echo -e "${GREEN} 完成! ${NC}"
echo -e "${BLUE}════════════════════════════════════════${NC}"
echo ""
echo "📦 输出文件: $(realpath "$OUTPUT_FILE")"
echo "📏 大小: $(du -h "$OUTPUT_FILE" | cut -f1)"
echo ""
echo "🚀 使用方法:"
echo " 在x86_64系统: ./\"$OUTPUT_FILE\""
echo " 在ARM系统: qemu-x86_64-static -L . ./\"$OUTPUT_FILE\""
echo ""
echo "📁 用户数据目录:"
echo " $HOME/.local/share/$APP_NAME/"
echo ""
echo "🔧 调试模式:"
echo " DEBUG=1 ./\"$OUTPUT_FILE\" [参数]"
echo ""
echo "🔄 QEMU运行助手脚本:"
cat > "$(dirname "$OUTPUT_FILE")/run-with-qemu.sh" <<'EOF2'
#!/bin/bash
# QEMU运行助手
APP="$1"
shift
qemu-x86_64-static -L . "$APP" "$@"
EOF2
chmod +x "$(dirname "$OUTPUT_FILE")/run-with-qemu.sh"
echo " 已创建: $(dirname "$OUTPUT_FILE")/run-with-qemu.sh"
echo " 使用: ./run-with-qemu.sh \"$OUTPUT_FILE\" [参数]"
# 清理
rm -rf AppDir
}
# 运行
main "$@"
Binary file not shown.
Binary file not shown.
+96
View File
@@ -0,0 +1,96 @@
#!/bin/bash
# qemu_run_proper.sh
APPIMAGE="$1"
shift
if [[ ! -f "$APPIMAGE" ]]; then
echo "错误: 文件不存在: $APPIMAGE" >&2
exit 1
fi
echo "=== 运行AppImage ==="
echo "文件: $(basename "$APPIMAGE")"
# 创建临时目录
TEMP_DIR=$(mktemp -d)
cp ${APPIMAGE} ${TEMP_DIR}
cd "$TEMP_DIR"
# 提取AppImage
echo "提取AppImage..."
qemu-x86_64 "$APPIMAGE" --appimage-extract 2>/dev/null || {
echo "提取失败" >&2
exit 1
}
cd squashfs-root
HERE="$PWD"
echo "提取目录: $HERE"
# 查找可执行文件(不输出额外信息)
find_executable() {
local dir="$1"
local app_name=$(basename "$APPIMAGE" .AppImage)
# 1. 尝试与AppImage同名的程序
if [[ -x "$dir/usr/bin/$app_name" ]]; then
echo "$dir/usr/bin/$app_name"
return 0
fi
# 2. 查找usr/bin下的第一个可执行文件
if [[ -d "$dir/usr/bin" ]]; then
for prog in "$dir/usr/bin"/*; do
if [[ -x "$prog" ]]; then
echo "$prog"
return 0
fi
done
fi
# 3. 查找AppRun
if [[ -x "$dir/AppRun" ]]; then
echo "$dir/AppRun"
return 0
fi
return 1
}
# 静默查找可执行文件
EXECUTABLE=$(find_executable "$HERE" 2>/dev/null)
if [[ -z "$EXECUTABLE" ]] || [[ ! -f "$EXECUTABLE" ]]; then
echo "错误: 未找到可执行文件" >&2
echo "目录内容:" >&2
ls -la "$HERE" >&2
if [[ -d "$HERE/usr/bin" ]]; then
echo "usr/bin内容:" >&2
ls -la "$HERE/usr/bin" >&2
fi
exit 1
fi
echo "可执行文件: $EXECUTABLE"
echo "文件类型: $(file -b "$EXECUTABLE" 2>/dev/null || echo "无法识别")"
# 运行程序
ARCH=$(uname -m)
if [[ "$ARCH" == "x86_64" ]]; then
echo "原生x86_64系统,直接运行..."
exec "$EXECUTABLE" "$@"
else
echo "ARM系统 ($ARCH),使用QEMU..."
# 确保lib64目录
if [[ ! -d "$HERE/lib64" ]] && [[ -f "$HERE/usr/lib/ld-linux-x86-64.so.2" ]]; then
mkdir -p "$HERE/lib64"
cp "$HERE/usr/lib/ld-linux-x86-64.so.2" "$HERE/lib64/" 2>/dev/null
fi
# 直接使用QEMU运行
exec qemu-x86_64 -L "$HERE" "$EXECUTABLE" "$@"
fi