This is my attempt to provide the steps and blueprint patches to build a fully functional local version of KMyMoney 5.2 on Apple Silicon.
- Setup Craft Environment
Initialize the build system and source the required environment.
bash
mkdir ~/CraftRoot
curl https://raw.githubusercontent.com/KDE/craft/master/setup/CraftBootstrap.py -o setup.py
python3 setup.py --prefix ~/CraftRoot # Select macOS/arm64
# Apply the blueprint changes below before proceeding
source ~/CraftRoot/craft/craftenv.sh
- Update Blueprints with macOS Fixes
Apply the following diffs to your local blueprints to automate the build on M-series hardware.
Gwenhywfar Blueprint
File: ~/CraftRoot/etc/blueprints/locations/craft-blueprints-kde/libs/_autotools/gwenhywfar/gwenhywfar.py
Fixes Qt6 tool detection (uic/moc) and dependencies.
diff
--- gwenhywfar.py-orig
+++ gwenhywfar.py
@@ -49,6 +48,7 @@
self.runtimeDependencies["libs/gnutls"] = None
self.runtimeDependencies["libs/gcrypt"] = None
self.runtimeDependencies["libs/qt6/qtbase"] = None
+ self.runtimeDependencies["libs/qt6/qttools"] = None
@@ -98,7 +98,6 @@
if CraftCore.compiler.isMacOS:
self.subinfo.options.configure.ldflags += " -Wl,-headerpad_max_install_names"
- with utils.ScopedEnv({"PATH": f"{CraftCore.standardDirs.craftRoot() / 'libexec'}:{os.environ.get('PATH')}"}):
- return super().configure()
+ os.environ["PATH"] = f"{CraftCore.standardDirs.craftRoot() / 'libexec'}:{os.environ['PATH']}"
AqBanking Blueprint
File: ~/CraftRoot/etc/blueprints/locations/craft-blueprints-kde/libs/_autotools/aqbanking/aqbanking.py
Ensures Qt6 tools are available and fixes CMake naming conventions for macOS.
diff
--- aqbanking.py-orig
+++ aqbanking.py
@@ -49,29 +48,21 @@
self.runtimeDependencies["virtual/base"] = None
self.buildDependencies["libs/libbzip2"] = None
self.runtimeDependencies["libs/gwenhywfar"] = None
+ self.runtimeDependencies["libs/qt6/qttools"] = None
KMyMoney Blueprint
File: ~/CraftRoot/etc/blueprints/locations/craft-blueprints-kde/extragear/kmymoney/kmymoney.py
Automates the bundle merge, rescues plugins from the image folder, and injects relative metadata to fix the âMissing AqBanking Menuâ and âMissing Storage Pluginâ errors.
diff
--- kmymoney.py-orig
+++ kmymoney.py
@@ -36,6 +36,7 @@
def setDependencies(self):
+ self.runtimeDependencies["libs/qt6/qttools"] = None
...
@@ -73,14 +71,107 @@
if CraftCore.compiler.isMacOS:
- self.subinfo.options.configure.args += ["-DENABLE_WOOB=OFF"]
+ self.subinfo.options.configure.args += [
+ "-DENABLE_WOOB=OFF",
+ "-DCMAKE_OSX_ARCHITECTURES=arm64",
+ "-DKDE_INSTALL_BUNDLEDIR=Applications/KDE",
+ "-DAPPLE_INSTALL_BUNDLE=ON"
+ ]
def install(self):
if not super().install():
return False
- # For AppImages create a gpgconf.ctl file that forces gpgconf to use the actual APPDIR
+ if CraftCore.compiler.isMacOS:
+ imageDir = self.imageDir()
+ craftRoot = CraftCore.standardDirs.craftRoot()
+
+ # 1. FIX MISSING PLUGINS & LIBS IN CRAFTROOT
+ for folder in ["plugins/kmymoney_plugins", "lib"]:
+ if (imageDir / folder).exists():
+ utils.copyDir(imageDir / folder, craftRoot / folder)
+
+ # 2. MANUALLY MERGE THE BUNDLE
+ destBundle = craftRoot / "Applications/KDE/kmymoney.app"
+ appContents = destBundle / "Contents"
+ if not destBundle.exists():
+ utils.createDir(destBundle.parent)
+ utils.copyDir(imageDir / "Applications/KDE/kmymoney.app", destBundle)
+
+ # 3. INTERNAL BUNDLE STRUCTURE
+ utils.createDir(appContents / "Resources/lib")
+ utils.copyDir(craftRoot / "lib/gwenhywfar", appContents / "Resources/lib/gwenhywfar")
+ utils.copyDir(craftRoot / "lib/aqbanking", appContents / "Resources/lib/aqbanking")
+ utils.copyDir(craftRoot / "share/gwenhywfar", appContents / "Resources/gwenhywfar")
+ utils.copyDir(craftRoot / "share/aqbanking", appContents / "Resources/aqbanking")
+
+ # 4. BRIDGE SYMLINKS
+ for folder in ["lib", "share"]:
+ link = appContents / folder
+ if link.exists() or link.is_symlink(): utils.deleteFile(link)
+ utils.createSymlink(craftRoot / folder, link, targetIsDirectory=True)
+
+ # 5. INJECT METADATA (LSEnvironment)
+ infoPlist = appContents / "Info.plist"
+ gwenPath = f"{craftRoot}/lib/gwenhywfar/plugins/79"
+ kmmPluginPath = f"{craftRoot}/plugins"
+ utils.system(["plutil", "-replace", "LSEnvironment", "-xml",
+ f"<dict><key>GWEN_PLUGIN_DIR</key><string>{gwenPath}</string>"
+ f"<key>QT_PLUGIN_PATH</key><string>{kmmPluginPath}</string></dict>",
+ str(infoPlist)])
+
+ # 6. SAFETY CHECK: Revert wrapper if it existed
+ binDir = appContents / "MacOS"
+ if (binDir / "kmymoney.real").exists():
+ utils.deleteFile(binDir / "kmymoney")
+ utils.moveFile(binDir / "kmymoney.real", binDir / "kmymoney")
+
+ return True
def createPackage(self):
- self.defines["executable"] = "bin\\kmymoney.exe"
+ self.defines["executable"] = "bin\\kmymoney.exe"
self.defines["icon"] = self.blueprintDir() / "kmymoney.ico"
- self.defines["mimetypes"] = [...]
+ self.defines["mimetypes"] = ["application/x-kmymoney", "application/x-ofx", "application/vnd.intu.qfx", "application/x-qfx", "application/x-qif", "text/csv"]
+ self.defines["file_types"] = [".kmy", ".ofx", ".qfx", ".qif", ".csv"]
- self.defines["website"] = "https://kmymoney.org/"
+ self.defines["website"] = "https://kmymoney.org"
+
+ if CraftCore.compiler.isMacOS:
+ self.defines["apppath"] = "Applications/KDE/kmymoney.app"
+ self.defines["extra_binaries"] = [
+ "Resources/lib/gwenhywfar/plugins/79/configmgr/dir.so",
+ "Resources/lib/aqbanking/plugins/44/providers/aqhbci.so"
+ ]
+
self.blacklist_file.append(self.blueprintDir() / "blacklist.txt")
if CraftCore.compiler.isMacOS:
self.blacklist_file.append(self.blueprintDir() / "blacklist_mac.txt")
@@ -121,3 +180,4 @@
self.ignoredPackages.append("binary/mysql")
return super().createPackage()
+
- Build and Launch
Execute the build sequence from scratch.
bash
craft --set branch=master kmymoney
craft kmymoney
open "$HOME/CraftRoot/Applications/KDE/kmymoney.app"
# Package the DMG
craft --package kmymoney
This (hopefully) will provide the needed information to get the build and packaging working again on your pipeline. However, the DMG still doesnât have the Configure AqBanking⌠menu (the build does, the dmg does not) which was the purpose of my previous post.