diff --git a/CMakeLists.txt b/CMakeLists.txt index b47a11e..87748c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,8 @@ find_package( Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network Concurrent PrintSupport Xml LinguistTools) +install(CODE "set(CMAKE_INSTALL_LOCAL_ONLY TRUE)" ALL_COMPONENTS) + option(BUILD_TEST_PLUGIN OFF) if(BUILD_TEST_PLUGIN) add_subdirectory(TestPlugin) @@ -534,3 +536,20 @@ set_target_properties( if(QT_VERSION_MAJOR EQUAL 6) qt_finalize_executable(WingHexExplorer2) endif() + +if(${QT_VERSION_MAJOR} EQUAL 6) + if(Qt6Widgets_VERSION VERSION_GREATER_EQUAL 6.5.0) + install( + TARGETS WingHexExplorer2 + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}) + + # Generate the deployment script. + qt6_generate_deploy_app_script( + TARGET WingHexExplorer2 OUTPUT_SCRIPT deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR) + + # Call the deployment script on "cmake --install". + install(SCRIPT ${deploy_script}) + endif() +endif() diff --git a/mkinstaller/deb/com.wingsummer.winghexexplorer2.desktop b/mkinstaller/deb/com.wingsummer.winghexexplorer2.desktop index 4ac3698..a9c809e 100755 --- a/mkinstaller/deb/com.wingsummer.winghexexplorer2.desktop +++ b/mkinstaller/deb/com.wingsummer.winghexexplorer2.desktop @@ -8,7 +8,7 @@ Type=Application StartupNotify=false Terminal=false Icon=/opt/WingHexExplorer2/share/icon.png -StartupWMClass=WingHexExplorer +StartupWMClass=WingHexExplorer2 Comment=a free and powerful opensource hexeditor based on QT Comment[zh_CN]=一个基于 QT 的自由和强大的开源十六进制编辑器 MimeType=application/x-winghex;text/plain;application/octet-stream; diff --git a/mkinstaller/linuxdeploy/README.md b/mkinstaller/linuxdeploy/README.md new file mode 100644 index 0000000..609cdc5 --- /dev/null +++ b/mkinstaller/linuxdeploy/README.md @@ -0,0 +1,15 @@ +## 文件说明 + +该目录的工具用于打包 Linux 通用安装包,方便没有编译环境的用户使用该软件,与此同时可以保证较好的通用性。 + +该工具包打包好的程序已经在 DeepinV23 、KDE Neon 测试过,未发现有啥问题。 + +如果你要尝试手动打包,首先需要配置好 CMake 的`CMAKE_PREFIX_INSTALL`到你要打包的目录,注意启用`install`,然后编译,完毕后会安装到打包目录中。 + +然后首先部署(deploy)一下,执行`deploy.py`这个脚本,要把你 **编译目录** 作为参数,注意是你编译的目录,而不是打包目录。该脚本会自行解析并定位到你的打包目录的。如果正常运行的话,你会得到如下输出: + +![screenshot](deployshot.png) + +这个警告尤其注意,它在你打包目录的`lib`目录下,你必须将警告中的文件加上可执行权限,否则部署之后的程序因该文件没有可执行权限导致无法执行。 **为啥不放到自动化脚本是因为修改权限需要 root,除非你用桌面的文件属性设置。** + +完成部署之后,你就可以打包了,执行`build.sh`这个脚本,要把你 **打包的目录** 作为参数,执行完之后,它会在打包工具目录下创建`build`目录生成一个`WingHexExplorer2-installer.run`文件和一个`payload.tar.gz`文件,前者是安装包,后者是生成安装包中间产物,是对你打包目录进行压缩的一个压缩包。 diff --git a/mkinstaller/linuxdeploy/WingHexExplorer2.sh b/mkinstaller/linuxdeploy/WingHexExplorer2.sh new file mode 100755 index 0000000..b33a291 --- /dev/null +++ b/mkinstaller/linuxdeploy/WingHexExplorer2.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +SCRIPT_DIR="/opt/WingHexExplorer2" +cd "$SCRIPT_DIR" || exit 1 +env LD_LIBRARY_PATH="$SCRIPT_DIR/lib" "$SCRIPT_DIR/WingHexExplorer2" "$@" diff --git a/mkinstaller/linuxdeploy/build.sh b/mkinstaller/linuxdeploy/build.sh new file mode 100755 index 0000000..29ee33e --- /dev/null +++ b/mkinstaller/linuxdeploy/build.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +F_RED="\e[31m" +F_GREEN="\e[32m" +A_DEFAULT="\033[0m" + +SCRIPT_DIR=$(dirname "$(realpath "$0")") +cd "$SCRIPT_DIR" || exit + +if [ "$#" -ne 1 ]; then + echo "$F_GREEN Usage: $0 $A_DEFAULT" + exit 1 +fi +if [ ! -d "$1" ]; then + echo -e "$F_RED Not exists: $1$A_DEFAULT" +fi +if [ ! -d build ]; then + mkdir build +fi + +cd build || exit 1 + +set -e + +fakeroot tar czvf payload.tar.gz -C "$1" . + +arch=$(uname -m) + +PACKAGE_NAME="WingHexExplorer2-$arch-installer.run" + +cat "$SCRIPT_DIR/installheader.sh" payload.tar.gz > "$PACKAGE_NAME" + +echo -e "$F_GREEN>> $PACKAGE_NAME was created under build.$A_DEFAULT" + +exit 0 diff --git a/mkinstaller/linuxdeploy/deploy.py b/mkinstaller/linuxdeploy/deploy.py new file mode 100755 index 0000000..afe8d18 --- /dev/null +++ b/mkinstaller/linuxdeploy/deploy.py @@ -0,0 +1,216 @@ +#! /usr/bin/env python3 +# -*- coding:utf-8 -*- +# @Time : 2024/12/15 +# @Author : 寂静的羽夏(wingsummer) +# @FileName: deploy.py + +import argparse +import os +import shutil +import hashlib +import subprocess + +from colorama import Fore, Style + +PACKAGE_NAME = "WingHexExplorer2" + + +def run_command_interactive(command): + """ + Run a command interactively, printing its stdout in real-time. + + :param command: List of command arguments (e.g., ["your_command", "arg1", "arg2"]) + :return: The return code of the command + """ + process = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, # Capture both stdout and stderr + text=True # Ensure the output is in text mode + ) + + while True: + output = process.stdout.readline() + if output == "" and process.poll() is not None: + break + if output: + print(Fore.GREEN + output.strip() + Style.RESET_ALL) + + return_code = process.wait() + return return_code + + +def create_dir(dir): + if not os.path.exists(dir): + os.mkdir(dir) + + +def remove(path): + """ param could either be relative or absolute. """ + if os.path.isfile(path) or os.path.islink(path): + os.remove(path) # remove the file + elif os.path.isdir(path): + shutil.rmtree(path) # remove dir and all contains + + +def is_empty(path): + if os.path.exists(path) and not os.path.isfile(path): + # Checking if the directory is empty or not + if not os.listdir(path): + return True + else: + return False + else: + return False + + +def main(): + parser = argparse.ArgumentParser( + prog="deploy.py", description=f"A deploying tool for {PACKAGE_NAME}") + parser.add_argument( + "folder", help="A folder that has contained the binary build") + args = parser.parse_args() + + # start parsing build directory + build_path = os.path.abspath(args.folder) + + installer_path = os.path.dirname(os.path.abspath(__file__)) + projectbase = os.path.abspath(os.path.join(installer_path, "../..")) + cmake_cache = os.path.join(build_path, "CMakeCache.txt") + + if (os.path.exists(build_path) == False): + print( + Fore.RED + "[Error] Not found a CMake build directory!" + Style.RESET_ALL) + exit(-1) + + if (os.path.exists(cmake_cache) == False): + print( + Fore.RED + "[Error] This is not a CMake build directory!" + Style.RESET_ALL) + exit(-1) + + installer_path_exec = "" + + with open(cmake_cache, 'r') as cmake_config: + while (True): + line = cmake_config.readline() + if not line: + break + if (line.startswith("CMAKE_INSTALL_PREFIX:PATH")): + set = line.split('=', 1) + installer_path_exec = set[1].strip('\n') + pass + + print(Fore.GREEN + ">> Checking patchelf..." + Style.RESET_ALL) + + ret = run_command_interactive(["patchelf", "--version"]) + if (ret != 0): + print( + Fore.RED + "[Error] patchelf is needed for deploying!" + Style.RESET_ALL) + exit(-2) + + print(Fore.GREEN + ">> Checking file integrity..." + Style.RESET_ALL) + + version_file_src = os.path.join(build_path, "WINGHEX_VERSION") + if (os.path.exists(version_file_src) == False): + print( + Fore.RED + "[Error] WINGHEX_VERSION file not found, maybe not a CMake build directory!" + Style.RESET_ALL) + exit(-1) + + version_qt_src = os.path.join(build_path, "QT_VERSION") + if (os.path.exists(version_qt_src) == False): + print( + Fore.RED + "[Error] QT_VERSION file not found, maybe not a CMake build directory!" + Style.RESET_ALL) + exit(-1) + + # check wether it has been installed + if (os.path.exists(installer_path_exec) == False): + print( + Fore.RED + "[Error] Installing directory not exists!" + Style.RESET_ALL) + exit(-1) + + install_content = ["bin", "lib", "plugins", + "translations", PACKAGE_NAME] + for item in install_content: + if (os.path.exists(os.path.join(installer_path_exec, item)) == False): + print( + Fore.RED + "[Error] Installing contents have been damaged!" + Style.RESET_ALL) + exit(-1) + + # ok, start deploying + remove(os.path.join(installer_path_exec, "bin")) + + exemain_src = os.path.join(installer_path_exec, PACKAGE_NAME) + print(Fore.GREEN + ">> Deploying..." + Style.RESET_ALL) + + create_dir(os.path.join(installer_path_exec, "plugin")) + create_dir(os.path.join(installer_path_exec, "scripts")) + create_dir(os.path.join(installer_path_exec, "aslib")) + + shutil.copytree(os.path.join(installer_path, "share"), + os.path.join(installer_path_exec, "share"), dirs_exist_ok=True) + + shutil.copytree(os.path.join(build_path, "lang"), + os.path.join(installer_path_exec, "lang"), dirs_exist_ok=True) + + # copying deployment files + deploy_files = ["qt.conf", "uninstall.sh", + "purge.sh", f"{PACKAGE_NAME}.sh"] + for item in deploy_files: + shutil.copy2(os.path.join(installer_path, item), + os.path.join(installer_path_exec, item)) + + # finally, copy other files + print(Fore.GREEN + ">> Copying License and other materials..." + Style.RESET_ALL) + + material_files = ["LICENSE", "authorband.svg", + "licenseband.svg", "screenshot.png", "README.md"] + + for f in material_files: + shutil.copyfile(os.path.join(projectbase, f), + os.path.join(installer_path_exec, f)) + + shutil.copyfile(os.path.join(projectbase, "images", "author.jpg"), + os.path.join(installer_path_exec, "author.jpg")) + + # in the end, start patching + ld_execs = [filename for filename in os.listdir(os.path.join( + installer_path_exec, "lib")) if filename.startswith("ld-linux")] + if (len(ld_execs) != 1): + print( + Fore.RED + "[Error] dynamic linker/loader can not be determined!" + Style.RESET_ALL) + exit(-3) + + ret = run_command_interactive( + ["patchelf", "--set-interpreter", f"./lib/{ld_execs[0]}", exemain_src]) + if (ret != 0): + print( + Fore.RED + "[Error] patchelf error!" + Style.RESET_ALL) + exit(-4) + + print(Fore.GREEN + ">> Calculating checksum..." + Style.RESET_ALL) + + # calculate the md5 checksum + with open(exemain_src, 'rb') as file_to_check: + data = file_to_check.read() + md5_returned = hashlib.md5(data).hexdigest().upper() + + print(Fore.GREEN + ">> Get MD5: " + md5_returned + Style.RESET_ALL) + + with open(os.path.join(installer_path_exec, "md5sums"), 'w') as md5_file: + md5_file.write(md5_returned) + + print(Fore.GREEN + ">> Deployment finished..." + Style.RESET_ALL) + + ld_path = os.path.join(installer_path_exec, "lib", ld_execs[0]) + if (os.access(ld_path, os.X_OK) == False): + print(Fore.YELLOW + f"[Warn] { + ld_execs[0]} has no executable permission! You should set it for running a deployed program!" + Style.RESET_ALL) + + exit(0) + + +if __name__ == "__main__": + main() +else: + print( + Fore.RED + "[Error] Please run this script in main mode" + Style.RESET_ALL) diff --git a/mkinstaller/linuxdeploy/deployshot.png b/mkinstaller/linuxdeploy/deployshot.png new file mode 100644 index 0000000..200d08b Binary files /dev/null and b/mkinstaller/linuxdeploy/deployshot.png differ diff --git a/mkinstaller/linuxdeploy/installheader.sh b/mkinstaller/linuxdeploy/installheader.sh new file mode 100644 index 0000000..e93d118 --- /dev/null +++ b/mkinstaller/linuxdeploy/installheader.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +F_RED="\e[31m" +F_GREEN="\e[32m" +A_DEFAULT="\033[0m" + +set -e + +echo -e "$F_GREEN" +echo "======================================================" +echo " A Self Extracting Installer for WingHexExplorer2 " +echo " License: AGPL-3.0" +echo " Author: Wingsummer" +echo "======================================================" +echo -e "$A_DEFAULT" + +if [ "$(id -u)" -ne 0 ]; then + echo -e "$F_RED Please run this script as root or using sudo! $A_DEFAULT" + exit 1 +fi + +# don't try to modified this path +INSTALL_PATH="/opt/WingHexExplorer2" + +if [ -d "$INSTALL_PATH" ]; then + rm -rf "$INSTALL_PATH" +fi + +mkdir -p "$INSTALL_PATH" + +ARCHIVE=$(awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' "$0") + +tail -n+"$ARCHIVE" "$0" | tar xzv -C "$INSTALL_PATH" + +echo -e "$F_GREEN" +echo ">> Registering componments of WingHexExplorer2!" +echo -e "$A_DEFAULT" + +mv $INSTALL_PATH/share/com.wingsummer.winghexexplorer2.desktop /usr/share/applications/com.wingsummer.winghexexplorer2.desktop + +xdg-mime install $INSTALL_PATH/share/x-winghex.xml +xdg-mime default /usr/share/applications/com.wingsummer.winghexexplorer2.desktop application/x-winghex + +xdg-icon-resource install --context mimetypes --size 32 $INSTALL_PATH/share/winghexpro32.png application-x-winghex +xdg-icon-resource install --context mimetypes --size 64 $INSTALL_PATH/share/winghexpro64.png application-x-winghex +xdg-icon-resource install --context mimetypes --size 128 $INSTALL_PATH/share/winghexpro128.png application-x-winghex +update-mime-database /usr/share/mime +xdg-icon-resource forceupdate +gtk-update-icon-cache /usr/share/icons/hicolor +update-desktop-database /usr/share/applications + +echo -e "$F_GREEN" +echo ">> WingHexExplorer2 is installed on your computer!" +echo -e "$A_DEFAULT" +exit 0 + +__ARCHIVE_BELOW__ diff --git a/mkinstaller/linuxdeploy/purge.sh b/mkinstaller/linuxdeploy/purge.sh new file mode 100755 index 0000000..484d6a6 --- /dev/null +++ b/mkinstaller/linuxdeploy/purge.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash + +F_RED="\e[31m" +F_GREEN="\e[32m" +A_DEFAULT="\033[0m" + +CFG_PATH=".config/WingCloudStudio" +AUTO_START=".config/autostart" +CONF_FILE="WingHexExplorer2.conf" +CONTENT_PATH=".local/share/WingCloudStudio" +DESKTOP_FILE_NAME="com.wingsummer.winghexexplorer2.desktop" +PACKAGE_NAME="WingHexExplorer2" + +# Function to check if a directory is empty +is_empty() { + local dir="$1" + if [ -d "$dir" ] && [ -z "$(ls -A "$dir")" ]; then + return 0 # True (empty) + else + return 1 # False (not empty) + fi +} + +# Function to remove a file or directory safely +remove() { + local target="$1" + if [ -e "$target" ]; then + rm -rf "$target" + echo -e "$F_GREEN >> Removed: $target$A_DEFAULT" + fi +} + +# Function to clear user data +clear_usrdata() { + local usr="$1" + local usr_path + + if [ "$usr" = "root" ]; then + usr_path="/root" + else + usr_path="/home/$usr" + fi + + local desktop_file="${usr_path}/${AUTO_START}/${DESKTOP_FILE_NAME}" + remove "$desktop_file" + + local cfgpath="${usr_path}/${CFG_PATH}" + remove "${cfgpath}/${CONF_FILE}" + if is_empty "$cfgpath"; then + remove "$cfgpath" + fi + + cfgpath="${usr_path}/${CONTENT_PATH}/${PACKAGE_NAME}" + remove "$cfgpath" + + cfgpath="${usr_path}/${CONTENT_PATH}" + if is_empty "$cfgpath"; then + remove "$cfgpath" + fi + + echo -e "$F_GREEN >> Purged $usr...$A_DEFAULT" +} + +# Function to purge all data +purge() { + # Loop through user directories + for p in /home/*; do + local usr + usr=$(basename "$p") + if [ "$usr" = "lost+found" ]; then + continue + fi + clear_usrdata "$usr" + done + + # Handle root user + clear_usrdata "root" + echo -e "$F_GREEN >> Clean-up finished...$A_DEFAULT" +} + +if [ "$(id -u)" -ne 0 ]; then + echo -e "$F_RED Please run this script as root or using sudo! $A_DEFAULT" + exit 1 +fi + +read -r -p "Are you sure you want to purge all contents? (y/N): " CONFIRM + +if [[ "$CONFIRM" =~ ^[Yy]$ ]]; then + xdg-mime uninstall /opt/WingHexExplorer2/share/x-winghex.xml + xdg-icon-resource uninstall --context mimetypes --size 32 application-x-winghex + xdg-icon-resource uninstall --context mimetypes --size 64 application-x-winghex + xdg-icon-resource uninstall --context mimetypes --size 128 application-x-winghex + + update-mime-database /usr/share/mime + xdg-icon-resource forceupdate + update-icon-caches /usr/share/icons/hicolor + + # remove desktop-icon + update-desktop-database /usr/share/applications + + # remove all contents + rm /usr/share/applications/com.wingsummer.winghexexplorer2.desktop + purge + rm -r /opt/WingHexExplorer2 + + echo -e "$F_GREEN Uninstallation is finished... $A_DEFAULT" +else + echo -e "$F_GREEN Uninstallation canceled. $A_DEFAULT" +fi diff --git a/mkinstaller/linuxdeploy/qt.conf b/mkinstaller/linuxdeploy/qt.conf new file mode 100644 index 0000000..b503b87 --- /dev/null +++ b/mkinstaller/linuxdeploy/qt.conf @@ -0,0 +1,3 @@ +[Paths] +Plugins= plugins +Libraries= lib diff --git a/mkinstaller/linuxdeploy/share/com.wingsummer.winghexexplorer2.desktop b/mkinstaller/linuxdeploy/share/com.wingsummer.winghexexplorer2.desktop new file mode 100755 index 0000000..4a0b6c3 --- /dev/null +++ b/mkinstaller/linuxdeploy/share/com.wingsummer.winghexexplorer2.desktop @@ -0,0 +1,14 @@ +[Desktop Entry] +Name=WingHexExplorer2 +Name[zh_CN]=羽云十六进制编辑器 +Categories=Utility; +Exec=/opt/WingHexExplorer2/WingHexExplorer2.sh %F +Encoding=UTF-8 +Type=Application +StartupNotify=false +Terminal=false +Icon=/opt/WingHexExplorer2/share/icon.png +StartupWMClass=WingHexExplorer2 +Comment=a free and powerful opensource hexeditor based on QT +Comment[zh_CN]=一个基于 QT 的自由和强大的开源十六进制编辑器 +MimeType=application/x-winghex;text/plain;application/octet-stream; diff --git a/mkinstaller/linuxdeploy/share/icon.png b/mkinstaller/linuxdeploy/share/icon.png new file mode 100644 index 0000000..7c56692 Binary files /dev/null and b/mkinstaller/linuxdeploy/share/icon.png differ diff --git a/mkinstaller/linuxdeploy/share/winghexpro128.png b/mkinstaller/linuxdeploy/share/winghexpro128.png new file mode 100644 index 0000000..86ad037 Binary files /dev/null and b/mkinstaller/linuxdeploy/share/winghexpro128.png differ diff --git a/mkinstaller/linuxdeploy/share/winghexpro32.png b/mkinstaller/linuxdeploy/share/winghexpro32.png new file mode 100644 index 0000000..802d36e Binary files /dev/null and b/mkinstaller/linuxdeploy/share/winghexpro32.png differ diff --git a/mkinstaller/linuxdeploy/share/winghexpro64.png b/mkinstaller/linuxdeploy/share/winghexpro64.png new file mode 100644 index 0000000..f25ebdd Binary files /dev/null and b/mkinstaller/linuxdeploy/share/winghexpro64.png differ diff --git a/mkinstaller/linuxdeploy/share/x-winghex.xml b/mkinstaller/linuxdeploy/share/x-winghex.xml new file mode 100644 index 0000000..2a9b004 --- /dev/null +++ b/mkinstaller/linuxdeploy/share/x-winghex.xml @@ -0,0 +1,9 @@ + + + + + + WingHexExplorer WorkSpace + 羽云十六进制编辑器项目文件 + + \ No newline at end of file diff --git a/mkinstaller/linuxdeploy/uninstall.sh b/mkinstaller/linuxdeploy/uninstall.sh new file mode 100755 index 0000000..bcef5a0 --- /dev/null +++ b/mkinstaller/linuxdeploy/uninstall.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +F_RED="\e[31m" +F_GREEN="\e[32m" +A_DEFAULT="\033[0m" + +if [ "$(id -u)" -ne 0 ]; then + echo -e "$F_RED Please run this script as root or using sudo! $A_DEFAULT" + exit 1 +fi + +read -r -p "Are you sure you want to uninstall and delete all contents? (y/N): " CONFIRM + +if [[ "$CONFIRM" =~ ^[Yy]$ ]]; then + xdg-mime uninstall /opt/WingHexExplorer2/share/x-winghex.xml + xdg-icon-resource uninstall --context mimetypes --size 32 application-x-winghex + xdg-icon-resource uninstall --context mimetypes --size 64 application-x-winghex + xdg-icon-resource uninstall --context mimetypes --size 128 application-x-winghex + + update-mime-database /usr/share/mime + xdg-icon-resource forceupdate + update-icon-caches /usr/share/icons/hicolor + + # remove desktop-icon + update-desktop-database /usr/share/applications + + # remove all contents + rm /usr/share/applications/com.wingsummer.winghexexplorer2.desktop + rm -r /opt/WingHexExplorer2 + + echo -e "$F_GREEN Uninstallation is finished... $A_DEFAULT" +else + echo -e "$F_GREEN Uninstallation canceled. $A_DEFAULT" +fi diff --git a/mkinstaller/pyscript/com.wingsummer.winghexexplorer2.desktop b/mkinstaller/pyscript/com.wingsummer.winghexexplorer2.desktop index 4ac3698..a9c809e 100755 --- a/mkinstaller/pyscript/com.wingsummer.winghexexplorer2.desktop +++ b/mkinstaller/pyscript/com.wingsummer.winghexexplorer2.desktop @@ -8,7 +8,7 @@ Type=Application StartupNotify=false Terminal=false Icon=/opt/WingHexExplorer2/share/icon.png -StartupWMClass=WingHexExplorer +StartupWMClass=WingHexExplorer2 Comment=a free and powerful opensource hexeditor based on QT Comment[zh_CN]=一个基于 QT 的自由和强大的开源十六进制编辑器 MimeType=application/x-winghex;text/plain;application/octet-stream;