fix: bundle fcitx gtk module in appimage
This commit is contained in:
@@ -21,10 +21,19 @@ export const FIXED_LINUXDEPLOY_APPRUN_DIR_LINE =
|
||||
'this_dir="$(dirname "$(readlink -f "$0")")"'
|
||||
export const APPIMAGE_PLUGIN_WRAPPER_NAME = 'linuxdeploy-plugin-appimage.AppImage'
|
||||
export const REAL_APPIMAGE_PLUGIN_NAME = 'linuxdeploy-plugin-appimage.real.AppImage'
|
||||
export const APPIMAGE_FCITX_GTK3_IM_MODULE_PATH =
|
||||
'usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-fcitx5.so'
|
||||
export const APPIMAGE_FCITX_GCLIENT_LIBRARY_PATH =
|
||||
'usr/lib/x86_64-linux-gnu/libFcitx5GClient.so.2'
|
||||
export const DEFAULT_APPIMAGE_PLUGIN_URL =
|
||||
'https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage'
|
||||
|
||||
const WRAPPER_MARKER = 'Tolaria AppImage symlink launcher shim'
|
||||
const REQUIRED_APPIMAGE_PATHS = [
|
||||
'AppRun',
|
||||
APPIMAGE_FCITX_GTK3_IM_MODULE_PATH,
|
||||
APPIMAGE_FCITX_GCLIENT_LIBRARY_PATH,
|
||||
]
|
||||
|
||||
export function tauriToolsCacheDir(env = process.env) {
|
||||
if (env.TOLARIA_TAURI_TOOLS_DIR) {
|
||||
@@ -76,6 +85,8 @@ set -euo pipefail
|
||||
PLUGIN_URL="\${TOLARIA_APPIMAGE_PLUGIN_URL:-${pluginUrl}}"
|
||||
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
||||
REAL_PLUGIN="\${TOLARIA_APPIMAGE_REAL_PLUGIN:-"$SCRIPT_DIR/${REAL_APPIMAGE_PLUGIN_NAME}"}"
|
||||
FCITX_GTK3_IM_MODULE="\${TOLARIA_FCITX_GTK3_IM_MODULE:-/usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-fcitx5.so}"
|
||||
FCITX_LIBRARY_DIR="\${TOLARIA_FCITX_LIBRARY_DIR:-/usr/lib/x86_64-linux-gnu}"
|
||||
|
||||
appdir_from_args() {
|
||||
local previous=""
|
||||
@@ -148,8 +159,39 @@ elif "autogenerated by linuxdeploy" in text and "AppRun.wrapped" in text:
|
||||
PY
|
||||
}
|
||||
|
||||
bundle_fcitx_gtk3_module() {
|
||||
local appdir="\${APPDIR:-}"
|
||||
|
||||
if [ -z "$appdir" ]; then
|
||||
appdir="$(appdir_from_args "$@" || true)"
|
||||
fi
|
||||
|
||||
if [ -z "$appdir" ] || [ ! -d "$appdir" ] || [ ! -f "$FCITX_GTK3_IM_MODULE" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local module_dest="$appdir/${APPIMAGE_FCITX_GTK3_IM_MODULE_PATH}"
|
||||
local library_dest_dir="$appdir/usr/lib/x86_64-linux-gnu"
|
||||
|
||||
mkdir -p "$(dirname -- "$module_dest")" "$library_dest_dir"
|
||||
cp -a "$FCITX_GTK3_IM_MODULE" "$module_dest"
|
||||
|
||||
local copied_library=0
|
||||
shopt -s nullglob
|
||||
for lib in "$FCITX_LIBRARY_DIR"/libFcitx5GClient.so* "$FCITX_LIBRARY_DIR"/libFcitx5Utils.so*; do
|
||||
cp -a "$lib" "$library_dest_dir/"
|
||||
copied_library=1
|
||||
done
|
||||
shopt -u nullglob
|
||||
|
||||
if [ "$copied_library" -eq 0 ]; then
|
||||
echo "No fcitx GTK client libraries found in $FCITX_LIBRARY_DIR" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
download_real_plugin
|
||||
patch_apprun "$@"
|
||||
bundle_fcitx_gtk3_module "$@"
|
||||
exec "$REAL_PLUGIN" "$@"
|
||||
`
|
||||
}
|
||||
@@ -181,6 +223,45 @@ export async function validateAppRunFile(path) {
|
||||
assertSymlinkSafeAppRunText(text, path)
|
||||
}
|
||||
|
||||
function extractAppImagePath(appImage, requiredPath, tempDir) {
|
||||
const result = spawnSync(appImage, ['--appimage-extract', requiredPath], {
|
||||
cwd: tempDir,
|
||||
encoding: 'utf8',
|
||||
})
|
||||
|
||||
if (result.status === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
[
|
||||
`Failed to extract ${requiredPath} from ${appImage}`,
|
||||
result.stdout.trim(),
|
||||
result.stderr.trim(),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('\n'),
|
||||
)
|
||||
}
|
||||
|
||||
function assertAppImagePathsExtracted(appImage, tempDir, requiredPaths) {
|
||||
for (const requiredPath of requiredPaths) {
|
||||
const extractedPath = join(tempDir, 'squashfs-root', requiredPath)
|
||||
if (!existsSync(extractedPath)) {
|
||||
throw new Error(`${appImage} is missing ${requiredPath}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function validateExtractedAppImage(appImage, tempDir) {
|
||||
for (const requiredPath of REQUIRED_APPIMAGE_PATHS) {
|
||||
extractAppImagePath(appImage, requiredPath, tempDir)
|
||||
}
|
||||
|
||||
await validateAppRunFile(join(tempDir, 'squashfs-root', 'AppRun'))
|
||||
assertAppImagePathsExtracted(appImage, tempDir, REQUIRED_APPIMAGE_PATHS.slice(1))
|
||||
}
|
||||
|
||||
export async function validateAppImages(paths) {
|
||||
if (paths.length === 0) {
|
||||
throw new Error('At least one AppImage path is required for launcher validation')
|
||||
@@ -189,24 +270,7 @@ export async function validateAppImages(paths) {
|
||||
for (const appImage of paths.map((path) => resolve(path))) {
|
||||
const tempDir = await mkdtemp(join(tmpdir(), 'tolaria-appimage-'))
|
||||
try {
|
||||
const result = spawnSync(appImage, ['--appimage-extract', 'AppRun'], {
|
||||
cwd: tempDir,
|
||||
encoding: 'utf8',
|
||||
})
|
||||
|
||||
if (result.status !== 0) {
|
||||
throw new Error(
|
||||
[
|
||||
`Failed to extract AppRun from ${appImage}`,
|
||||
result.stdout.trim(),
|
||||
result.stderr.trim(),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('\n'),
|
||||
)
|
||||
}
|
||||
|
||||
await validateAppRunFile(join(tempDir, 'squashfs-root', 'AppRun'))
|
||||
await validateExtractedAppImage(appImage, tempDir)
|
||||
} finally {
|
||||
await rm(tempDir, { recursive: true, force: true })
|
||||
}
|
||||
|
||||
@@ -112,3 +112,52 @@ test('plugin wrapper patches AppRun before delegating to the real output plugin'
|
||||
assert.equal(patched.includes(BROKEN_LINUXDEPLOY_APPRUN_DIR_LINE), false)
|
||||
assert.equal(patched.includes(FIXED_LINUXDEPLOY_APPRUN_DIR_LINE), true)
|
||||
})
|
||||
|
||||
test('plugin wrapper bundles fcitx GTK3 input module before sealing AppImage', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'tolaria-appimage-fcitx-'))
|
||||
const appDir = join(root, 'Tolaria.AppDir')
|
||||
const wrapper = join(root, 'linuxdeploy-plugin-appimage.AppImage')
|
||||
const realPlugin = join(root, 'linuxdeploy-plugin-appimage.real.AppImage')
|
||||
const pluginMarker = join(root, 'plugin-ran')
|
||||
const hostModule = join(root, 'host', 'im-fcitx5.so')
|
||||
const hostLibraryDir = join(root, 'host-lib')
|
||||
const hostLibrary = join(hostLibraryDir, 'libFcitx5GClient.so.2')
|
||||
|
||||
await mkdir(appDir)
|
||||
await mkdir(dirname(hostModule), { recursive: true })
|
||||
await mkdir(hostLibraryDir)
|
||||
await writeFile(hostModule, 'fake fcitx gtk module', 'utf8')
|
||||
await writeFile(hostLibrary, 'fake fcitx client library', 'utf8')
|
||||
await writeFile(wrapper, appImagePluginWrapperSource(), 'utf8')
|
||||
await chmod(wrapper, 0o755)
|
||||
await writeFile(
|
||||
realPlugin,
|
||||
`#!/usr/bin/env bash\nset -euo pipefail\ntouch "${pluginMarker}"\n`,
|
||||
'utf8',
|
||||
)
|
||||
await chmod(realPlugin, 0o755)
|
||||
|
||||
const result = spawnSync(wrapper, [], {
|
||||
encoding: 'utf8',
|
||||
env: {
|
||||
...process.env,
|
||||
APPDIR: appDir,
|
||||
TOLARIA_APPIMAGE_REAL_PLUGIN: realPlugin,
|
||||
TOLARIA_FCITX_GTK3_IM_MODULE: hostModule,
|
||||
TOLARIA_FCITX_LIBRARY_DIR: hostLibraryDir,
|
||||
},
|
||||
})
|
||||
|
||||
assert.equal(result.status, 0, result.stderr)
|
||||
assert.equal(existsSync(pluginMarker), true)
|
||||
assert.equal(
|
||||
existsSync(
|
||||
join(
|
||||
appDir,
|
||||
'usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-fcitx5.so',
|
||||
),
|
||||
),
|
||||
true,
|
||||
)
|
||||
assert.equal(existsSync(join(appDir, 'usr/lib/x86_64-linux-gnu/libFcitx5GClient.so.2')), true)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user