From c33a27b2ea5aadfae0905e14b96d83700b19598a Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Wed, 9 Mar 2022 18:47:31 -0500 Subject: [PATCH] AppImage! --- .gitignore | 3 + CMakeLists.txt | 4 + Dockerfile | 23 ++- ...6-toolchain.cmake => i386-toolchain.cmake} | 0 debian/client-amd64 | 7 - debian/client-arm64 | 7 - debian/client-armhf | 7 - debian/server-amd64 | 7 - debian/server-arm64 | 7 - debian/server-armhf | 7 - ...p => com.thebrokenrail.MCPIReborn.desktop} | 2 +- ...t.png => com.thebrokenrail.MCPIReborn.png} | Bin launcher/src/bootstrap.c | 137 +++++++++---- launcher/src/ldconfig.cpp | 5 +- libreborn/CMakeLists.txt | 2 +- libreborn/include/libreborn/elf.h | 2 +- libreborn/include/libreborn/exec.h | 26 ++- libreborn/src/{reborn.c => patch.c} | 0 mods/src/chat/ui.c | 2 +- mods/src/home/home.c | 3 +- mods/src/override/override.c | 5 +- mods/src/sound/sound.cpp | 6 +- scripts/build-all.sh | 14 -- scripts/ci/run.sh | 10 +- scripts/generate-appimage-builder-yaml.js | 185 ++++++++++++++++++ scripts/install-dependencies.sh | 37 +++- scripts/package-all.sh | 14 ++ scripts/package.sh | 36 +--- scripts/test.sh | 4 + 29 files changed, 404 insertions(+), 158 deletions(-) rename cmake/{i686-toolchain.cmake => i386-toolchain.cmake} (100%) delete mode 100644 debian/client-amd64 delete mode 100644 debian/client-arm64 delete mode 100644 debian/client-armhf delete mode 100644 debian/server-amd64 delete mode 100644 debian/server-arm64 delete mode 100644 debian/server-armhf rename launcher/client-data/share/applications/{minecraft-pi-reborn-client.desktop => com.thebrokenrail.MCPIReborn.desktop} (87%) rename launcher/client-data/share/icons/hicolor/scalable/apps/{minecraft-pi-reborn-client.png => com.thebrokenrail.MCPIReborn.png} (100%) rename libreborn/src/{reborn.c => patch.c} (100%) delete mode 100755 scripts/build-all.sh create mode 100755 scripts/generate-appimage-builder-yaml.js create mode 100755 scripts/package-all.sh diff --git a/.gitignore b/.gitignore index 58bbd3351..bc57d619e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ debian/tmp build CMakeLists.txt.user *.autosave +AppImageBuilder.yml +appimage-builder-cache +AppDir diff --git a/CMakeLists.txt b/CMakeLists.txt index da53fd719..5dec89349 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ option(MCPI_HEADLESS_MODE "Headless Mode" ${MCPI_SERVER_MODE}) set(MCPI_BUILD_MODE "both" CACHE STRING "\"arm\" = Build Only Code That Must Be ARM; \"native\" = Build Architecture-Independent Code; \"both\" = Build All Code As ARM") set_property(CACHE MCPI_BUILD_MODE PROPERTY STRINGS "both" "arm" "native") option(MCPI_OPEN_SOURCE_ONLY "Only Install Open-Source Code (Will Result In Broken Install)" FALSE) +option(MCPI_IS_APPIMAGE_BUILD "AppImage Build" FALSE) # Configure Build Mode if(MCPI_BUILD_MODE STREQUAL "arm") @@ -95,6 +96,9 @@ endif() if(MCPI_HEADLESS_MODE) add_definitions(-DMCPI_HEADLESS_MODE) endif() +if(MCPI_IS_APPIMAGE_BUILD) + add_definitions(-DMCPI_IS_APPIMAGE_BUILD) +endif() # Version file(STRINGS VERSION VERSION) diff --git a/Dockerfile b/Dockerfile index 96baa5f73..12d5a5a5d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,32 @@ FROM debian:bullseye-slim -# Copy DEB -ADD ./out/minecraft-pi-reborn-server_*_amd64.deb /root - # Install RUN \ apt-get update && \ - apt-get install -y tini && \ - (dpkg -i /root/*.deb || :) && \ + apt-get install -y tini sed && \ apt-get --fix-broken install -y && \ rm -f /root/*.deb && \ rm -rf /var/lib/apt/lists/* +# Copy AppImage +RUN mkdir /app +ADD ./out/minecraft-pi-reborn-server-*-amd64.AppImage /app + +# Extract AppImage +WORKDIR /app +RUN \ + sed -i '0,/AI\x02/{s|AI\x02|\x00\x00\x00|}' ./*.AppImage && \ + ./*.AppImage --appimage-extract && \ + rm -f ./*.AppImage + +# Setup AppImage +ENV OWD=/data +ENV APPDIR=/app/squashfs-root + # Setup Working Directory RUN mkdir /data WORKDIR /data # Setup Entrypoint ENTRYPOINT ["/usr/bin/tini", "--"] -CMD ["minecraft-pi-reborn-server"] +CMD ["/app/squashfs-root/AppRun"] diff --git a/cmake/i686-toolchain.cmake b/cmake/i386-toolchain.cmake similarity index 100% rename from cmake/i686-toolchain.cmake rename to cmake/i386-toolchain.cmake diff --git a/debian/client-amd64 b/debian/client-amd64 deleted file mode 100644 index ae1265473..000000000 --- a/debian/client-amd64 +++ /dev/null @@ -1,7 +0,0 @@ -Package: minecraft-pi-reborn-client -Version: ${VERSION} -Maintainer: TheBrokenRail -Description: Fun with Blocks -Homepage: https://www.minecraft.net/en-us/edition/pi -Architecture: amd64 -Depends: libc6, libstdc++6, libc6-armhf-cross, libstdc++6-armhf-cross, zenity, libgles1, libegl1, libglfw3 | libglfw3-wayland, libfreeimage3, libopenal1, qemu-user diff --git a/debian/client-arm64 b/debian/client-arm64 deleted file mode 100644 index d89a362a0..000000000 --- a/debian/client-arm64 +++ /dev/null @@ -1,7 +0,0 @@ -Package: minecraft-pi-reborn-client -Version: ${VERSION} -Maintainer: TheBrokenRail -Description: Fun with Blocks -Homepage: https://www.minecraft.net/en-us/edition/pi -Architecture: arm64 -Depends: libc6, libstdc++6, libc6:armhf, libstdc++6:armhf, zenity, libgles1, libegl1, libglfw3 | libglfw3-wayland, libfreeimage3, libopenal1 diff --git a/debian/client-armhf b/debian/client-armhf deleted file mode 100644 index 7d98f9695..000000000 --- a/debian/client-armhf +++ /dev/null @@ -1,7 +0,0 @@ -Package: minecraft-pi-reborn-client -Version: ${VERSION} -Maintainer: TheBrokenRail -Description: Fun with Blocks -Homepage: https://www.minecraft.net/en-us/edition/pi -Architecture: armhf -Depends: libc6, libstdc++6, zenity, libgles1, libegl1, libglfw3 | libglfw3-wayland, libfreeimage3, libopenal1 diff --git a/debian/server-amd64 b/debian/server-amd64 deleted file mode 100644 index 4ef2278ff..000000000 --- a/debian/server-amd64 +++ /dev/null @@ -1,7 +0,0 @@ -Package: minecraft-pi-reborn-server -Version: ${VERSION} -Maintainer: TheBrokenRail -Description: Fun with Blocks -Homepage: https://www.minecraft.net/en-us/edition/pi -Architecture: amd64 -Depends: libc6, libstdc++6, libc6-armhf-cross, libstdc++6-armhf-cross, qemu-user diff --git a/debian/server-arm64 b/debian/server-arm64 deleted file mode 100644 index 5bd4d13f9..000000000 --- a/debian/server-arm64 +++ /dev/null @@ -1,7 +0,0 @@ -Package: minecraft-pi-reborn-server -Version: ${VERSION} -Maintainer: TheBrokenRail -Description: Fun with Blocks -Homepage: https://www.minecraft.net/en-us/edition/pi -Architecture: arm64 -Depends: libc6, libstdc++6, libc6:armhf, libstdc++6:armhf diff --git a/debian/server-armhf b/debian/server-armhf deleted file mode 100644 index 697b4ec37..000000000 --- a/debian/server-armhf +++ /dev/null @@ -1,7 +0,0 @@ -Package: minecraft-pi-reborn-server -Version: ${VERSION} -Maintainer: TheBrokenRail -Description: Fun with Blocks -Homepage: https://www.minecraft.net/en-us/edition/pi -Architecture: armhf -Depends: libc6, libstdc++6 diff --git a/launcher/client-data/share/applications/minecraft-pi-reborn-client.desktop b/launcher/client-data/share/applications/com.thebrokenrail.MCPIReborn.desktop similarity index 87% rename from launcher/client-data/share/applications/minecraft-pi-reborn-client.desktop rename to launcher/client-data/share/applications/com.thebrokenrail.MCPIReborn.desktop index 2a809dab2..c05013118 100755 --- a/launcher/client-data/share/applications/minecraft-pi-reborn-client.desktop +++ b/launcher/client-data/share/applications/com.thebrokenrail.MCPIReborn.desktop @@ -1,7 +1,7 @@ [Desktop Entry] Name=Minecraft: Pi Edition: Reborn Comment=Fun with Blocks -Icon=minecraft-pi-reborn-client +Icon=com.thebrokenrail.MCPIReborn StartupNotify=false StartupWMClass=Minecraft: Pi Edition: Reborn Exec=minecraft-pi-reborn-client diff --git a/launcher/client-data/share/icons/hicolor/scalable/apps/minecraft-pi-reborn-client.png b/launcher/client-data/share/icons/hicolor/scalable/apps/com.thebrokenrail.MCPIReborn.png similarity index 100% rename from launcher/client-data/share/icons/hicolor/scalable/apps/minecraft-pi-reborn-client.png rename to launcher/client-data/share/icons/hicolor/scalable/apps/com.thebrokenrail.MCPIReborn.png diff --git a/launcher/src/bootstrap.c b/launcher/src/bootstrap.c index 40a2d0ad4..5da18ff84 100644 --- a/launcher/src/bootstrap.c +++ b/launcher/src/bootstrap.c @@ -134,32 +134,60 @@ static void load(char **ld_preload, char *folder) { } } -#define MCPI_NAME "minecraft-pi" +#define MCPI_BINARY "minecraft-pi" +#define QEMU_BINARY "qemu-arm" // Bootstrap void bootstrap(int argc, char *argv[]) { INFO("%s", "Configuring Game..."); + // AppImage +#ifdef MCPI_IS_APPIMAGE_BUILD + if (chdir(getenv("OWD")) != 0) { + ERR("AppImage: Unable To Fix Current Directory: %s", strerror(errno)); + } +#endif + // Get Binary Directory char *binary_directory = get_binary_directory(); + // Handle AppImage +#ifdef MCPI_IS_APPIMAGE_BUILD + char *usr_prefix = getenv("APPDIR"); +#else + char *usr_prefix = ""; +#endif + // Configure LD_LIBRARY_PATH { + // Preserve PRESERVE_ENVIRONMENTAL_VARIABLE("LD_LIBRARY_PATH"); + char *new_ld_path = NULL; + // Add Library Directory - char *new_ld_path; safe_asprintf(&new_ld_path, "%s/lib", binary_directory); - // Add Existing LD_LIBRARY_PATH + + // Add MCPI_LD_LIBRARY_PATH + { + char *value = get_env_safe("MCPI_LD_LIBRARY_PATH"); + if (strlen(value) > 0) { + string_append(&new_ld_path, ":%s", value); + } + } + + // Add LD_LIBRARY_PATH (ARM32 Only) +#ifdef __arm__ { char *value = get_env_safe("LD_LIBRARY_PATH"); if (strlen(value) > 0) { string_append(&new_ld_path, ":%s", value); } } - // Load ARM Libraries -#ifdef __ARM_ARCH - string_append(&new_ld_path, "%s", ":/usr/lib/arm-linux-gnueabihf:/usr/arm-linux-gnueabihf/lib"); #endif + + // Load ARM Libraries (Ensure Priority) + string_append(&new_ld_path, ":%s/usr/lib/arm-linux-gnueabihf:%s/usr/arm-linux-gnueabihf/lib", usr_prefix, usr_prefix); + // Add Full Library Search Path { char *value = get_full_library_search_path(); @@ -168,8 +196,10 @@ void bootstrap(int argc, char *argv[]) { } free(value); } + // Add Fallback Library Directory string_append(&new_ld_path, ":%s/fallback-lib", binary_directory); + // Set And Free set_and_print_env("LD_LIBRARY_PATH", new_ld_path); free(new_ld_path); @@ -177,9 +207,9 @@ void bootstrap(int argc, char *argv[]) { // Configure LD_PRELOAD { + // Preserve PRESERVE_ENVIRONMENTAL_VARIABLE("LD_PRELOAD"); char *new_ld_preload = NULL; - safe_asprintf(&new_ld_preload, "%s", get_env_safe("LD_PRELOAD")); // Built-In Mods { @@ -203,6 +233,24 @@ void bootstrap(int argc, char *argv[]) { free(mods_folder); } + // Add MCPI_LD_PRELOAD + { + char *value = get_env_safe("MCPI_LD_PRELOAD"); + if (strlen(value) > 0) { + string_append(&new_ld_preload, ":%s", value); + } + } + + // Add LD_PRELOAD (ARM32 Only) +#ifdef __arm__ + { + char *value = get_env_safe("MCPI_LD_PRELOAD"); + if (strlen(value) > 0) { + string_append(&new_ld_preload, ":%s", value); + } + } +#endif + // Set LD_PRELOAD set_and_print_env("LD_PRELOAD", new_ld_preload); free(new_ld_preload); @@ -225,40 +273,63 @@ void bootstrap(int argc, char *argv[]) { free(new_path); } - // Start Game - INFO("%s", "Starting Game..."); + // Resolve Binary Path & Set MCPI_DIRECTORY + { + // Resolve Full Binary Path + char *full_path = NULL; + safe_asprintf(&full_path, "%s/" MCPI_BINARY, binary_directory); + char *resolved_path = realpath(full_path, NULL); + ALLOC_CHECK(resolved_path); + free(full_path); - // Use Correct LibC -#ifndef __ARM_ARCH - setenv("QEMU_LD_PREFIX", "/usr/arm-linux-gnueabihf", 1); -#endif + // Set MCPI_EXECUTABLE_PATH + set_and_print_env("MCPI_EXECUTABLE_PATH", resolved_path); - // Create Full Path - char *full_path = NULL; - safe_asprintf(&full_path, "%s/" MCPI_NAME, binary_directory); + // Set MCPI_DIRECTORY + chop_last_component(&resolved_path); + set_and_print_env("MCPI_DIRECTORY", resolved_path); + free(resolved_path); + } // Free Binary Directory free(binary_directory); -#ifdef __ARM_ARCH - // Mark argc As Used - (void) argc; - // Run - safe_execvpe(full_path, argv, environ); -#else + // Start Game + INFO("%s", "Starting Game..."); + + // Arguments + int argv_start = 2; // argv = &new_args[argv_start] + char *new_args[argv_start /* 2 Potential Prefix Arguments (QEMU And Linker) */ + argc + 1 /* NULL-Terminator */]; // + + // Copy Existing Arguments + for (int i = 1; i < argc; i++) { + new_args[i + argv_start] = argv[i]; + } + // NULL-Terminator + new_args[argv_start + argc] = NULL; + + // Set Executable Argument + new_args[argv_start] = getenv("MCPI_EXECUTABLE_PATH"); + + // Non-ARM32 Systems Need Manually Specified Linker +#ifndef __arm__ + argv_start--; + char *linker = NULL; + safe_asprintf(&linker, "%s/usr/arm-linux-gnueabihf/lib/ld-linux-armhf.so.3", usr_prefix); + new_args[argv_start] = linker; + + // Non-ARM Systems Need QEMU +#ifndef __ARM_ARCH + argv_start--; + new_args[argv_start] = QEMU_BINARY; + // Prevent QEMU From Being Modded PASS_ENVIRONMENTAL_VARIABLE_TO_QEMU("LD_LIBRARY_PATH"); PASS_ENVIRONMENTAL_VARIABLE_TO_QEMU("LD_PRELOAD"); - // Use QEMU -#define EXE_INTERPRETER "qemu-arm" - // Create Arguments List - char *new_argv[argc + 2]; - for (int i = 1; i <= argc; i++) { - new_argv[i + 1] = argv[i]; - } - new_argv[0] = NULL; // Updated By safe_execvpe() - new_argv[1] = full_path; // Path To MCPI - // Run - safe_execvpe(EXE_INTERPRETER, new_argv, environ); #endif +#endif + + // Run + char **new_argv = &new_args[argv_start]; + safe_execvpe(new_argv[0], new_argv, environ); } diff --git a/launcher/src/ldconfig.cpp b/launcher/src/ldconfig.cpp index 44d73477b..aa4dfb22a 100644 --- a/launcher/src/ldconfig.cpp +++ b/launcher/src/ldconfig.cpp @@ -37,7 +37,10 @@ char *get_full_library_search_path() { output.pop_back(); } // Close Process - pclose(file); + int ret = WEXITSTATUS(pclose(file)); + if (ret != 0) { + ERR("ldconfig Failed: Exit Code: %i", ret); + } // Return char *output_str = strdup(output.c_str()); ALLOC_CHECK(output_str); diff --git a/libreborn/CMakeLists.txt b/libreborn/CMakeLists.txt index 8eea982f8..6bddb827d 100644 --- a/libreborn/CMakeLists.txt +++ b/libreborn/CMakeLists.txt @@ -4,7 +4,7 @@ add_library(reborn-headers INTERFACE) target_include_directories(reborn-headers INTERFACE include) if(BUILD_ARM_COMPONENTS) - add_library(reborn SHARED src/reborn.c) + add_library(reborn SHARED src/patch.c) target_link_libraries(reborn dl reborn-headers) target_compile_definitions(reborn PUBLIC -DREBORN_HAS_COMPILED_CODE) # Install diff --git a/libreborn/include/libreborn/elf.h b/libreborn/include/libreborn/elf.h index 1a3fd4886..5922ee512 100644 --- a/libreborn/include/libreborn/elf.h +++ b/libreborn/include/libreborn/elf.h @@ -13,7 +13,7 @@ typedef void (*text_section_callback_t)(ElfW(Addr) section, ElfW(Word) size, void *data); static inline void iterate_text_sections(text_section_callback_t callback, void *data) { // Load Main Binary - FILE *file_obj = fopen("/proc/self/exe", "rb"); + FILE *file_obj = fopen(getenv("MCPI_EXECUTABLE_PATH"), "rb"); // Verify Binary if (!file_obj) { diff --git a/libreborn/include/libreborn/exec.h b/libreborn/include/libreborn/exec.h index 85628c0cc..0b6106ea1 100644 --- a/libreborn/include/libreborn/exec.h +++ b/libreborn/include/libreborn/exec.h @@ -19,6 +19,18 @@ __attribute__((noreturn)) static inline void safe_execvpe(const char *pathname, ERR("%s", "Unknown execvpe() Error"); } } + +// Chop Off Last Component +static inline void chop_last_component(char **str) { + size_t length = strlen(*str); + for (size_t i = 0; i < length; i++) { + size_t j = length - i - 1; + if ((*str)[j] == '/') { + (*str)[j] = '\0'; + break; + } + } +} // Get Binary Directory (Remember To Free) static inline char *get_binary_directory() { // Get Path To Current Executable @@ -26,17 +38,12 @@ static inline char *get_binary_directory() { ALLOC_CHECK(exe); // Chop Off Last Component - int length = strlen(exe); - for (int i = length - 1; i >= 0; i--) { - if (exe[i] == '/') { - exe[i] = '\0'; - break; - } - } + chop_last_component(&exe); // Return return exe; } + // Safe execvpe() Relative To Binary __attribute__((noreturn)) static inline void safe_execvpe_relative_to_binary(const char *pathname, char *argv[], char *const envp[]) { // Get Binary Directory @@ -49,3 +56,8 @@ __attribute__((noreturn)) static inline void safe_execvpe_relative_to_binary(con // Run safe_execvpe(full_path, argv, envp); } + +// Get MCPI Directory +static inline char *get_mcpi_directory() { + return getenv("MCPI_DIRECTORY"); +} diff --git a/libreborn/src/reborn.c b/libreborn/src/patch.c similarity index 100% rename from libreborn/src/reborn.c rename to libreborn/src/patch.c diff --git a/mods/src/chat/ui.c b/mods/src/chat/ui.c index 06187f76a..c316ab015 100644 --- a/mods/src/chat/ui.c +++ b/mods/src/chat/ui.c @@ -29,7 +29,7 @@ static char *run_command(char *command, int *return_code) { } // Return - *return_code = pclose(out); + *return_code = WEXITSTATUS(pclose(out)); return output; } diff --git a/mods/src/home/home.c b/mods/src/home/home.c index b08362c31..1474b8aae 100644 --- a/mods/src/home/home.c +++ b/mods/src/home/home.c @@ -29,9 +29,8 @@ void init_home() { // Change Directory To Binary Directory Manually unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop" patch((void *) 0xe0ac, nop_patch); - char *binary_directory = get_binary_directory(); + char *binary_directory = get_mcpi_directory(); if (chdir(binary_directory) != 0) { ERR("Unable To Change Directory: %s", strerror(errno)); } - free(binary_directory); } diff --git a/mods/src/override/override.c b/mods/src/override/override.c index f7930d65b..0e3a0fdcf 100644 --- a/mods/src/override/override.c +++ b/mods/src/override/override.c @@ -10,10 +10,12 @@ #include "../home/home.h" +// Check If String Starts With Prefix static int starts_with(const char *s, const char *t) { return strncmp(s, t, strlen(t)) == 0; } +// Get Override Path For File (If It Exists) char *override_get_path(const char *filename) { // Get MCPI Home Path char *home_path = home_get(); @@ -23,9 +25,8 @@ char *override_get_path(const char *filename) { // Get Data Path char *data = NULL; - char *binary_directory = get_binary_directory(); + char *binary_directory = get_mcpi_directory(); safe_asprintf(&data, "%s/data", binary_directory); - free(binary_directory); int data_length = strlen(data); // Get Full Path diff --git a/mods/src/sound/sound.cpp b/mods/src/sound/sound.cpp index 4f845ef19..35491a75a 100644 --- a/mods/src/sound/sound.cpp +++ b/mods/src/sound/sound.cpp @@ -23,15 +23,11 @@ std::string _sound_get_source_file() { // Resolve // Get Binary Directory - char *binary_directory = get_binary_directory(); - + char *binary_directory = get_mcpi_directory(); // Get Full Path char *full_path = NULL; safe_asprintf(&full_path, "%s/" SOURCE_FILE_BASE, binary_directory); - // Free Binary Directory - free(binary_directory); - // Handle Overrides char *overridden_full_path = override_get_path(full_path); if (overridden_full_path != NULL) { diff --git a/scripts/build-all.sh b/scripts/build-all.sh deleted file mode 100755 index 674d780ee..000000000 --- a/scripts/build-all.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -set -e - -# Clean Prefix -rm -rf out - -# Build -./scripts/build.sh client amd64 -./scripts/build.sh server amd64 -./scripts/build.sh client arm64 -./scripts/build.sh server arm64 -./scripts/build.sh client armhf -./scripts/build.sh server armhf diff --git a/scripts/ci/run.sh b/scripts/ci/run.sh index 937904f5a..37dba8cc8 100755 --- a/scripts/ci/run.sh +++ b/scripts/ci/run.sh @@ -13,14 +13,10 @@ export ARM_PACKAGES_SUPPORTED=1 echo '==== Installing Dependencies ====' ./scripts/install-dependencies.sh -# Build -echo '==== Building ====' -./scripts/build-all.sh +# Build/Package +echo '==== Building & Packaging ====' +./scripts/package-all.sh # Test echo '==== Testing ====' ./scripts/test.sh - -# Package -echo '==== Packaging ====' -./scripts/package.sh diff --git a/scripts/generate-appimage-builder-yaml.js b/scripts/generate-appimage-builder-yaml.js new file mode 100755 index 000000000..d8571322b --- /dev/null +++ b/scripts/generate-appimage-builder-yaml.js @@ -0,0 +1,185 @@ +#!/usr/bin/env node + +// Arguments +if (process.argv.length < 4) { + throw new Error('Invalid Arguments'); +} +const mode = process.argv[2]; +const arch = process.argv[3]; + +// Data +const id = `com.thebrokenrail.MCPIReborn${mode === 'server' ? 'Server' : ''}`; +const name = `minecraft-pi-reborn-${mode}`; + +// APT Data +const apt_distribution = 'bullseye'; +const apt_key_url = 'https://ftp-master.debian.org/keys/archive-key-11.asc'; + +// Version +const fs = require('fs'); +const version = fs.readFileSync('VERSION', 'utf8').trim(); + +// Packages/Dependencies +const packages = [ + 'libc6', + 'libc-bin', + 'libstdc++6' +]; +if (mode === 'client') { + packages.push( + 'zenity', + 'libcanberra-gtk3-module', + 'libglfw3', + 'libfreeimage3', + 'libopenal1' + ); +} +if (arch !== 'armhf') { + packages.push( + 'libc6-armhf-cross', + 'libstdc++6-armhf-cross' + ); + if (arch !== 'arm64') { + packages.push('qemu-user'); + } +} + +// Package Exclusions +const packageExclusions = [ + // Exclude Unneeded Packages + 'humanity-icon-theme', + 'adwaita-icon-theme', + 'libwebkit2gtk-*', + 'libnotify4', + '*systemd*', + 'dconf-service', + 'dconf-gsettings-backend', + 'libgnutls*', + 'librest-*', + 'libcups2', + 'libcolord2', + 'libmount1' +]; + +// APT +const apt = { + arch: arch, + sources: [ + { + sourceline: `deb [arch=${arch}] http://deb.debian.org/debian/ ${apt_distribution} main`, + key_url: apt_key_url + }, + { + sourceline: `deb [arch=${arch}] http://deb.debian.org/debian/ ${apt_distribution}-updates main`, + key_url: apt_key_url + } + ], + include: packages, + exclude: packageExclusions +}; + +// Get Architecture Triplet +const triplet = { + 'amd64': 'x86_64-linux-gnu', + 'i386': 'i386-linux-gnu', + 'arm64': 'aarch64-linux-gnu', + 'armhf': 'arm-linux-gnueabihf' +}[arch]; +if (!triplet) { + throw new Error(); +} + +// Files +const files = { + exclude: [ + // Exclude Unused Files + `usr/lib/${triplet}/gconv`, + 'usr/share/man', + 'usr/share/doc/*/README.*', + 'usr/share/doc/*/changelog.*', + 'usr/share/doc/*/NEWS.*', + 'usr/share/doc/*/TODO.*', + 'usr/include', + 'usr/share/locale', + 'usr/share/help' + ] +}; + +// Script After Bundling Dependencies +const afterBundle = [ + // Remove Unused QEMU Binaries + 'find ./AppDir/usr/bin -maxdepth 1 -name \'qemu-*\' -a ! -name \'qemu-arm\' -delete' +]; + +// Runtime +const runtime = { + env: mode === 'client' ? { + // Make GTK Work (Zenity Uses GTK) + GTK_EXE_PREFIX: '${APPDIR}/usr', + GTK_PATH: `\${APPDIR}/usr/lib/${triplet}/gtk-3.0`, + GTK_DATA_PREFIX: '${APPDIR}', + GTK_THEME: 'Default', + APPDIR_LIBRARY_PATH: `\${APPDIR}/usr/lib/${triplet}:\${APPDIR}/lib/${triplet}:\${APPDIR}/usr/lib:\${APPDIR}/usr/lib/${triplet}/gdk-pixbuf-2.0/2.10.0/loaders` + } : undefined, + preserve: arch !== 'armhf' ? [ + // On non-ARM32 systems, an ARM32 linker is embedded, this + // prevents AppImage-Builder from modifying ARM32 binaries + // to use a (usually non-existent) system linker. + `usr/lib/${name}/minecraft-pi`, + `usr/lib/${name}/**/*.so`, + 'usr/arm-linux-gnueabihf/lib' + ] : undefined +}; + +// AppDir +const appDir = { + path: `./AppDir`, + app_info: { + id: id, + name: `${name}`, + icon: mode === 'client' ? id : 'utilities-terminal', + version: version, + exec: `usr/bin/${name}`, + exec_args: '$@' + }, + apt: apt, + files: files, + after_bundle: afterBundle, + runtime: runtime +}; + +// Build Script +const script = [ + `rm -rf ./build/${mode}-${arch}`, + `./scripts/setup.sh ${mode} ${arch} -DMCPI_IS_APPIMAGE_BUILD=ON`, + `rm -rf ./out/${mode}-${arch}`, + `./scripts/build.sh ${mode} ${arch}`, + 'rm -rf ./AppDir', + `cp -ar ./out/${mode}-${arch} ./AppDir` +]; + +// AppImage +const appImageArch = { + 'amd64': 'x86_64', + 'i386': 'i686', + 'arm64': 'aarch64', + 'armhf': 'armhf' +}[arch]; +if (!appImageArch) { + throw new Error(); +} +const appImage = { + arch: appImageArch, + file_name: `./out/${name}-${version}-${arch}.AppImage` +}; + +// Root +const root = { + version: 1, + AppDir: appDir, + script: script, + AppImage: appImage +}; + +// Write +fs.writeFileSync(`AppImageBuilder.yml`, JSON.stringify(root, null, 4)); diff --git a/scripts/install-dependencies.sh b/scripts/install-dependencies.sh index 36e27aaf3..ddefd4416 100755 --- a/scripts/install-dependencies.sh +++ b/scripts/install-dependencies.sh @@ -18,9 +18,14 @@ fi sudo apt-get update sudo apt-get dist-upgrade -y +# Install Everything In One Go +PKG_QUEUE='' +queue_pkg() { + PKG_QUEUE="${PKG_QUEUE} $@" +} + # Install -sudo apt-get install --no-install-recommends -y \ - dpkg-dev \ +queue_pkg \ git \ cmake \ ninja-build \ @@ -28,12 +33,13 @@ sudo apt-get install --no-install-recommends -y \ libfreeimage3 libfreeimage-dev \ crossbuild-essential-armhf \ gcc g++ \ + nodejs \ libopenal-dev \ qemu-user # Install ARM Dependencies if [ ! -z "${ARM_PACKAGES_SUPPORTED}" ]; then - sudo apt-get install --no-install-recommends -y \ + queue_pkg \ libglfw3:armhf libglfw3-dev:armhf \ libfreeimage3:armhf \ libopenal-dev:armhf \ @@ -43,3 +49,28 @@ if [ ! -z "${ARM_PACKAGES_SUPPORTED}" ]; then crossbuild-essential-arm64 fi +# Install appimagetool Dependencies +queue_pkg \ + python3-pip \ + python3-setuptools \ + patchelf \ + desktop-file-utils \ + libgdk-pixbuf2.0-dev \ + fakeroot \ + strace \ + fuse \ + sed + +# Install Queue +sudo apt-get install --no-install-recommends -y ${PKG_QUEUE} + +# Download appimagetool +sudo mkdir -p /opt +sudo wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O /opt/appimagetool +# Workaround AppImage Issues With Docker +cd /opt; sudo chmod +x ./appimagetool; sed -i '0,/AI\x02/{s|AI\x02|\x00\x00\x00|}' ./appimagetool; sudo ./appimagetool --appimage-extract +sudo mv /opt/squashfs-root /opt/appimagetool.AppDir +sudo ln -s /opt/appimagetool.AppDir/AppRun /usr/local/bin/appimagetool + +# Install appimage-builder +sudo pip3 install git+https://github.com/AppImageCrafters/appimage-builder.git diff --git a/scripts/package-all.sh b/scripts/package-all.sh new file mode 100755 index 000000000..a0ae95db6 --- /dev/null +++ b/scripts/package-all.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -e + +# Clean Prefix +rm -rf out + +# Build +./scripts/package.sh client amd64 +./scripts/package.sh server amd64 +./scripts/package.sh client arm64 +./scripts/package.sh server arm64 +./scripts/package.sh client armhf +./scripts/package.sh server armhf diff --git a/scripts/package.sh b/scripts/package.sh index a99cf141b..dc2c9a263 100755 --- a/scripts/package.sh +++ b/scripts/package.sh @@ -2,36 +2,8 @@ set -e -# Prepare -VERSION="$(cat VERSION)" +# Generate +./scripts/generate-appimage-builder-yaml.js "$1" "$2" -# Common -package() { - local dir="out/$1" - - # Create DEBIAN Dir - rm -rf "${dir}/DEBIAN" - mkdir -p "${dir}/DEBIAN" - cp "debian/$1" "${dir}/DEBIAN/control" - - # Format DEBIAN/control - sed -i "s/\${VERSION}/${VERSION}/g" "${dir}/DEBIAN/control" - - # Fix Permissions On Jenkins - chmod -R g-s "${dir}" - - # Package - dpkg-deb --root-owner-group --build "${dir}" out -} - -# Find And Package -for dir in out/*; do - # Check If Directory Exists - if [ -d "${dir}" ]; then - # Check If Debian Package Exists - pkg="$(basename ${dir})" - if [ -f "debian/${pkg}" ]; then - package "${pkg}" - fi - fi -done +# Build/Package +appimage-builder --recipe AppImageBuilder.yml diff --git a/scripts/test.sh b/scripts/test.sh index 75407580f..7dfb5fb11 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -2,6 +2,10 @@ set -e +# Build Test +./scripts/setup.sh server "$(dpkg-architecture -qDEB_BUILD_ARCH)" +./scripts/build.sh server "$(dpkg-architecture -qDEB_BUILD_ARCH)" + # Add minecraft-pi-reborn-server To PATH export PATH="$(pwd)/out/server-$(dpkg-architecture -qDEB_BUILD_ARCH)/usr/bin:${PATH}"