diff options
| author | ruki <[email protected]> | 2025-11-26 18:25:01 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-11-26 18:25:01 +0800 |
| commit | fee49894d216c926cd789ccf7883e220971c20ef (patch) | |
| tree | d76c02691ccd4edb2ea650f6606a5459494b1a66 /xmake/plugins | |
| parent | e7cd3c2c12d3d51ffef3d2aa9885bfef9db8d650 (diff) | |
| parent | b9bb0835067ae93417112cf15eb686a1d9c1f7da (diff) | |
Merge pull request #7064 from xmake-io/appimage
Pack Appimage support
Diffstat (limited to 'xmake/plugins')
| -rw-r--r-- | xmake/plugins/pack/appimage/main.lua | 229 | ||||
| -rw-r--r-- | xmake/plugins/pack/xpack.lua | 3 |
2 files changed, 232 insertions, 0 deletions
diff --git a/xmake/plugins/pack/appimage/main.lua b/xmake/plugins/pack/appimage/main.lua new file mode 100644 index 000000000..3ae3e5c99 --- /dev/null +++ b/xmake/plugins/pack/appimage/main.lua @@ -0,0 +1,229 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015-present, Xmake Open Source Community. +-- +-- @author ruki +-- @file main.lua +-- + +-- imports +import("core.base.option") +import("lib.detect.find_tool") +import("private.action.require.impl.packagenv") +import("private.action.require.impl.install_packages") +import(".batchcmds") + +-- handle icon file +function _handle_icon(package, appdir, appname) + local iconname = nil + local iconfile = package:get("iconfile") + if iconfile then + local iconpath = path.absolute(iconfile) + if not os.isfile(iconpath) then + raise("icon file not found: %s", iconfile) + end + local ext = path.extension(iconpath) + -- AppImage only supports .png, .svg, .xpm + if ext ~= ".png" and ext ~= ".svg" and ext ~= ".xpm" then + raise("appimage format only supports .png, .svg, or .xpm icon files, but got: %s", ext) + end + iconname = appname .. ext + -- copy to AppDir root (required for AppImage) + os.cp(iconpath, path.join(appdir, iconname)) + -- also copy to standard location + local iconsdir = path.join(appdir, "usr", "share", "icons", "hicolor", "256x256", "apps") + os.mkdir(iconsdir) + os.cp(iconpath, path.join(iconsdir, iconname)) + end + return iconname +end + +-- create desktop file +function _create_desktop_file(package, appdir, appname, apptitle, appdescription, main_executable, iconname) + local desktopfile = path.join(appdir, appname .. ".desktop") + local icon_line = "" + if iconname then + icon_line = string.format("Icon=%s\n", appname) + end + local version_line = "" + local version = package:version() + if version then + version_line = string.format("X-AppImage-Version=%s\n", version) + end + local desktop_content = string.format([[ +[Desktop Entry] +Type=Application +Name=%s +Comment=%s +Exec=usr/bin/%s +%s%sCategories=Utility; +]], apptitle, appdescription, main_executable, icon_line, version_line) + io.writefile(desktopfile, desktop_content) +end + +-- create AppRun script +function _create_apprun_script(appdir, main_executable) + local apprun = path.join(appdir, "AppRun") + local apprun_content = string.format([[ +#!/bin/bash +HERE="$(dirname "$(readlink -f "${0}")")" +exec "${HERE}/usr/bin/%s" "$@" +]], main_executable) + io.writefile(apprun, apprun_content) + os.vrunv("chmod", {"+x", apprun}) +end + +-- get the appimagetool +function _get_appimagetool() + + -- enter the environments of appimagetool + local oldenvs = packagenv.enter("appimagetool") + + -- find appimagetool + local packages = {} + local appimagetool = find_tool("appimagetool") + if not appimagetool then + table.join2(packages, install_packages("appimagetool")) + end + + -- enter the environments of installed packages + for _, instance in ipairs(packages) do + instance:envs_enter() + end + + -- we need to force detect and flush detect cache after loading all environments + if not appimagetool then + appimagetool = find_tool("appimagetool", {force = true}) + end + assert(appimagetool, "appimagetool not found!") + return appimagetool, oldenvs +end + +-- get main executable from package +function _get_main_executable(package, usrdir) + local main_executable = nil + local main_executable_path = nil + + -- try to find from targets first + for _, target in ipairs(package:targets()) do + if target:is_binary() then + main_executable = target:basename() + -- check if file exists in usr/bin + local exec_path = path.join(usrdir, "bin", main_executable) + if os.isfile(exec_path) then + main_executable_path = exec_path + break + end + end + end + + -- fallback: find in bindir + if not main_executable_path then + local bindir = package:bindir() + if bindir and os.isdir(bindir) then + -- find executable files in bindir using os.files callback + os.files(path.join(bindir, "*"), function (file, isdir) + if not isdir and os.isfile(file) and not os.islink(file) and os.isexec(file) then + main_executable = path.filename(file) + main_executable_path = path.join(usrdir, "bin", main_executable) + if os.isfile(main_executable_path) then + return false + end + end + return true + end) + end + end + + return main_executable, main_executable_path +end + +-- pack appimage package +function _pack_appimage(package, appimagetool) + + -- check platform + assert(package:is_plat("linux"), "appimage format only supports Linux platform!") + + -- archive binary files + batchcmds.get_installcmds(package):runcmds() + for _, component in table.orderpairs(package:components()) do + if component:get("default") ~= false then + batchcmds.get_installcmds(component):runcmds() + end + end + + -- get install root directory + local rootdir = package:install_rootdir() + assert(os.isdir(rootdir), "install root directory not found: %s", rootdir) + + -- get output file + local outputfile = package:outputfile() + os.tryrm(outputfile) + + -- create AppDir directory structure + local builddir = package:builddir() + local appdir = path.join(builddir, "AppDir") + os.tryrm(appdir) + os.mkdir(appdir) + + -- copy files to AppDir/usr + local usrdir = path.join(appdir, "usr") + os.cp(rootdir, usrdir) + + -- get main executable + local main_executable, main_executable_path = _get_main_executable(package, usrdir) + assert(main_executable and main_executable_path and os.isfile(main_executable_path), + "main executable not found! Please ensure at least one binary target is added to xpack.") + + -- get application name and title + local appname = package:name() + local apptitle = package:title() or appname + local appdescription = package:description() or "" + + -- handle icon file + local iconname = _handle_icon(package, appdir, appname) + + -- create desktop file + _create_desktop_file(package, appdir, appname, apptitle, appdescription, main_executable, iconname) + + -- create AppRun script + _create_apprun_script(appdir, main_executable) + + -- create AppImage using appimagetool + os.vrunv(appimagetool, {appdir, outputfile}, {envs = {APPIMAGE_EXTRACT_AND_RUN = "1"}}) + + -- verify AppImage was created + assert(os.isfile(outputfile), "generate %s failed!", outputfile) +end + +function main(package) + + -- only for linux + if not is_host("linux") then + return + end + + cprint("packing %s .. ", package:outputfile()) + + -- get appimagetool + local appimagetool, oldenvs = _get_appimagetool() + + -- pack appimage package + _pack_appimage(package, appimagetool.program) + + -- done + os.setenvs(oldenvs) +end + diff --git a/xmake/plugins/pack/xpack.lua b/xmake/plugins/pack/xpack.lua index 1e39d6a83..bccb93cfa 100644 --- a/xmake/plugins/pack/xpack.lua +++ b/xmake/plugins/pack/xpack.lua @@ -206,6 +206,7 @@ function xpack:inputkind() zip = "binary", targz = "binary", dmg = "binary", + appimage = "binary", srczip = "source", srctargz = "source", runself = "source", @@ -226,6 +227,7 @@ function xpack:outputkind() zip = "binary", targz = "binary", dmg = "binary", + appimage = "binary", srczip = "source", srctargz = "source", runself = "source", @@ -376,6 +378,7 @@ function xpack:extension() zip = ".zip", targz = ".tar.gz", dmg = ".dmg", + appimage = ".AppImage", srczip = ".zip", srctargz = ".tar.gz", runself = ".gz.run", |
