diff --git a/.github/workflows/macbuild.yml b/.github/workflows/macbuild.yml new file mode 100644 index 0000000..bb5126d --- /dev/null +++ b/.github/workflows/macbuild.yml @@ -0,0 +1,34 @@ +# macOS CI — builds a .app bundle and packages it as a .dmg +name: MacOS CI + +on: + push: + branches: + - '*' + pull_request: + branches: + - '*' + +jobs: + build: + runs-on: macos-latest # arm64 (Apple Silicon) + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Build macOS App + run: bash macbuild.sh + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: DMG Package + path: dist/*.dmg diff --git a/icons/logo.icns b/icons/logo.icns new file mode 100644 index 0000000..1f356ef Binary files /dev/null and b/icons/logo.icns differ diff --git a/macbuild.sh b/macbuild.sh new file mode 100755 index 0000000..1416d90 --- /dev/null +++ b/macbuild.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# macOS build script for DroneCAN GUI Tool +# Produces a .app bundle and a .dmg installer in the dist/ folder. +# Requirements: Python 3.9+ + +set -e + +python3 --version + +# Create and activate a clean virtual environment +python3 -m venv venv +source venv/bin/activate + +python3 -m pip install -U pip +python3 -m pip install -U py2app +python3 -m pip install -U pymavlink +python3 -m pip install -U python-can +python3 -m pip install -U . + +APP_NAME="DroneCAN GUI Tool" + +# Clean previous build artifacts +rm -rf build dist + +# Build the .app bundle with py2app +python3 setup_mac.py py2app + +# Package the .app into a .dmg +hdiutil create \ + -volname "${APP_NAME}" \ + -srcfolder "dist/${APP_NAME}.app" \ + -ov \ + -format UDZO \ + "dist/DroneCAN_GUI_Tool.dmg" + +echo "Build complete. Artifact: dist/DroneCAN_GUI_Tool.dmg" +ls -lh dist/ diff --git a/setup_mac.py b/setup_mac.py new file mode 100644 index 0000000..482b71d --- /dev/null +++ b/setup_mac.py @@ -0,0 +1,57 @@ +""" +py2app build configuration for macOS .app bundle. +Usage: python3 setup_mac.py py2app +""" +import os +import site + +PACKAGE_NAME = 'dronecan_gui_tool' +APP_NAME = 'DroneCAN GUI Tool' + +# Locate the dronecan dsdl_specs data files +site_packages = site.getsitepackages()[0] +dsdl_specs = os.path.join(site_packages, 'dronecan', 'dsdl_specs') + +OPTIONS = { + 'argv_emulation': False, + 'packages': [ + PACKAGE_NAME, + 'dronecan', + 'qtwidgets', + 'pyqtgraph', + 'qtawesome', + 'qtconsole', + 'ipykernel', + 'jupyter_client', + 'zmq', + 'pygments', + 'traitlets', + ], + 'includes': [ + 'PyQt5', + 'PyQt5.QtCore', + 'PyQt5.QtGui', + 'PyQt5.QtWidgets', + 'PyQt5.QtSvg', + 'PyQt5.QtPrintSupport', + 'PyQt5.QtSerialPort', + ], + 'resources': [dsdl_specs], + 'iconfile': 'icons/logo.icns', + 'plist': { + 'CFBundleName': APP_NAME, + 'CFBundleDisplayName': APP_NAME, + 'CFBundleIdentifier': 'org.dronecan.gui-tool', + 'NSHighResolutionCapable': True, + }, +} + +from setuptools import setup + +setup( + name=APP_NAME, + app=['bin/dronecan_gui_tool'], + options={'py2app': OPTIONS}, + setup_requires=['py2app'], + data_files=[('icons', ['icons/logo.icns'])], +)