""" novelWriter – AppImage Build ============================ This file is a part of novelWriter Copyright (C) 2025 Veronica Berglyd Olsen and novelWriter contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ # noqa from __future__ import annotations import argparse import datetime import os import shutil import sys from pathlib import Path from utils.common import ( ROOT_DIR, SETUP_DIR, appdataXml, copyPackageFiles, copySourceCode, extractVersion, freshFolder, makeCheckSum, removeRedundantQt, systemCall, toUpload, writeFile ) def appImage(args: argparse.Namespace) -> None: """Build an AppImage.""" try: import python_appimage # noqa: F401 # type: ignore except ImportError: print( "ERROR: Package 'python-appimage' is missing on this system.\n" " Please run 'pip install --user python-appimage' to install it.\n" ) sys.exit(1) if sys.platform != "linux": print("ERROR: Command 'build-appimage' can only be used on Linux") sys.exit(1) print("") print("Build AppImage") print("="*120) mLinux = args.linux mArch = args.arch pyVer = args.python # Version Info pkgVers, _, relDate = extractVersion() relDate = datetime.datetime.strptime(relDate, "%Y-%m-%d") # Set Up Folder bldDir = ROOT_DIR / "dist_appimage" bldPkg = f"novelwriter-{pkgVers}-{mArch}" bldImg = f"{bldPkg}.AppImage" outDir = bldDir / bldPkg imgDir = bldDir / "appimage" appDir = bldDir / f"novelWriter-{mArch}" bldDir.mkdir(exist_ok=True) freshFolder(outDir) freshFolder(imgDir) freshFolder(appDir) # Remove old AppImages if images := bldDir.glob("*.AppImage"): print("Removing old AppImages") for image in images: image.unlink() # Copy novelWriter Source print("Copying novelWriter source ...") copySourceCode(outDir) print("Copying or generating additional files ...") copyPackageFiles(outDir) # Write Metadata writeFile(imgDir / "novelwriter.appdata.xml", appdataXml()) writeFile(imgDir / "requirements.txt", str(outDir)) writeFile(imgDir / "entrypoint.sh", ( # f"export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${{APPDIR}}/usr/lib/{mArch}-linux-gnu/\n" '{{ python-executable }} -sE ${APPDIR}/opt/python{{ python-version }}/bin/novelwriter "$@"' )) shutil.copyfile(SETUP_DIR / "data" / "novelwriter.desktop", imgDir / "novelwriter.desktop") print("Copied: novelwriter.desktop") shutil.copyfile(SETUP_DIR / "icons" / "novelwriter.png", imgDir / "novelwriter.png") print("Copied: novelwriter.png") # Build AppDir systemCall([ sys.executable, "-m", "python_appimage", "build", "app", "--no-packaging", "-l", f"{mLinux}_{mArch}", "-p", pyVer, "appimage" ], cwd=bldDir) # Copy Libraries libPath = Path(f"/usr/lib/{mArch}-linux-gnu") siteDir = appDir / "opt" / f"python{pyVer}" / "lib" / f"python{pyVer}" / "site-packages" qt6Lib = siteDir / "PyQt6" / "Qt6" / "lib" shutil.copyfile(libPath / "libxcb-cursor.so.0", qt6Lib / "libxcb-cursor.so.0") # Remove Redundant removeRedundantQt(siteDir) # Build Image appToolExec = os.environ.get("APPIMAGE_TOOL_EXEC", "appimagetool") env = os.environ.copy() env["ARCH"] = mArch systemCall([ appToolExec, "--no-appstream", "--updateinformation", f"gh-releases-zsync|vkbo|novelwriter|latest|novelwriter-*-{mArch}.AppImage.zsync", str(appDir), bldImg ], cwd=bldDir, env=env) updFile = bldDir / f"{bldImg}.zsync" bldFile = bldDir / bldImg shaFile = makeCheckSum(bldFile.name, cwd=bldDir) toUpload(bldFile) toUpload(updFile) toUpload(shaFile)