|
| 1 | +# linuxdeploy.cmake |
| 2 | +# |
| 3 | +# This file provides a CMake function "build_appimage" |
| 4 | +# that uses linuxdeploy to create an AppImage from |
| 5 | +# a given executable, desktop file, and icon. |
| 6 | +# |
| 7 | +# Usage: |
| 8 | +# build_appimage( |
| 9 | +# APP_NAME "MyApp" |
| 10 | +# APP_VERSION "1.0.0" |
| 11 | +# EXECUTABLE "/path/to/MyApp" |
| 12 | +# ICON "/path/to/myapp.png" |
| 13 | +# DESKTOP_NAME "myapp.desktop" # optional |
| 14 | +# DESKTOP_CATEGORIES "Utility;" # optional |
| 15 | +# APP_ARCH "aarch64" # optional (default x86_64) |
| 16 | +# ) |
| 17 | +# |
| 18 | +# Note on cross-compilation: |
| 19 | +# If you specify APP_ARCH != the host architecture, |
| 20 | +# make sure you have: |
| 21 | +# 1) A cross-compiled executable (ELF for the target arch). |
| 22 | +# 2) linuxdeploy-<ARCH>.AppImage for that target. |
| 23 | +# 3) A suitable environment (QEMU / Docker / etc.) so that |
| 24 | +# linuxdeploy can run and detect libraries. |
| 25 | + |
| 26 | +function(build_appimage) |
| 27 | + # We expect user to pass these named arguments: |
| 28 | + set(optional_args) |
| 29 | + set(single_args |
| 30 | + APP_NAME |
| 31 | + APP_VERSION |
| 32 | + EXECUTABLE |
| 33 | + ICON |
| 34 | + DESKTOP_NAME |
| 35 | + DESKTOP_CATEGORIES |
| 36 | + APP_ARCH |
| 37 | + ) |
| 38 | + set(multi_value_args) |
| 39 | + |
| 40 | + cmake_parse_arguments( |
| 41 | + APPIMG |
| 42 | + "${optional_args}" |
| 43 | + "${single_args}" |
| 44 | + "${multi_value_args}" |
| 45 | + ${ARGN} |
| 46 | + ) |
| 47 | + |
| 48 | + # Basic checks |
| 49 | + if(NOT APPIMG_APP_NAME) |
| 50 | + message(FATAL_ERROR "build_appimage: APP_NAME is not specified!") |
| 51 | + endif() |
| 52 | + if(NOT APPIMG_EXECUTABLE) |
| 53 | + message(FATAL_ERROR "build_appimage: EXECUTABLE is not specified!") |
| 54 | + endif() |
| 55 | + if(NOT EXISTS "${APPIMG_EXECUTABLE}") |
| 56 | + message(FATAL_ERROR "build_appimage: EXECUTABLE does not exist: ${APPIMG_EXECUTABLE}") |
| 57 | + endif() |
| 58 | + if(NOT APPIMG_ICON) |
| 59 | + message(FATAL_ERROR "build_appimage: ICON is not specified!") |
| 60 | + endif() |
| 61 | + if(NOT EXISTS "${APPIMG_ICON}") |
| 62 | + message(FATAL_ERROR "build_appimage: ICON does not exist: ${APPIMG_ICON}") |
| 63 | + endif() |
| 64 | + |
| 65 | + # If DESKTOP_NAME is not provided, use appname.desktop |
| 66 | + if(NOT APPIMG_DESKTOP_NAME) |
| 67 | + set(APPIMG_DESKTOP_NAME "${APPIMG_APP_NAME}.desktop") |
| 68 | + endif() |
| 69 | + |
| 70 | + # If DESKTOP_CATEGORIES is not provided, use Utility; |
| 71 | + if(NOT APPIMG_DESKTOP_CATEGORIES) |
| 72 | + set(APPIMG_DESKTOP_CATEGORIES "Utility;") |
| 73 | + endif() |
| 74 | + |
| 75 | + # If APP_ARCH is not specified, default to x86_64 |
| 76 | + if(NOT APPIMG_APP_ARCH) |
| 77 | + set(APPIMG_APP_ARCH "x86_64") |
| 78 | + endif() |
| 79 | + |
| 80 | + # Decide on final AppImage name |
| 81 | + if(APPIMG_APP_VERSION) |
| 82 | + set(APPIMAGE_NAME "${APPIMG_APP_NAME}-${APPIMG_APP_VERSION}-${APPIMG_APP_ARCH}.AppImage") |
| 83 | + else() |
| 84 | + set(APPIMAGE_NAME "${APPIMG_APP_NAME}-${APPIMG_APP_ARCH}.AppImage") |
| 85 | + endif() |
| 86 | + |
| 87 | + # Download linuxdeploy if needed |
| 88 | + # We'll fetch the <ARCH> version from official releases |
| 89 | + set(LINUXDEPLOY_BIN "${CMAKE_BINARY_DIR}/linuxdeploy-${APPIMG_APP_ARCH}.AppImage") |
| 90 | + if(NOT EXISTS "${LINUXDEPLOY_BIN}") |
| 91 | + set(LINUXDEPLOY_URL "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${APPIMG_APP_ARCH}.AppImage") |
| 92 | + message(STATUS "Downloading linuxdeploy from: ${LINUXDEPLOY_URL}") |
| 93 | + message(STATUS "into: ${LINUXDEPLOY_BIN}") |
| 94 | + file(DOWNLOAD |
| 95 | + "${LINUXDEPLOY_URL}" |
| 96 | + "${LINUXDEPLOY_BIN}" |
| 97 | + SHOW_PROGRESS |
| 98 | + ) |
| 99 | + execute_process(COMMAND chmod +x "${LINUXDEPLOY_BIN}") |
| 100 | + endif() |
| 101 | + |
| 102 | + # Create an AppDir folder in the build directory |
| 103 | + set(APPDIR "${CMAKE_BINARY_DIR}/${APPIMG_APP_NAME}.AppDir") |
| 104 | + file(REMOVE_RECURSE "${APPDIR}") |
| 105 | + file(MAKE_DIRECTORY "${APPDIR}") |
| 106 | + file(MAKE_DIRECTORY "${APPDIR}/usr/bin") |
| 107 | + file(MAKE_DIRECTORY "${APPDIR}/usr/share/applications") |
| 108 | + file(MAKE_DIRECTORY "${APPDIR}/usr/share/icons/hicolor/256x256/apps") |
| 109 | + |
| 110 | + # Copy the executable into AppDir/usr/bin |
| 111 | + file(COPY "${APPIMG_EXECUTABLE}" DESTINATION "${APPDIR}/usr/bin") |
| 112 | + get_filename_component(EXE_BASENAME "${APPIMG_EXECUTABLE}" NAME) |
| 113 | + |
| 114 | + # Write or copy a .desktop file |
| 115 | + # If you already have a .desktop file, you could copy it, |
| 116 | + # but here we'll generate one for simplicity. |
| 117 | + set(DESKTOP_PATH "${APPDIR}/usr/share/applications/${APPIMG_DESKTOP_NAME}") |
| 118 | + file(WRITE "${DESKTOP_PATH}" |
| 119 | + "[Desktop Entry] |
| 120 | +Type=Application |
| 121 | +Name=${APPIMG_APP_NAME} |
| 122 | +Exec=${EXE_BASENAME} |
| 123 | +Icon=${APPIMG_APP_NAME} |
| 124 | +Categories=${APPIMG_DESKTOP_CATEGORIES} |
| 125 | +") |
| 126 | + |
| 127 | + # Copy the icon (PNG) |
| 128 | + file(COPY "${APPIMG_ICON}" DESTINATION "${APPDIR}/usr/share/icons/hicolor/256x256/apps/") |
| 129 | + # Rename it so that it matches the Icon= name in the .desktop |
| 130 | + get_filename_component(ICON_BASENAME "${APPIMG_ICON}" NAME) |
| 131 | + file(RENAME |
| 132 | + "${APPDIR}/usr/share/icons/hicolor/256x256/apps/${ICON_BASENAME}" |
| 133 | + "${APPDIR}/usr/share/icons/hicolor/256x256/apps/${APPIMG_APP_NAME}.png" |
| 134 | + ) |
| 135 | + |
| 136 | + # Now call linuxdeploy to bundle everything into AppImage |
| 137 | + # If you are cross-compiling, you must ensure that your environment |
| 138 | + # can run the binary (QEMU, Docker, etc.). |
| 139 | + # Also ensure that libraries for the target arch are visible to linuxdeploy. |
| 140 | + execute_process( |
| 141 | + COMMAND "${LINUXDEPLOY_BIN}" |
| 142 | + --appdir "${APPDIR}" |
| 143 | + --desktop-file "${DESKTOP_PATH}" |
| 144 | + --icon-file "${APPDIR}/usr/share/icons/hicolor/256x256/apps/${APPIMG_APP_NAME}.png" |
| 145 | + --output appimage |
| 146 | + --verbosity=0 |
| 147 | + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" |
| 148 | + ) |
| 149 | + |
| 150 | + # Rename the output .AppImage to include the version and arch |
| 151 | + # Typically, linuxdeploy produces something like "MyApp-x86_64.AppImage" |
| 152 | + # We'll rename it to our desired name: |
| 153 | + set(LINUXDEPLOY_DEFAULT_OUTPUT "${CMAKE_BINARY_DIR}/${APPIMG_APP_NAME}-${APPIMG_APP_ARCH}.AppImage") |
| 154 | + if(EXISTS "${LINUXDEPLOY_DEFAULT_OUTPUT}") |
| 155 | + file(RENAME |
| 156 | + "${LINUXDEPLOY_DEFAULT_OUTPUT}" |
| 157 | + "${CMAKE_BINARY_DIR}/${APPIMAGE_NAME}" |
| 158 | + ) |
| 159 | + else() |
| 160 | + # In some versions it might directly produce the desired name, or a slightly different one: |
| 161 | + # fallback check: |
| 162 | + set(ALT_OUTPUT "${CMAKE_BINARY_DIR}/${APPIMG_APP_NAME}-x86_64.AppImage") |
| 163 | + if(EXISTS "${ALT_OUTPUT}") |
| 164 | + file(RENAME |
| 165 | + "${ALT_OUTPUT}" |
| 166 | + "${CMAKE_BINARY_DIR}/${APPIMAGE_NAME}" |
| 167 | + ) |
| 168 | + endif() |
| 169 | + endif() |
| 170 | + |
| 171 | + message(STATUS "AppImage created at: ${CMAKE_BINARY_DIR}/${APPIMAGE_NAME}") |
| 172 | +endfunction() |
0 commit comments